estrazione numeri casuali

Ciao a tutti, sono una neonata nel campo di arduino, e a natale ho avuto la brillante idea di voler progettare una tombola elettronica.
I problemi sono nati quando ho chiesto ad arduino di avere una sequenza di 90 numeri casuali e non ripetuti.Una soluzione che ho trovato è quella di imporre delle condizioni attraverso le quali arduino scrive i numeri estratti in una matrice e poi rileggendoli riesce a "capire" se il numero è già uscito o no. Ma volevo andare un pò oltre e volevo riuscire a estrarre numeri casuali fra 1 e 90 e farglieli mettere in una matrice a partire dall'ultimoposto (il 90esimo) e poi estrarre numeri negli 89 posti restanti e così via. In questo modo il numero di scelte diminuisce di volta in volta. Ho trovato in internet dei programmi scritti in C che fanno questa cosa ma non riesco a tradurli. :sweat_smile:
Qualcuno di voi sa aiutarmi?..se non sono stata chiara sono pronta a rispiegare tutto quanto sia necessario :wink:

Guarda, senza diventare matta con funzioni che non ti daranno MAI sequenze casuali, fai prima a campionare un ingresso analogico, collegato al nulla, limitando l'escursione a 90.

Al limite esegui qualche operazione software su questi campioni, in modo da avere valori che coprano ben lo span 0-90

All'ingresso analogico collega un filo volante di 3-4 cm a mo di antenna, in modo che peschi bene i numeri nell'aria... XD

Carlotta:
Ma volevo andare un pò oltre e volevo riuscire a estrarre numeri casuali fra 1 e 90 e farglieli mettere in una matrice a partire dall'ultimoposto (il 90esimo) e poi estrarre numeri negli 89 posti restanti e così via. In questo modo il numero di scelte diminuisce di volta in volta. Ho trovato in internet dei programmi scritti in C che fanno questa cosa ma non riesco a tradurli. :sweat_smile:
Qualcuno di voi sa aiutarmi?..se non sono stata chiara sono pronta a rispiegare tutto quanto sia necessario :wink:

Non mi è chiarissimo, ma comunque i numeri casuali saranno sempre tra 1 e 90. Anche se è uscito il 50, non puoi escluderlo brutalmente. Fosse uscito il 90 puoi allora generare tra 1 e 89.
Per ogni numero dovrai verificare tu se è già uscito (usando l'array va bene) e se già uscito lo scarti e ne generi un'altro.
Potresti fare un array di 90 elementi, valori tutti a 0, Quando esce un numero, lo usi come indice di quel array e in quell'elemento ci metti 1. Se esce 50, all'elemento 50esimo metti 1. (Occhio che gli indici però vanno da 0-89 nell'array)
Se però l'elemento non è a 0 ma a 1 allora già uscito e ripeti l'estrazione.

Per la tua idea di mettere nell'ultimo elemento, come dicevo, non mi è chiaro.
Estrai primo numero, 50. Lo metti nell'array nell'elemento 90esimo.
Ma cosa c'e' dentro agli altri 89 posti?

Forse, ma mi sembra complicato, potresti fare cosi, ma è moolto complesso.
Premetto che l'indice lo considero da 1 a 90 creando un array di 91 elementi di nome ARR, elemento 0 non usato.
Metti tutti i numeri da 1 a 90 nell'array. Quindi c'e' praticamente corrispondenza tra indice ed elemento.
Una variabile XULTIMO vale 90. E questa punta all'elemento 90.
Estrai un numero tra 1 e il valore di XULTIMO (=90) usando random(min, max) ed esce il 50.
Il numero uscito per la tombola è quello in posizione 50 dell'array (NON 50).
Prendi l'elemento (il valore) in posizione 50isa e lo scambi con quello a posizione XULTIMO.
XULTIMO lo diminuisci di 1. Ora ripeterai l'estrazione tra 1 e XULTIMO che ora è 89. Ovvero hai a disposizione l'indice di ARR da 1 a 89. Ricorda solo che il numero estratto è dentro all'array e non il numero generato da random().
Dovrebbe funzionare ma è abbastanza arzigogolato. Forse segue la tua idea.

Se invece scambi con la prima posizione (e non con l'ultima) alla fine hai l'array ordinato con le estrazioni di tutti i 90 numeri.
Hai i link ai programmai in C che citi?

Per vedere come inserire link e simili guarda il Regolamento (punto 7 e seguenti) --> [REGOLAMENTO] Come usare questa sezione del forum - Italiano - Arduino Forum

Allora,
prima di tutto ti consiglio di scaricarti la libreria pRNG del nostro buon leo ... così avrai a disposizione un ottimo generatore di numeri pseudo casuali ... questa la descrizione originale :

It uses a mechanism based on an interrupt raised by the WatchDog Timer of the microcontroller to collect entropy and a Galois 32 bit LFSR (Linear Feedback Shift Register) to distribuite it into a 10-bytes pool.

Per il secondo problema ... purtroppo, contrariamente alla tombola, dove, ogni volta che esce un numero, i rimanenti diminuiscono, ad un generatore di numeri causali NON puoi chiedere di generare numeri, ma mano mano eliminarne alcuni ... quella è una cosa che dovrai fare tu.

Un metodo potrebbe essere quello dell'array di 90 elementi ...
... inizialmente l'array contiene tutti elementi FALSE e il generatore potrebbe generare un numero da 1 a 90 ... il numero identifica quante caselle contare (... ovviamente la prima volta il numero coincide con la casella visto che sono tutte FALSE).
Estratto il primo quella casella viene marcata TRUE e non conta più, per cui ti sono rimaste 89 caselle valide ... tu generi un numero causale, questa volta tra 1 e 89 e conti, saltando le caselle TRUE, su che casella cadi ... quello è il numero estratto, la marchi a sua volta TRUE ed estrai ora un numero tra 1 e 88, conti le caselle saltando quelle TRUE .... e così via fino a quando o uno fa tombola o ... tutte le caselle sono TRUE :slight_smile:

Più complicato a dirsi che a farsi ... :slight_smile:

Guglielmo

BaBBuino:
Guarda, senza diventare matta con funzioni che non ti daranno MAI sequenze casuali, fai prima a campionare un ingresso analogico, collegato al nulla, limitando l'escursione a 90.

Al limite esegui qualche operazione software su questi campioni, in modo da avere valori che coprano ben lo span 0-90

All'ingresso analogico collega un filo volante di 3-4 cm a mo di antenna, in modo che peschi bene i numeri nell'aria... XD

Secondo me esageri. Non dobbiamo mica criptare qualcosa.
Penso prendere una lettura di un entrata analogica come base della funzione random vada bene. Se usi un tasto per l' estrazione del succesivo numero puoi anche usare i millis nel momento che premi il Tasto come valore di partenza per il random per il prossimo numero di estrazione.

Come dice guglielmo mi sembra la cosa piú semplice.

Ciao Uwe

L'ingresso analogico in alta impedenza probabilmente legge una sinusoide a 50 Hz, frutto dei disturbi causati dalle linee elettriche. Non è quindi molto random..

Meglio la buon vecchia funzione rand con un seed generato dal millis in cui si preme il tasto..

E' scappata!! :grin:

Comunque sulla rete ho trovato e adattato questo:

#define MAXNUMBER 90
int vet[MAXNUMBER];

void setup()
{
  Serial.begin(9600);

  for( int i = 0; i < MAXNUMBER; i++)
  {
    fillin(vet, MAXNUMBER);
    mixup(vet, MAXNUMBER);
    if( check(vet, MAXNUMBER) != 0 )
    {
      Serial.println("L'algoritmo genera duplicati!!");
    }
  }
  printnumbers(vet, MAXNUMBER);
}


void loop()
{

}

void swap(int* vector, int i, int j)
{
  int tmp = vector[i];
  vector[i] = vector[j];
  vector[j] = tmp;
}

void fillin(int* vector, int len)
{
  for (int i = 0; i < len; i++)
  {
    vector[i] = i + 1;
  }
}

void printnumbers(int* vector, int len)
{
  for (int i = 0; i < len; i++)
  {
    Serial.println(vector[i]);
  }
  Serial.println("");
}

void mixup(int* vector, int len)
{
  for (int i = 0; i < len; i++)
  {
    swap(vector, rand() % len, len - i - 1);
  }
}

int check(int* vector, int len)
{
  int i, j;
  for (i = 0; i < len; i++)
  {
    for (j = i + 1; j < len; j++)
    {
      if( vector[i] == vector[j] )
      {
        return 1;
      }
    }
  }
  return 0;
}

Non testato su Arduino!

... ma la libreria di leo che ho indicato QUI ... vi fa proprio schifo ??? :astonished: :astonished: :astonished: ... visto che continuate a cercare strampalate soluzioni ... :roll_eyes:

Guardate che al momento, salvo usare dell'HW esterno apposito (es. Atmel ATSHA204), quella è probabilmente quanto c'è di meglio !!!

Guglielmo

lock:
Scherzi a parte, ci sono, sopratutto negli acceleratori VPN, chip per la generazione di numeri casuali =P

Che sono sempre pseudo-casuali, nel senso che in una qualche maniera sono ricostruibili (vale sopratutto per gli algoritmi cifranti)

Ora si dice che l'AES 128 bit sia sostanzialmente sicuro, ma se è nel libero mercato è perchè i nostri matematici della NSA hanno già qualche sistema multiprocessore "ASICS" che decodifica l'AES 128 in meno di quanto uno possa immaginare.

P.S. negli States sono VIETATI gli algoritmi crittografici non "vagliati" dal Governo...

Tornando a noi, come dice Lock, si può mettere sul pin analogico la giunzione BE di un transistor (polarizzata inversamente e previa amplificazione in tensione) per sfruttare il rumore atomico della giunzione appunto. Quelli si che sarebbero numeri casuali! XD

Credo che l'algoritmo che si usa per mescolare le carte sia la soluzione. Penso che il codice si possa
trovare pronto in rete con facilità.

In questo modo si possono escludere i numeri già usciti in quanto il mescolamento dei numeri avviene tra quelli ancora presenti e magari dura un tempo x fornito dal generatore di numeri casuali. Terminato il mescolamento basta estrarre il primo o l'ultimo. Se si potessero usare le linked list sarebbe molto semplice rimuovere un elemento, con gli array statici diventa più complicato gestire la rimozione del dato valore.

Ciao.

Carlotta:
Ciao a tutti, sono una neonata nel campo di arduino, e a natale ho avuto la brillante idea di voler progettare una tombola elettronica.
I problemi sono nati quando ho chiesto ad arduino di avere una sequenza di 90 numeri casuali e non ripetuti.

Che Arduino usi?
Se usi la UNO, la Leonardo o la MEGA, la lib pRNG che ti ha suggerito Guglielmo è la soluzione più pratica e sicura per ottenere numeri pseudo-causali senza l'uso di hardware esterno.
Se invece usi la DUE, il chip SAM3X integrato nella scheda ha un TRNG, ossia un generatore di numeri casuali crittograficamente sicuro, ma l'IDE di Arduino non lo supporta. Però ho scritto un'altra libreria, advancedFunctions, che permette di usarlo.

Per una tombola, non scomoderei hardware esterno :wink:

la cosa più semplice è un array di 90 elemneti, che contiene i numeri da 1 a 90.

poi, usando i valori letti da uno o più ingressi analogici, o l'uso di una libreria/hw specifica, scambi posizioni random di celle di array; fai tipo 500/1000 scambi e avrai un bel array di numeri da 1 a 90 con un buon random.

il mescolamento avviene solo una volta e non hai problemi di numeri duplicati o altro.

a questo punto conosci già l'oridne di estrazione dei numeri: infatti saranno estratti a partire dall'indice 0 al 89

@Lesto.
Sembra quello fatto qui --> estrazione numeri casuali - #9 by PaoloP - Generale - Arduino Forum :grin:

wow quante risposte! Non ho ben capito chi sia Guglielmo, ma se è nid69ita, sono d'accordo con quanti dicono sia la soluzione più pratica,o comunque è quella che stavo cercando. Ora provo a "giocare un pò" e vedere se riesco a tirarne fuori qualcosa, in ogni caso ho tempo un anno prima di rigiocare a tombola.
Per risponderea leo72: sto usando un arduino UNO.
Per PaoloP: ecco il link con il codice in C: [C] Generare numero casuale in intervallo senza ripetizioni | HTML.it forum

Grazie a tutti per le risposte :smiley:

Carlotta:
Non ho ben capito chi sia Guglielmo, ma se è nid69ita, ....

No ... è gpb01 ... :grin:

Guglielmo

PaoloP:
@Lesto.
Sembra quello fatto qui --> estrazione numeri casuali - #9 by PaoloP - Generale - Arduino Forum :grin:

ehm ehm bhe ottimo :stuck_out_tongue:

cmq un errore "logico"

rand() % len

è sbagliatissimo perchèp rompe tutti i conti che si fanno per avere la distribuziuone uniforme!
la cosa giusta da fare è dividere per il valore massimo (sui pc è RAND_MAX, ma non so sevale anche per gli AVR), e quindi moltiplicare per len (ed arrotondare, occhio al cast a float se no non va)

 ( (float)rand()/RAND_MAX) * len

motivo da c++ - Why do people say there is modulo bias when using a random number generator? - Stack Overflow

mettimao che RAND_MAX vale 10 e noi vogliamo numeri da 0 a 2, faremmo
rand()%3
PERO'
avremo 0 se rand() genera 0, 3, 6, 9 ovvero 4/11
avremo 1 se rand() genera 1, 4, 7, 10 ovvero 4/11
avremo 2 se rand() genera 2, 5, 8 ovvero 3/11 <<!!

Come lo avrei fatto io:

  • array da 90 elementi a contiene inizialmente gli interi 1, ... 90
  • intero estratti = inizialmente 0
    a:
  • seme numeri casuali generato combinando millis e microsec al momento della pressione del pulsante
  • genera numero casuale x fra 1 e (90-estratti), numero estratto = elemento x di a
  • estratti ++
  • fai scorrere gli elementi in a a partire da x fino a (90-estratti) di un posto verso il basso, saltando x
    quindi tanto per capirci a = 1,2,3,4,5,6,7,8; x= 4, numero estratto = 4, a diventa a=1,2,3,5,6,7,8,8
    x = 5; numero estratto=6, a diventa a=1,2,3,5,7,8,8,8 ...
  • se estratti<90 e viene premuto il tasto, torna in a:

Mi sembra semplice e privo della necessità di verificare ad ogni estrazione casuale se vi siano ripetizioni

@paulus1969 sia il tuo che il metodo di paolop non hanno bisogno di check di ripetizioni (lui lo fa solo per verificare che non ci siano sttai errori da parte del PROGRAMMATORE, cosa che si può eliminare)

analizziamo vantaggi e svantaggi dei 2 sistemi:

paolop: lenta inizializazione, all'avvio devi fare tanti swap. però poi leggi l'array in ordine, quindi l'strazione è un semplice +1 sull'indice

paulus: veloce inizializzazione, all'avvio crei solo l'array. però poi AD OGNI elemento estratto devi eliminarlo dall'array; se estrai l'elemento in posizione 15 (il 16 visto che l'array parte da 0), poi però devi sostituire la cella 15 con il contenuto della 16, la 16 col 17 etc, e così via per ogni estrazione.

Qual'è il metodo migliore? dipende. per uesta applicazione è ininfluente, visto la velocità di prcessamento. A complessità di codice (e comprensione) credo vinca quella di paolop, avendo la locica tutta in un punto e non sparsa per ogni iterazione.