Algoritmul pentru generarea unui număr aleator

voturi
7

Caut pentru a genera un număr aleator și emite-l la o masă într-o bază de date pentru un anumit USER_ID. Captura este, același număr nu poate fi utilizat de două ori. Există un milion de moduri de a face acest lucru, dar sper cineva foarte pasionat de algoritmi are un mod inteligent de a rezolva problema într-o soluție elegantă în faptul că următoarele criterii este îndeplinit:

1) cel puțin suma de interogări la baza de date sunt realizate. 2) cel puțin suma de crawling printr-o structură de date în memorie se face.

În esență, ideea este de a face următoarele

1) Crearea unui număr aleator 0-9,999,999
2) Verificați baza de date pentru a vedea dacă există numărul
SAU
2) interogarea bazei de date pentru toate numerele
3) A se vedea în cazul în care se potriveste cu rezultate indiferent provin din db
4) În cazul în care se potrivește, repetați pasul 1, în cazul în care nu, problema este rezolvată.

Mulțumiri.

Întrebat 26/11/2008 la 02:44
sursa de către utilizator
În alte limbi...                            


17 răspunsuri

voturi
1

Cred că veți găsi că într-adevăr nu doresc să facă acest lucru. Pe măsură ce numerele din creșterea bazei de date, s-ar putea petrece prea mult timp în bucla „asigurați-vă că acest număr nu este luat“.

Personal, am avut noroc cu hash-uri ca o alternativă, ci de a veni cu o soluție mai bună, chiar mi-ar trebuie să știu de ce vrei să o faci în acest fel.

Publicat 26/11/2008 la 02:51
sursa de către utilizator

voturi
1

Experiența mea a fost pur și simplu, folosind RNG în PHP. Am constatat că, folosind o anumită dimensiune de număr (Sunt folosind un int, asa ca am un maxim de 4G). Am făcut niște teste și a constatat că, în medie, în 500.000 de iterații, am primit 120 de duplicate unice. N-am primit un triplu exemplar după rularea bucla o grămadă de ori. „Soluție“ mea a fost apoi doar pentru a introduce și a verifica dacă acesta nu reușește, apoi genera un nou ID-ul și du-te din nou.

Sfatul meu este de a face același lucru și a vedea ce rata de coliziune este & C și a vedea dacă este acceptabil pentru cazul dumneavoastră.

Acest lucru nu este optimă, așa că dacă cineva are sugestii caut prea :)

EDIT: Am fost limitat la un ID de 5 cifre ([a-zA-Z0-9] {5,5}), cu cât id (mai combinatie, cele câteva coliziuni). O MD5 a e-mailului aproape niciodată ar intra în conflict, de exemplu.

Publicat 26/11/2008 la 02:51
sursa de către utilizator

voturi
17

Nu algoritmul nu este scalabil. Ceea ce am făcut înainte de a emite un număr serial (1 de fiecare dată) și apoi le trec printr-o operație XOR pentru a talmeș-balmeș biți oferindu-mi astfel un număr aparent aleatoare. Desigur, ei nu sunt cu adevărat aleatoare, dar ele arata atat pentru utilizatori ochi.


[Edit] Informații suplimentare

logica Acest algoritm merge ca acest lucru să utilizați o secvență cunoscută pentru a genera numere unice și apoi să le manipuleze determinist, astfel încât acestea să nu mai uite de serie. Soluția generală este de a utiliza o anumită formă de criptare, care, în cazul meu a fost un flipflop XOR, pentru că ei cât de repede se poate obține, și îndeplinește garanția că numerele nu se vor ciocni.

Cu toate acestea, puteți utiliza alte forme de criptare, dacă doriți preferați numerele în căutarea chiar mai aleatoare, peste viteza (spun că nu aveți nevoie pentru a genera mai multe ID-uri la un moment dat). Acum, punctul important în alegerea unui algoritm de criptare este „garanția că numerele nu se vor ciocni“. Și o modalitate de a dovedi dacă un algoritm de criptare poate îndeplini această garanție este de a verifica dacă atât numărul inițial și rezultatul criptării au același număr de biți, și că algoritmul este reversibil (bijectie).

[Datorită Adam Liss & CesarB pentru exapanding pe soluția]

Publicat 26/11/2008 la 02:51
sursa de către utilizator

voturi
1

Problema este că, dacă sunt generatoare de numere aleatoare este foarte posibil să se producă duplicate infinatly.

in orice caz:

<?php
//Lets assume we already have a connection to the db
$sql = "SELECT randField FROM tableName";
$result = mysql_query($sql);
$array = array();
while($row = mysql_fetch_assoc($result))
 {
   $array[] = $row['randField'];
 }
while(True)
 {
   $rand = rand(0, 999999);
   if(!in_array($rand))
     {
       //This number is not in the db so use it!
       break;
     }
 }
?>

In timp ce acest lucru va face ceea ce-l doresc prea, este o idee rea ca acest lucru nu va scala pentru mult timp, eventualy matrice va ajunge la mare și va dura un timp extrem de lung pentru a genera o întâmplare care nu este deja în dB dvs. .

Publicat 26/11/2008 la 02:55
sursa de către utilizator

voturi
2

presupunând:

  • Randomizarea este necesar pentru unicitatea, nu pentru securitate
  • user_id dvs. este pe 32 de biți
  • Limita dvs. de 9999999 a fost doar un exemplu

Ai putea face ceva simplu ca având numărul aleatoriu ca un număr întreg de 64 de biți, cu 32 de biți superiori conținând timestamp-ul (la Inserare rând) și de cei 32 biti USER_ID. Asta ar fi unic chiar și pentru mai multe rânduri cu același utilizator, cu condiția să utilizați o rezoluție adecvată privind marcajul temporal în funcție de cât de des adăugați noi rânduri pentru același utilizator. Se combină cu o constrângere unică pe coloana aleatoare și captura orice astfel de eroare în logica și apoi pur și simplu încercați din nou.

Publicat 26/11/2008 la 03:00
sursa de către utilizator

voturi
1

Este ușor de a proiecta un generator de numere pseudoaleatoare cu o perioadă lungă de nonrepetition; de exemplu , aceasta , care este folosit pentru același lucru pe care îl doriți pentru.

BTW, de ce să nu emită doar secvențial userid lui?

Publicat 26/11/2008 la 03:02
sursa de către utilizator

voturi
0

PHP are deja o functie pentru aceasta, uniqid . Acesta generează un UUID standard , care este mare , dacă aveți pentru a accesa datele din altă parte. Nu reinventeze roata.

Publicat 26/11/2008 la 03:06
sursa de către utilizator

voturi
6

Vrei o solutie over-the-top?

Presupun intamplarea nu este destinat să fie de criptare de calitate, dar suficient pentru a descuraja ghicitul longevitatea unui utilizator, prin USER_ID.

În timpul dezvoltării, de a genera o listă cu toate cele 10 de milioane de numere în formă de șir de caractere.

Opțional, efectua unele transformări simple, cum ar fi adăugarea unui șir constant la mijloc. (Acesta este doar în cazul în care rezultatul este prea previzibil.)

Trece - le într - un instrument care generează funcții hash perfecte , cum ar fi gperf .

Codul rezultat poate fi utilizat pentru a codifica rapid ID-ul utilizatorului în timpul rulării într-o valoare unică hash care este garantat să nu intre în conflict cu alte valori hash.

Publicat 26/11/2008 la 03:16
sursa de către utilizator

voturi
17

De ce nu folosiți doar un GUID? Cele mai multe limbi ar trebui să aibă un mod de built-in pentru a face acest lucru. Este garantat să fie unic (cu limite foarte rezonabile).

Publicat 26/11/2008 la 03:19
sursa de către utilizator

voturi
1

Îmi place ideea Oddthinking lui, dar în loc de a alege cea mai puternică funcție hash din lume, ai putea pur și simplu:

  • Generează anii MD5 a primelor 10 de milioane de numere (exprimate ca siruri de caractere, + putina sare)
  • Verificați duplicate off - line , adică înainte de a merge în producție (cred că nu va fi nici o )
  • Depozitați duplicatele într-o matrice undeva
  • Atunci când aplicația pornește, încărcați matrice
  • Când doriți să inserați un ID, pentru a alege numărul următor, calcula MD5 acestuia, verificați dacă acesta este în matrice, și dacă nu este utiliza ca ID-ul în baza de date. În caz contrar, pentru a alege numărul următor

lui MD5 sunt rapide, și a verifica dacă un șir aparține unei matrice veți evita o SELECT.

Publicat 26/11/2008 la 03:41
sursa de către utilizator

voturi
3

Încercați declarația în mysql SELECT CAST (RAND () * 1,000,000 AS INT)

Publicat 26/11/2008 la 08:51
sursa de către utilizator

voturi
1

De fapt , am scris anterior un articol despre acest lucru . Este nevoie de aceeași abordare ca și răspunsul lui Robert Gould, dar în plus , arată cum să scurteze un cifru bloc la o lungime adecvată , folosind XOR de pliere, și apoi cum să genereze permutările de peste un interval care nu este o putere de 2, în timp ce încă protejând proprietate unicitate.

Publicat 26/11/2008 la 11:13
sursa de către utilizator

voturi
0

Probabil nu am prinde punctul de vedere, dar ce despre auto_increments?

Publicat 27/11/2008 la 19:11
sursa de către utilizator

voturi
1

Dacă într-adevăr doriți să obțineți numere „aleatoare“ forma zero-nouă 999 999, atunci soluția este de a face „randomizare“ o dată, și apoi stoca rezultatul pe disc.

Nu este greu pentru a obține rezultatul dorit, dar cred că de ea mai mult ca „face o listă lungă cu numere“, decât „obține un număr aleatoriu“.

$array = range(0, 9999999);
$numbers = shuffle($array);

Ai nevoie, de asemenea, un pointer la poziția curentă în număr $ (păstrați-l într-o bază de date); începe cu 0 și incrementa-l de fiecare dată când aveți nevoie de un număr nou. (Sau ai putea folosi array_shift () sau array_pop (), dacă tu dont doriți să utilizați indicii.)

Publicat 27/11/2008 la 23:41
sursa de către utilizator

voturi
1

O buna prng (Pseudo-Random Number Generator) algoritm va avea un ciclu de timp în timpul căreia nu va fi niciodată în aceeași stare. Dacă vă expune întreaga stare a prng numărului recuperat de la ea, veți primi un număr garantat unic pentru perioada generatorului.

Un prng simplu care face acest lucru se numește „ Linear Congruential prng“ , care o formulă reiterează:

X(i) = AX(i-1)|M

Folosind dreptul de pereche de factori puteți obține o perioadă de 2 ^ 30 (aproximativ 1 miliard) dintr-un prng simplu, cu un acumulator de 32 de biți. Rețineți că veți avea nevoie de un pic mai lung de 64 de lungă variabilă temporară pentru a menține elementul intermediar „AX“ din calcul. Cele mai multe, dacă nu toate compilatoare C va sprijini acest tip de date. Ar trebui să fie, de asemenea, posibilitatea de a face cu un tip de date numerice pe cele mai multe dialecte SQL.

Cu valorile corecte ale lui A și M putem obține un generator de numere aleatoare cu proprietăți statistice și geometrice bune. Există un faimos material despre scris de Fishman și Moore.

Pentru M = 2 ^ 31-1 obținem pot folosi valorile A de mai jos pentru a obține un prng cu o frumoasă perioadă lungă (2 ^ 30 IIRC).

Valori bune de A:

742,938,285  
950,706,376  
1,226,874,159  
62,089,911  
1,343,714,438   

Rețineți că acest tip de generator este (prin definiție) nu criptografic securizat. Dacă știți ultimul număr generat de la ea poti prezice ce va face în continuare. Din păcate , cred că nu se poate obține de securitate criptografică și garantată fără repetabilitate în același timp. Pentru o prng să fie criptografic securizat ( de exemplu , Blum Blum Shub ) nu poate expune starea suficientă într - un număr generat pentru a permite numărul următor în secvența care urmează să fie prezise. Prin urmare , starea internă este mai mare decât numărul generat și (pentru a avea securitate bun) perioada va fi mai mare decât numărul de valori posibile care pot fi generate. Acest lucru înseamnă că numărul expus nu va fi unic în perioada.

Din motive similare , același lucru este valabil generatoare de perioadă lungă , cum ar fi Mersenne Twister.

Publicat 27/11/2008 la 23:59
sursa de către utilizator

voturi
1

există două moduri de a merge despre acest lucru într-un fel ar fi de a construi o matrice cu numerele 0000000, prin 9999999 și apoi alege un pick aleatoare a acestor numere în această matrice și swap valorile numere alese cu cea mai mare valoare maximă, apoi a reduce max de 1 și alege un alt membru aleatoriu al acestei matrice până la noul maxim

de fiecare dată reducând Max de unul

de exemplu (în bază): (spre dreapta sunt observații care ar trebui să fie eliminate în programul real) Rndfunc este un apel la orice număr aleatoriu generator de funcții pe care îl utilizați

dim array(0 to 9999999) as integer
for x% = 1 to 9999999
array(x%)=x%
next x%
maxPlus = 10000000
max =9999999
pickedrandom =int(Rndfunc*maxPlus)  picks a random indext of the array based on    
                                   how many numbers are left
maxplus = maxplus-1
swap array(pickedrandom) , array(max) swap this array value to the current end of the
                                     array 
max = max -1                   decrement the pointer of the max array value so it 
                              points to the next lowest place..

apoi continua să faci acest lucru pentru fiecare număr pe care doriți să alegeți, dar va trebui să aibă opțiunea de a utiliza matrice foarte mari

Cealaltă metodă ar fi după cum urmează: a genera un număr și păstrați-l într-o matrice care poate crește în mod dinamic apoi, după care alegeți un număr nou și compara-l la valoarea pe care este la jumătatea distanței de la primul la ultimul element din matrice, în acest caz, ar fi primul număr ales dacă se potrivește alege un alt număr aleatoriu, sortare matrice în funcție de mărimea și dacă nu există o potrivire, apoi, în funcție de vreme este mai mare sau mai mic decât numărul pe care-l în comparație cu tine merge în sus sau în jos în lista jumătate jumătatea distanței, de fiecare dată că nu se potrivește și este mai mare sau mai mică decât ceea ce-l comparativ cu.

de fiecare dată când reducerea la jumătate ea până când ajunge la o dimensiune de decalaj de una, atunci verificați o dată și se va opri, deoarece nu există nici un meci, iar apoi se adaugă numărul pe listă și lista este remaniat în ordine crescătoare, așa mai departe și așa mai departe până când nu sunt face alegerea numere aleatoare ... sper că acest lucru ajută ..

Publicat 27/01/2012 la 14:05
sursa de către utilizator

voturi
0

Dacă doriți să se asigure că numerele aleatoare nu se repetă, aveți nevoie de un non-repetarea numere aleatorii generator (așa cum este descris aici ).

Ideea de bază este că următoarea formulă seed * seed & pva produce nerepetitiva aleatoare de numere pentru orice intrare x such that 2x < pși p - x * x % pproduce toate celelalte număr aleatoriu la fel de bine non-repetitivă, dar numai în cazul în care p = 3 mod 4. Deci , practic tot ce ai nevoie este un singur primnumber cât mai aproape de 9999999posibil. Astfel , efortul poate fi redus la un singur câmp de citire, dar cu dezavantajul că , fie prea mari , ID - uri sunt generate sau vor fi generate prea puține ID - uri.

Acest algoritm nu permuta foarte bine, asa ca mi-ar recomanda combinarea fie cu XOR sau adăugarea sau o altă abordare pentru a modifica valoarea exactă fără a distruge 1-la-1-relația dintre semințe și valoarea generată.

Publicat 04/10/2015 la 22:49
sursa de către utilizator

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more