Algoritme for generering av et tilfeldig tall

stemmer
7

Jeg ønsker å generere et tilfeldig tall og utstede det til en tabell i en database for en bestemt user_id. Fangsten, samme antall kan ikke brukes to ganger. Det finnes en million måter å gjøre dette, men jeg håper noen veldig ivrig på algoritmer har en smart måte å løse problemet på en elegant løsning ved at følgende kriterier er oppfylt:

1) Den minste mengde av forespørsler til databasen er gjort. 2) minst mulig gjennomgang gjennom en datastruktur i lageret lages.

I hovedsak ideen er å gjøre følgende

1) Lag et tilfeldig tall 0-9999999
2) Kontroller databasen for å se om nummeret finnes
OR
2) søke i databasen for alle tall
3) Se om den returnerte kampene uansett kom fra db
4) Hvis det stemmer, gjentar trinn 1, hvis ikke, blir problemet løst.

Takk.

Publisert på 26/11/2008 klokken 01:44
kilden bruker
På andre språk...                            


17 svar

stemmer
1

Jeg tror du vil finne at du egentlig ikke ønsker å gjøre dette. Som tallene i databasen øker, kan du bruke for mye tid i "sørge for at dette tallet er ikke tatt" loop.

Personlig har jeg hatt flaks med hashes som et alternativ, men for å komme opp med en bedre løsning, ville jeg virkelig trenger å vite hvorfor du ønsker å gjøre det på denne måten.

Svarte 26/11/2008 kl. 01:51
kilden bruker

stemmer
1

Min erfaring var rett og slett å bruke RNG i PHP. Jeg fant at bruk av en viss størrelse på antall (jeg bruker en int, så jeg har en max på 4G). Jeg kjørte noen tester og funnet ut at i gjennomsnitt i 500.000 iterasjoner, fikk jeg 120 enkelt duplikater. Jeg fikk aldri en tre eksemplarer etter å ha kjørt løkken en rekke ganger. Min "løsning" var å så bare sette inn og sjekke om det mislykkes, deretter generere en ny ID og gå igjen.

Mitt råd er å gjøre det samme og se hva din kollisjon rate er & c og se om det er akseptabelt for ditt tilfelle.

Dette er ikke optimalt, så jeg ser også på om noen har forslag :)

EDIT: I var begrenset til et 5 sifret ID ([a-zA-Z0-9] {5,5}), jo lengre id (mer kombinasjon, de få kollisjoner). En md5 av e-posten vil nesten konflikt aldri, for eksempel.

Svarte 26/11/2008 kl. 01:51
kilden bruker

stemmer
17

Ingen algoritmen er ikke skalerbar. Hva jeg har gjort før er å utstede tall serielt (en hver tid) og deretter sende dem gjennom en XOR operasjon for å virvar biter og dermed gi meg et tilsynelatende tilfeldige tall. Selvfølgelig er de ikke egentlig tilfeldig, men de ser så til brukernes øyne.


[Edit] Ytterligere informasjoner

Denne algoritmen logikk går slik du bruker en kjent sekvens for å generere unike numre og så deterministisk manipulere dem, så de ikke ser serie lenger. Den generelle løsningen er å bruke noen form for kryptering, som i mitt tilfelle var en XOR-vippen, fordi det så fort som det kan få, og det oppfyller garanti for at tallene aldri vil kollidere.

Men du kan bruke andre former for kryptering, hvis du ønsker foretrekker enda mer tilfeldige ser tallene, fremfor hastighet (si at du ikke trenger å generere mange IDer om gangen). Nå viktig poeng i å velge en krypteringsalgoritme er "garanti for at tallene aldri vil kollidere". Og en måte å bevise om en krypteringsalgoritme kan oppfylle denne garantien er å sjekke om både det opprinnelige nummeret, og resultatet av krypteringen har samme antall biter, og at algoritmen er reversibel (Bijeksjon).

[Takk til Adam Liss & CesarB for exapanding på løsningen]

Svarte 26/11/2008 kl. 01:51
kilden bruker

stemmer
1

Problemet er at hvis du genererer tilfeldige tall er det meget mulig å produsere duplikater infinatly.

derimot:

<?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;
     }
 }
?>

Selv om dette vil gjøre hva du vil ha det også, det er en dårlig idé, da dette ikke vil skalere for lenge, eventualy array vil få store og det vil ta svært lang tid å generere en tilfeldig som ikke allerede er i db .

Svarte 26/11/2008 kl. 01:55
kilden bruker

stemmer
2

Forutsatt:

  • Tilfeldig er nødvendig for unikhet, ikke for sikkerhet
  • Din user_id er 32 bit
  • Grensen av 9999999 var bare et eksempel

Man kan gjøre noe enkelt som å ha det tilfeldige tallet som en 64 bits heltall, med de øvre 32 biter som inneholder tidsstempelet (i rad insert) og de nedre 32 bitene BRUKER-ID. Det ville være unikt selv for flere rader med samme bruker, forutsatt at du bruker en passende oppløsning på tidsstempel avhengig av hvor ofte du legger til nye rader for samme bruker. Kombiner med en entydig begrensning på tilfeldig kolonne og fange slike feil i din logikk og så bare prøve på nytt.

Svarte 26/11/2008 kl. 02:00
kilden bruker

stemmer
1

Det er lett å lage en pseudo tall generator med en lang periode med nonrepetition; for eksempel denne , som blir brukt for det samme som du vil ha det til.

BTW, hvorfor ikke bare utstede brukerid er sekvensielt?

Svarte 26/11/2008 kl. 02:02
kilden bruker

stemmer
0

PHP har allerede en funksjon for dette, uniqid . Det genererer en standard UUID som er flott hvis du har tilgang til data fra andre steder. Ikke gjenoppfinne hjulet.

Svarte 26/11/2008 kl. 02:06
kilden bruker

stemmer
6

Vil en over-the-top løsning?

Jeg antar at tilfeldig er ikke ment å være kryptering kvalitet, men akkurat nok til å ta motet gjette lang av en bruker, ved user_id.

Under utviklingen generere en liste over alle 10 millioner tallene i streng form.

Eventuelt kan utføre noen enkel transformasjon, som å legge til en konstant streng til midten. (Dette er bare i tilfelle resultatet er altfor forutsigbar.)

Sende dem til et verktøy som genererer Perfect Hash-funksjoner , som for eksempel gperf .

Den resulterende koden kan brukes til raskt å kode brukerens id under kjøring inn i et unikt hash-verdi som er garantert ikke å være i konflikt med eventuelle andre hash-verdier.

Svarte 26/11/2008 kl. 02:16
kilden bruker

stemmer
17

Hvorfor ikke bare bruke en GUID? De fleste språk bør ha en innebygd måte å gjøre dette. Det er garantert å være unik (med svært fornuftige grenser).

Svarte 26/11/2008 kl. 02:19
kilden bruker

stemmer
1

Jeg liker Oddthinking idé, men i stedet for å velge den sterkeste hash-funksjon i verden, kan du ganske enkelt:

  • Generere MD5 er av de første 10 millioner av tall (uttrykt som strenger, + litt salt)
  • Se etter duplikater offline , altså før du går i produksjon (Jeg antar det vil ikke være noen)
  • Lagre duplikater i en rekke sted
  • Når programmet starter, legger matrisen
  • Når du vil sette inn en ID, velger du det neste nummeret, beregne dens MD5, sjekk om det er i rekken, og hvis det ikke er å bruke det som ID i databasen. Ellers velger neste nummer

MD5-er rask, og sjekke om en streng tilhører en array vil unngå deg en SELECT.

Svarte 26/11/2008 kl. 02:41
kilden bruker

stemmer
3

Prøv uttalelse i mysql SELECT CAST (RAND () * million AS INT)

Svarte 26/11/2008 kl. 07:51
kilden bruker

stemmer
1

Jeg har faktisk tidligere skrevet en artikkel om dette . Det tar samme tilnærming som Robert Gould svar, men i tillegg viser hvordan å forkorte en blokk chiffer til en passende lengde ved hjelp av XOR folding, og deretter hvordan å generere permutasjoner over et område som ikke er en potens av 2, samtidig som man beholder den unikhet eiendom.

Svarte 26/11/2008 kl. 10:13
kilden bruker

stemmer
0

Jeg sannsynligvis ikke ta poenget ditt, men hva med auto_increments?

Svarte 27/11/2008 kl. 18:11
kilden bruker

stemmer
1

Hvis du virkelig ønsker å få "tilfeldige" tall skjema 0-9 999 999, så løsningen er å gjøre det "randomisering" en gang, og deretter lagre resultatet til disken.

Det er ikke vanskelig å få det resultatet du ønsker, men jeg tenker på det mer som "lage en lang liste med tall", enn "få et tilfeldig tall".

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

Du trenger også en peker til nåværende posisjon i $ tall (lagre den i en database); starter med 0 og øke den hver gang du trenger et nytt nummer. (Eller du kan bruke array_shift () eller array_pop (), hvis du ikke liker å bruke pekere.)

Svarte 27/11/2008 kl. 22:41
kilden bruker

stemmer
1

En skikkelig PRNG (Pseudo-Random Number Generator) algoritme vil ha en syklus tid hvor det aldri vil være i samme tilstand. Hvis du utsetter hele delstaten PRNG i antall hentet fra det, vil du få en rekke garantert unik for perioden av generatoren.

En enkel PRNG som gjør dette kalles ' Linear kongruentrekursjonssekvens ' PRNG som itererer en formel:

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

Bruke riktig par faktorer du kan få en periode på 2 ^ 30 (ca. 1 milliard) fra en enkel PRNG med en 32 bit akkumulator. Merk at du trenger en 64 bit lang lang midlertidig variabel å holde mellom 'AX' en del av beregningen. De fleste om ikke alle C-kompilatorer vil støtte denne datatypen. Du bør også være i stand til å gjøre det med en numerisk datatype på de fleste SQL-dialekter.

Med de riktige verdiene av A og M kan vi få et tilfeldig tall generator med gode statistiske og geometriske egenskaper. Det er en kjent papir om dette er skrevet av Fishman og Moore.

For M = 2 ^ 31-1 vi får bruke verdiene av A nedenfor for å få en PRNG med en fin lang periode (2 ^ 30 IIRC).

Gode ​​verdier av A:

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

Merk at denne type generator er (per definisjon) ikke kryptografisk sikker. Hvis du vet det siste nummeret som genereres fra det du kan forutsi hva det vil gjøre videre. Dessverre tror jeg at du ikke kan få kryptografisk sikkerhet og garantert ikke-repeterbarhet på samme tid. For en PRNG å være kryptografisk sikker (f.eks Blum Blum Shub ) det kan ikke blottlegge tilstrekkelig tilstand i et generert tall for å tillate den neste nummer i sekvensen som skal forutsies. Derfor er den interne tilstanden er bredere enn det genererte tall og (for å ha god sikkerhet) det tar lengre tid enn antallet mulige verdier som kan bli generert. Dette betyr at den eksponerte nummeret ikke vil være unik innen fristen.

Av lignende grunner er det samme gjelder for langperiodiske generatorer som Mersenne Twister.

Svarte 27/11/2008 kl. 22:59
kilden bruker

stemmer
1

det er et par måter å gå om denne måten ville være å konstruere en matrise med tallene 0000000 gjennom 9.999.999 og deretter plukke en tilfeldig plukke av disse tallene i denne tabellen og bytte plukket tall verdier med den høyeste verdien Max deretter redusere max med 1 og velge en annen tilfeldig medlem av denne matrisen opp til den nye maksimums

hver gang reduseres med maksimal ett

for eksempel (i grunn): (til høyre er kommentarer som bør fjernes i selve programmet) Rndfunc er en oppfordring til det tilfeldig nummer generator funksjonen du bruker

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..

deretter fortsette å gjøre dette for hvert nummer du ønsker å plukke, men du må ha muligheten til å bruke svært store arrays

den andre metode vil være som følger: generere et tall og lagrer den i en matrise som kan vokse dynamisk deretter etter at velge en ny rekke og sammenligne den med den verdi som er halvveis fra det første til det siste element i gruppen i dette tilfellet det ville være det første nummeret plukket om det samsvarer velge en annen tilfeldig tall, sortere tabellen etter størrelse og hvis det ikke er en kamp så avhengig av været det er større eller mindre enn antallet du sammenlignet den med du går opp eller ned i listen halvparten av halve distansen, hver gang at det ikke samsvarer og er større eller mindre enn hva du sammenligner det til.

hver gang halvere det inntil du når et gap størrelse på en så du sjekke en gang og stoppe når det ikke er noen kamp, ​​og deretter nummeret er lagt til listen og listen er stokkes i stigende rekkefølge, så videre og så videre til du er gjort plukke tilfeldige tall ... håper dette hjelper ..

Svarte 27/01/2012 kl. 13:05
kilden bruker

stemmer
0

Hvis du ønsker å sikre at de tilfeldige-tallene ikke er å gjenta, trenger du en ikke-repeterende tilfeldig tall-generator (som beskrevet her ).

Den grunnleggende idé er at den følgende formel seed * seed & pblir produsert ikke-repeterende tilfeldig-tall for en hvilken som helst inngang x such that 2x < pog p - x * x % pfrembringer all annen tilfeldig-tall så vel som ikke-repeterende, men bare hvis p = 3 mod 4. Så i utgangspunktet alt du trenger er et enkelt primnumber så nær 9999999som mulig. På denne måten arbeidet kan reduseres til en enkelt lese-feltet, men med ulempen at enten er for store IDer genereres eller for få ID-er vil bli generert.

Denne algoritmen ikke permutere veldig godt, så jeg vil anbefale å kombinere det med enten XOR eller tillegg eller en annen tilnærming til å endre den eksakte verdien uten å ødelegge en-til-en-forhold mellom frø og deres genererte verdi.

Svarte 04/10/2015 kl. 22:49
kilden bruker

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