Matematica: Come fare la Democrazia Aleatoria usando l'Estrazione del Lotto?

L’idea è geniale, ma non sono riuscito a trovare alcuna implementazione di un algoritmo adatto per svolgere delle elezioni usando l’esito del lotto. Ho provato a farne uno io stesso, ma le mie competenze in statistica sono piuttosto arrugginite. Magari avete idee migliori. Ecco qui un prototipo in Javascript:

L’idea è che moltiplicando i numeri mantengo l’entropia (basta cambiare una cifra per arrivare ad esiti completamente diversi), poi faccio il trucco dirty per trasformare l’esito in un valore floating tra 0 ed 1. Alla fine applico tale valore per estrarre un numero di vincenti dalla lista dei candidati. Matematicamente possono uscire esiti identici — in tal caso bisogna aggiungere una regola tipo prendere il numero inferiore non ancora uscito.

Vorrei una soluzione magari più elegante e vorrei stare sul sicuro che l’algoritmo è statisticamente salvo. Potrei provare un approccio empirico (un milione di test con esiti del lotto finti o reali)…

Versione online. Ho anche una versione perl del algoritmo, ma non va d’accordo con il markup del forum.

Se il problema è eleggere casualmente un candidato da una rosa di candidati basandoci sulle estrazioni del lotto, di seguito una proposta.

Abbiamo 10 ruote locali in ordine alfabetico più quella nazionale. Partiamo dal primo numero estratto da 1 a 90. Se dovessimo scegliere 1 persona tra 90 candidati sarebbe ovvio e logico usare un singolo numero.

Se i candidati sono più realisticamente 8 e sempre 1 va selezionato, allora potremmo fare il numero che viene (es. 25) diviso 8 e prendere il resto (es. viene 1 cioè il primo). Però, le possibilità non sarebbero equiprobabili per gli 8 candidati, perché, come si può vedere sotto, 89 e 90 darebbero una possibilità in più al 1° e al 2° candidato.

1   |   01   09   ...   81   89
2   |   02   10   ...   82   90
3   |   03   11   ...   83
4   |   04   12   ...   84
5   |   05   13   ...   85
6   |   06   14   ...   86
7   |   07   15   ...   87
8   |   08   16   ...   88

Se ricorriamo ad una proporzione diretta, quindi usando i decimali, i risultati finali vanno comunque arrotondati e riotteniamo, per esempio, il 2° ed il 6° candidato con una probabilità lievemente maggiore. Dato che 90 / 8 = 11,25 e non c’è possibilità di avere una perfetta equiprobabilità.

Quindi possiamo recuperare il sistema precedente e dedicare un numero da 1 a 90 (lo chiamiamo Numero iniziale Ni) che indica da dove dovremo iniziare a contare nella lista dei candidati (il cui username, per esempio, è usato per un ordinamento iniziale alfabetico). Un secondo numero da 1 a 90 (Ne) indica chi viene estratto contando a partire da Ni, così viene distribuito a caso questo lieve vantaggio di estrazione.

Dunque se vogliamo estrarre N candidati da un elenco di massimo 90 candidati, abbiamo bisogno di N+1 numeri del lotto (recuperati secondo le ruote locali in ordine alfabetico più quella nazionale in fondo). Il numero Ne di un candidato estratto ci indicherà il nuovo inizio Ni da cui ri-contare per un eventuale ulteriore candidato con il suo Ne.

Se l’elenco dei candidati fosse (difficilmente) maggiore di 90, allora si deve usare lo stesso procedimento usando 2 numeri per volta. Chiamiamo i due numeri consecutivi A e B, con la formula A+(B-1)*90 possiamo ottenere un numero estratto da 1 a 8.100. È quasi da escludere che avremo mai una lista di 8.100 candidati. Ma nulla vieta di usare 3 numeri consecutivi per avere un intervallo da 1 a 72.900.

Implementare tutto questo in JavaScript è facile.

1 Mi Piace

Ecco un unico file html che implementa quanto ho proposto:

<!DOCTYPE html>
<html>
<head>
<title>Estrazione casuale</title>
<meta charset="utf-8" />
<style>
body {
	margin:0 auto;
	width:800px;
}
p, div, table {
	margin:0 0 16px 0;
}
input {
	width:30px;
}
body, input, button, textarea {
	font-family:Verdana,sans-serif;
	font-size:16px;
}
</style>
<script>
function generaTabellaRuote() {

	var e = document.getElementById("ruote");
	var righe = '';
	var nomiRuote = ['Bari','Cagliari','Firenze','Genova','Milano','Napoli','Palermo','Roma','Torino','Venezia','Nazionale'];
	var numero = '<td><input type="text" class="estratto"></td>';

	// Costruisce una riga per ciascuna ruota con il nome della ruota e 5 caselle per i relativi numeri
	for (var i = 0; i < nomiRuote.length; i++) {
		righe += '<tr>';
		righe += '<td style="width:90px;">' + nomiRuote[i] + '</td>';
		righe += numero + numero + numero + numero + numero;
		righe += '</tr>';
	}
	e.innerHTML = righe;
}
function elencaEstratti() {

	var e_c = document.getElementById("candidati");
	var e_r = document.getElementById("risultato");
	var e_e = document.getElementsByClassName("estratto");

	// Svuota i risultati
	e_r.innerHTML = '';

	// Recupera i candidati uno per riga e li mette in ordine alfabetico
	var candidati = e_c.value.split('\n').sort();
	
	// Riscrive i candidati in ordine alfabetico nell'elenco
	e_c.value = candidati.join('\n');
	e_r.innerHTML = '<p>I candidati sono stati messi in ordine alfabetico</p>';

	// Determina da quale candidato si inizia a contare
	var Ni = Number(e_e[0].value) % candidati.length;
	if (Ni === 0) Ni = candidati.length;
	e_r.innerHTML += '<p>Si inizia a contare dal candidato: '+e_e[0].value+' modulo '+candidati.length+' = '+Ni+': '+candidati[Ni-1]+'</p>';
	
	// Si estraggono tanti candidati quanti ne sono stati richiesti
	var Nc = document.getElementById("nCandidati").value;
	var estratti = [];
	for (var i = 1; i <= Nc; i++) {
		var nCandidato = (Number(e_e[i].value) + Ni - 1) % candidati.length;
		if (nCandidato === 0) nCandidato = candidati.length;
		estratti.push(candidati[nCandidato - 1]);

		// Si riparte a contare dall'ultimo candidato estratto
		Ni = nCandidato;
		
		// Viene eliminato l'ultimo candidato estratto dalla lista affinché non sia estratto di nuovo
		candidati.splice(nCandidato - 1, 1);
	}
	e_r.innerHTML += '<p>I candidati estratti sono: '+estratti.join(', ')+'.</p>';
}
</script>
</head>
<body onload="generaTabellaRuote()">
<h1>Estrazione casuale</h1>
<h3>Numeri estratti nelle ruote del Lotto</h3>
<p>Le ruote locali sono in ordine alfabetico, in fondo la ruota nazionale. Quando si decide di estrarre casualmente un cadidato, occorre stabilire a quale giorno dell'estrazione del Lotto bisogna fare riferimento per compilare questa tabella. Molto spesso basterà inserire i numeri della sola ruota di Bari.</p>
<table id="ruote"></table>
<div>Numero candidati da estrarre: <input type="text" id="nCandidati" value="1" /></div>
<p>Scrivi di seguito, uno per riga, i nomi utente (o cognome e nome) dei candidati da estrarre. Saranno messi in ordine alfabetico automaticamente.</p>
<textarea id="candidati" rows="10" cols="30"></textarea><br />
<button type="button" onclick="elencaEstratti()">Elenca estratti</button>
<h3>Risultati</h3>
<div id="risultato"></div>
</body>
</html>

Anvedi che bella appina che ci ha programmato il bravo mago @Silvan. Sono ancora indeciso sul uso individuale degli esiti del lotto invece di una cumulazione. In ambito DiEM ci è stato uno che ha proposto di fare un hash crittografico su tutti gli esiti concatenati come stringa, poi usare l’hash per l’estrazione dei vincitori. Il tuo metodo è certamente più semplice da spiegare agli elettori però!

Che problemi può avere l’uso individuale dei numeri? C’è già l’ordinamento alfabetico iniziale ed il primo numero che stabilisce da dove si inizia a contare, per rendere difficili e casuali gli eventuali sfruttamenti di lievi piccole possibilità in più di essere estratto.

Siccome anche la semplicità e la chiarezza sono un valore, credo che questo sia molto maggiore a fronte di non chiare motivazioni contrarie.

Giusto questo mi viene in mente. Sulla stessa ruota se viene estratto un numero, gli altri numeri non saranno da 1 a 90, ma da 1 a 90 meno i numeri usciti. Questo, in realtà, non dovrebbe comportare alcun problema, dato che resta pur sempre casuale quel che accadrà e non sfruttabile. Però, si può migliorare. Anziché usare i numeri di una ruota e poi passare alla successiva, si usano i primi numeri di tutte le ruote e poi (eventualmente) i secondi numeri di tutte le ruote e così via. In questo modo, abbiamo (quasi sempre) veri numeri da 1 a 90, a meno che, i candidati da estrarre sono più di 11 (ruote locali + quella nazionale) e quindi si finisce sui secondi numeri delle ruote, cioè non più esattamente da 1 a 90.

Non vedo chiaramente alcun problema nemmeno nella versione attuale. Ad ogni modo, ecco una versione perfezionata:

<!DOCTYPE html>
<html>
<head>
<title>Estrazione casuale</title>
<meta charset="utf-8" />
<style>
body {
	margin:0 auto;
	width:800px;
	line-height:125%;
}
p, div, table {
	margin:0 0 16px 0;
}
input {
	width:30px;
	text-align:center;
}
body, input, button, textarea {
	font-family:Verdana,sans-serif;
	font-size:16px;
}
.verticale {
	-ms-transform:rotate(-90deg);
    -moz-transform:rotate(-90deg);
    -webkit-transform:rotate(-90deg);
    transform:rotate(-90deg);
}
.prima-colonna {
	vertical-align:bottom;
	padding-right:8px;
	font-size:14px;
	color:#808080;
}
#ruote div {
	margin:0;
	width:40px !important;
}
#ruote th {
	height:60px;
	vertical-align:bottom;
	padding-bottom:20px;
	color:#808080;
}
</style>
<script>
function creaTabellaRuote() {

	var e = document.getElementById("ruote");
	var righe = '';
	var nomiRuote = ['Bari','Cagliari','Firenze','Genova','Milano','Napoli','Palermo','Roma','Torino','Venezia','Nazionale'];
	var cella = '<td><input type="text" class="estratto"></td>';

	// Costruisce una riga con il nome di ciascuna ruota
	// Riserva una posizione iniziale per le etichette 1° 2° 3° 4° 5° estratto
	righe += '<tr><th>&nbsp;</th>';
	for (var i = 0; i < nomiRuote.length; i++) {
		righe += '<th><div class="verticale">' + nomiRuote[i] + '</div></th>';
	}
	righe += '</tr>';
	
	// Costruisce 5 righe per i 5 numeri estratti per ciascuna ruota
	for (var i = 0; i < 5; i++) {
		righe += '<tr><td class="prima-colonna">'+ Number(i + 1) +'°</td>';
		for (var c = 0; c < 11; c++) { righe += cella; }
		righe += '<tr>';
	}
	e.innerHTML = righe;
}
function candidatiEstratti() {

	var e_c = document.getElementById("candidati");
	var e_r = document.getElementById("risultato");
	var e_e = document.getElementsByClassName("estratto");

	// Svuota il risultato
	e_r.innerHTML = '';

	// Recupera i candidati uno per riga e li mette in ordine alfabetico
	var candidati = e_c.value.split('\n').sort();
	
	// Riscrive i candidati in ordine alfabetico nell'elenco
	e_c.value = candidati.join('\n');
	e_r.innerHTML = '<p>I candidati sono stati messi in ordine alfabetico</p>';

	// Determina da quale candidato si inizia a contare
	var Ni = Number(e_e[0].value) % candidati.length;
	if (Ni === 0) Ni = candidati.length;
	e_r.innerHTML += '<p>Si inizia a contare dal candidato: '+e_e[0].value+' modulo '+candidati.length+' = '+Ni+': '+candidati[Ni-1]+'</p>';
	
	// Si estraggono tanti candidati quanti ne sono stati richiesti
	var Nc = document.getElementById("nCandidati").value;
	var estratti = [];
	for (var i = 1; i <= Nc; i++) {

		// Ogni successivo numero estratto disponibile in tabella indica quale candidato risulta estratto
		var nCandidato = (Number(e_e[i].value) + Ni - 1) % candidati.length;
		if (nCandidato === 0) nCandidato = candidati.length;
		estratti.push(candidati[nCandidato - 1]);

		// Si riparte a contare dall'ultimo candidato estratto
		Ni = nCandidato;
		
		// Viene eliminato l'ultimo candidato estratto dalla lista affinché non sia estratto di nuovo
		candidati.splice(nCandidato - 1, 1);
	}
	e_r.innerHTML += '<p>I candidati estratti sono: '+estratti.join(', ')+'.</p>';
}
</script>
</head>
<body onload="creaTabellaRuote()">
<h1>Estrazione casuale</h1>
<h3>Numeri estratti nelle ruote del Lotto</h3>
<p>Le ruote locali sono in ordine alfabetico, in fondo la ruota nazionale. Quando si decide di estrarre casualmente dei cadidati, occorre stabilire a quale giorno dell'estrazione del Lotto bisogna fare riferimento per compilare questa tabella. Assicurarsi di copiare i numeri rispettando l'effettivo ordine di estrazione (1° estratto, 2° estratto, ecc.) e non l'eventuale ordinamento crescente dei numeri. È sufficiente inserire un numero di numeri pari al numero dei candidati da estrarre più uno.</p>
<table id="ruote"></table>
<h3>Candidati da estrarre casualmente</h3>
<div>Numero candidati da estrarre: <input type="text" id="nCandidati" value="1" /></div>
<p>Scrivere di seguito, uno per riga, i nomi utente (o cognome e nome) dei candidati da estrarre. Saranno messi in ordine alfabetico automaticamente.</p>
<textarea id="candidati" rows="10" cols="30"></textarea><br />
<button type="button" onclick="candidatiEstratti()">Elenca candidati estratti</button>
<h3>Risultato</h3>
<div id="risultato"></div>
</body>
</html>
1 Mi Piace

Lo svantaggio è minimale: teoricamente, se il notaio a Bari sapesse del suo ruolo e avesse la capacità di falsare gli esiti, con il primo numero estratto potrebbe eleggere una persona tra i candidati. Poi, non ho capito bene, ma mi pare che non cumuli gli esiti, perciò anche la prima estrazione di Cagliari potrebbe, se manomessa, eleggere direttamente una persona. Il vantaggio dei metodi che molteplicano o hashano gli esiti è che tutti i notai dovrebbero mettersi d’accordo per influenzare le elezioni… in cambio il codice è decisamente meno intellegibile da persone che hanno imparato la matematica a scuola. Per questo questa tua seconda soluzione già mi piace tantissimo.

@solibo, tu che hai avuto la lungimiranza di inserire questo bel metodo nel regolamento… che piano avevi in mente per una effettiva implementazione?

[quote=“lynX, post:6, topic:1939”] se il notaio a Bari sapesse del suo ruolo e avesse la capacità di falsare gli esiti, con il primo numero estratto potrebbe eleggere una persona tra i candidati.[/quote]

No, il primo numero serve solo per sapere da dove inizieremo a contare nella lista alfabeticamente ordinata, ma non si elegge ancora nessuno. Servono almeno 2 numeri.

La prima estrazione di Cagliari è dipendente da quanto è stato estratto a Bari (per il primo candidato servono 2 numeri di 2 ruote diverse). Inoltre, il secondo candidato eletto (se c’è) dipende dal numero estratto a Firenze, che a sua volta dipende dal primo candidato eletto, cioè dal numero di Cagliari (infatti questo è cumulativo perché si ri-inizia a contare dall’ultimo candidato estratto e non da quanto indicato nel numero di Bari), che a sua volta dipende dal numero estratto a Bari. Quindi, non basta corrompere un solo notaio, come minimo 2 e più si va avanti (con i candidati estratti insieme da una lista) più ne devi corrompere o manipolare (di notai).

Perfezionamento: volendo migliorare, si possono sommare tutti i numeri - primi estratti - di tutte le ruote. Il numerone somma viene usato per iniziare a contare nella nostra lista di candidati alfabeticamente ordinata. Così, ancora nessuno è estratto e dipendiamo dall’esito di tutte le ruote. Poi si riparte dai secondi numeri estratti di tutte le ruote per stabilire quali candidati sono casualmente emersi.

Perfetto. Con tutti i tuoi talenti ora dimostri di essere bravo anche in matematica ed informatica!

:slight_smile: Grazie per il riconoscimento.

Qui la versione perfezionata con la somma dei primi estratti di tutte le ruote (migliorato anche il layout):

<!DOCTYPE html>
<html>
<head>
<title>Estrazione casuale</title>
<meta charset="utf-8" />
<style>
body {
	margin:0 auto;
	line-height:125%;
	text-align:center;
}
p, div, table {
	margin:0 0 16px 0;
}
input {
	width:30px;
	text-align:center;
}
body, input, button, textarea {
	font-family:Verdana,sans-serif;
	font-size:16px;
}
.blocco {
	display:inline-block;
	vertical-align:top;
	text-align:justify;
	max-width:512px;
	padding:16px;
}
.verticale {
	-ms-transform:rotate(-90deg);
    -moz-transform:rotate(-90deg);
    -webkit-transform:rotate(-90deg);
    transform:rotate(-90deg);
}
.prima-colonna {
	vertical-align:bottom;
	padding-right:8px;
	font-size:14px;
	color:#808080;
}
.numeri-somma td input {
	border:2px solid #808080;
}
#ruote div {
	margin:0;
	width:40px !important;
}
#ruote th {
	height:60px;
	vertical-align:bottom;
	padding-bottom:20px;
	color:#808080;
}
</style>
<script>
function creaTabellaRuote() {

	var e = document.getElementById("ruote");
	var righe = '';
	var nomiRuote = ['Bari','Cagliari','Firenze','Genova','Milano','Napoli','Palermo','Roma','Torino','Venezia','Nazionale'];
	var cella = '<td><input type="text" class="estratto"></td>';

	// Costruisce una riga con il nome di ciascuna ruota
	// Riserva una posizione iniziale per le etichette 1° 2° 3° 4° 5° estratto
	righe += '<tr><th>&nbsp;</th>';
	for (var i = 0; i < nomiRuote.length; i++) {
		righe += '<th><div class="verticale">' + nomiRuote[i] + '</div></th>';
	}
	righe += '</tr>';
	
	// Costruisce 5 righe per i 5 numeri estratti per ciascuna ruota
	// Nella posizione iniziale viene inserita l'etichetta per 1° 2° 3° 4° 5° estratto
	var cls = ''; // cls: classe
	for (var i = 0; i < 5; i++) {
		if (i === 0) { cls = ' class="numeri-somma"' } else { cls = '' }
		righe += '<tr'+ cls +'><td class="prima-colonna">'+ Number(i + 1) +'°</td>';
		for (var c = 0; c < 11; c++) { righe += cella; }
		righe += '<tr>';
	}
	e.innerHTML = righe;
}
function candidatiEstratti() {

	var e_c = document.getElementById("candidati");
	var e_r = document.getElementById("risultato");
	var e_e = document.getElementsByClassName("estratto");

	// Svuota il risultato
	e_r.innerHTML = '';

	// Recupera i candidati uno per riga e li mette in ordine alfabetico
	var candidati = e_c.value.split('\n').sort();
	
	// Riscrive i candidati in ordine alfabetico nell'elenco
	e_c.value = candidati.join('\n');
	e_r.innerHTML = '<p>I candidati sono stati messi in ordine alfabetico.</p>';

	// Somma i primi estratti di tutte le ruote
	var somma = 0;
	for (var i = 0; i < 11; i++) { somma += Number(e_e[i].value); }
	e_r.innerHTML += '<p>La somma dei primi estratti di tutte le ruote è: '+somma+'.</p>';

	// Determina da quale candidato si inizia a contare, usando la precedente somma
	var Ni = somma % candidati.length;
	if (Ni === 0) Ni = candidati.length;
	e_r.innerHTML += '<p>Si inizia a contare dal candidato: '+somma+' diviso '+candidati.length+' con resto '+Ni+': '+candidati[Ni-1]+'.</p>';
	
	// Si estraggono tanti candidati quanti ne sono stati richiesti
	var Nc = document.getElementById("nCandidati").value;
	var estratti = [];
	for (var i = 1; i <= Nc; i++) {

		// Ogni successivo numero estratto disponibile in tabella indica quale candidato risulta estratto
		var nCandidato = (Number(e_e[i + 10].value) + Ni - 1) % candidati.length;
		if (nCandidato === 0) nCandidato = candidati.length;
		estratti.push(candidati[nCandidato - 1]);

		// Si riparte a contare dall'ultimo candidato estratto
		Ni = nCandidato;
		
		// Viene eliminato l'ultimo candidato estratto dalla lista affinché non sia estratto di nuovo
		candidati.splice(nCandidato - 1, 1);
	}
	e_r.innerHTML += '<p>I candidati estratti sono: '+estratti.join(', ')+'.</p>';
}
</script>
</head>
<body onload="creaTabellaRuote()">
<h1>Estrazione casuale</h1>
<div class="blocco">
<h3>Numeri estratti nelle ruote del Lotto</h3>
<p>Le ruote locali sono in ordine alfabetico, in fondo la ruota nazionale. Quando si decide di estrarre casualmente dei cadidati, occorre stabilire a quale giorno dell'estrazione del Lotto bisogna fare riferimento per compilare la tabella. Assicurarsi di copiare i numeri rispettando l'effettivo ordine di estrazione (1° estratto, 2° estratto, ecc.) e non l'eventuale ordinamento crescente dei numeri. Occorre inserire sempre i primi numeri estratti di tutte le ruote, e poi tanti numeri aggiuntivi quanti candidati desideriamo estrarre.</p>
<table id="ruote"></table>
</div>
<div class="blocco">
<h3>Candidati da estrarre casualmente</h3>
<div>Numero candidati da estrarre: <input type="text" id="nCandidati" value="1" /></div>
<p>Scrivere di seguito, uno per riga, i nomi utente (o cognome e nome) dei candidati da estrarre. Essi saranno messi in ordine alfabetico automaticamente.</p>
<textarea id="candidati" rows="10" cols="30"></textarea><br />
<button type="button" onclick="candidatiEstratti()">Elenca candidati estratti</button>
<h3>Risultato</h3>
<div id="risultato"></div>
</div>
</body>
</html>
1 Mi Piace

Ciao,Anichang, non ti conosco ma ti ho letto con molta attenzione finalmente ho una visione più ampia del problema.A proposito di pippe ecc. non tutti hanno gli strumenti e le capacità informatiche che mi pare tu abbia.Detto ciò ti chiedo se saresti in grado di trasmettere verbalmente ciò che hai appena descritto?Se si vorrei invitarti come relatore ad una iniziativa che faremo a Milano nel mese di febbraio. Grazie

Non ci capisco niente con quelle scritte di diversi colori :smiley:

D’accordo con l’appello ,tuttavia sarà l’ennesimo,sono Più d’acc Di concentrarsi sulle Europee del 2019.

Le elezioni europee sono un ottimo campo di battaglia per i pirati, faremo sicuramente bene. Io sono pronto :smile: