Algoritmo per generare un numero casuale

voti
7

Sto cercando di generare un numero casuale ed emettere a una tabella in un database per un particolare user_id. La cattura è, lo stesso numero non può essere utilizzato due volte. C'è un milione di modi per farlo, ma spero che qualcuno molto appassionato di algoritmi ha un modo intelligente di risolvere il problema in una soluzione elegante in quanto i seguenti criteri è soddisfatto:

1) La minor quantità di query al database sono fatti. 2) La quantità minima di scansione attraverso una struttura di dati in memoria è fatta.

In sostanza l'idea è di fare quanto segue

1) Creare un numero casuale 0-9.999.999
2) Controllare il database per vedere se il numero esiste
OR
2) interrogare il database per tutti i numeri
3) Verificare se le partite di risultati restituiti qualunque provenivano dal db
4) Se corrisponde, ripetere fase 1, se non, problema è risolto.

Grazie.

È pubblicato 26/11/2008 alle 02:44
fonte dall'utente
In altre lingue...                            


17 risposte

voti
1

Penso che troverete che davvero non si vuole fare questo. Come i numeri in aumento del database, si potrebbe spendere troppo tempo nella "assicurarsi che questo numero non è preso" loop.

Personalmente, ho avuto fortuna con gli hash come alternativa, ma di trovare una soluzione migliore, avrei davvero bisogno di sapere perché si vuole fare in questo modo.

Risposto il 26/11/2008 a 02:51
fonte dall'utente

voti
1

La mia esperienza è stata semplicemente utilizzando il RNG in PHP. Ho trovato che l'uso di una certa dimensione del numero (sto usando un int, quindi ho un massimo di 4G). Ho eseguito alcuni test e ha scoperto che, in media, a 500.000 iterazioni, ho ottenuto 120 singoli duplicati. Non ho mai avuto un triplice copia dopo l'esecuzione del ciclo un mucchio di volte. La mia "soluzione" è stato quello di allora basta inserire e verificare se non riesce, quindi generare un nuovo ID e andare di nuovo.

Il mio consiglio è di fare lo stesso e vedere che cosa il vostro tasso di collisione è & C e vedere se è accettabile per il vostro caso.

Questo non è ottimale, quindi se qualcuno ha suggerimenti non vedo troppo :)

EDIT: mi è stato limitato a un ID di 5 cifre ([a-zA-Z0-9] {5,5}), più lungo è il id (più combinazioni, le poche collisioni). Un md5 della mail sarebbe quasi mai in conflitto, per esempio.

Risposto il 26/11/2008 a 02:51
fonte dall'utente

voti
17

No vostro algoritmo non è scalabile. Quello che ho fatto prima è quella di emettere i numeri in serie (uno alla volta) e poi passare attraverso un'operazione XOR per jumble i bit dando così mi ha un numero apparentemente casuali. Naturalmente non sono veramente casuali, ma hanno un aspetto per così gli occhi degli utenti.


[Edit] Ulteriori informazioni

La logica di questo algoritmo va in questo modo si utilizza una sequenza conosciuta per generare numeri unici e quindi è deterministicamente li manipolare, in modo da non guardare più serie. La soluzione generale è quella di utilizzare una qualche forma di cifratura, che nel mio caso è stato un flip-flop XOR, perché la sua veloce come si può ottenere, e soddisfa la garanzia che i numeri non potrà mai entrare in collisione.

Tuttavia è possibile utilizzare altre forme di crittografia, se si desidera che preferisce i numeri che cercano ancora di più casuali, sopra la velocità (dici che non c'è bisogno di generare molti ids alla volta). Ora il punto importante nella scelta di un algoritmo di crittografia è "la garanzia che i numeri non potrà mai entrare in collisione". E un modo per dimostrare se un algoritmo di crittografia in grado di adempiere a tale garanzia è quello di verificare se sia il numero originale e il risultato della cifratura hanno lo stesso numero di bit, e che il l'algoritmo è reversibile (bijection).

[Grazie a Adam Liss & CesarB per exapanding sulla soluzione]

Risposto il 26/11/2008 a 02:51
fonte dall'utente

voti
1

Il problema è che se si sta generando numeri casuali è molto è possibile produrre duplicati infinatly.

però:

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

Anche se questo sarà fare quello che vuoi anche tu, è una cattiva idea in quanto questo non scala per lungo tempo, eventualy l'array è arrivare al grande e ci vorrà un tempo estremamente lungo per generare un caso che non sia già in vostro db .

Risposto il 26/11/2008 a 02:55
fonte dall'utente

voti
2

assumendo:

  • La casualità è necessario per l'unicità, non per la sicurezza
  • Il tuo user_id è a 32 bit
  • Il tuo limite di 9999999 era solo un esempio

Si potrebbe fare qualcosa semplice come avere il numero casuale come un intero a 64 bit, con i 32 bit superiori contengono il timestamp (a inserto fila) ed i 32 bit inferiori del user_id. Sarebbe unica anche per i più righe con lo stesso utente, a patto di utilizzare una risoluzione appropriata sul vostro timestamp seconda di come spesso si aggiungono nuove righe per lo stesso utente. Combinare con un vincolo univoco sulla colonna casuale e catturare qualsiasi errore nella logica e poi basta riprovare.

Risposto il 26/11/2008 a 03:00
fonte dall'utente

voti
1

E 'facile per la progettazione di un generatore di numeri pseudo con un lungo periodo di nonrepetition; ad esempio, questo , che viene utilizzato per la stessa cosa che si desidera per.

BTW, perché non basta rilasciare sequenzialmente del userid?

Risposto il 26/11/2008 a 03:02
fonte dall'utente

voti
0

PHP ha già una funzione per questo, uniqid . Esso genera un UUID standard che è grande se si dispone di accedere ai dati da altrove. Non reinventare la ruota.

Risposto il 26/11/2008 a 03:06
fonte dall'utente

voti
6

Vuoi una soluzione over-the-top?

Presumo casualità non è destinato ad essere la crittografia di qualità, ma quel tanto che basta per scoraggiare indovinare la longevità di un utente, da user_id.

Durante lo sviluppo, generare un elenco di tutti i 10 milioni di numeri in forma di stringa.

Facoltativamente, eseguire qualche semplice trasformazione, come l'aggiunta di una stringa costante al centro. (Questo è solo nel caso in cui il risultato è troppo prevedibile.)

Li passare in uno strumento che genera funzioni hash perfetta , come gperf .

Il codice risultante può essere utilizzato per codificare rapidamente l'ID dell'utente in fase di esecuzione in un valore hash univoco che è garantito di non scontrarsi con qualsiasi altro valore hash.

Risposto il 26/11/2008 a 03:16
fonte dall'utente

voti
17

Perché non basta usare un GUID? La maggior parte delle lingue dovrebbero avere un modo integrato per effettuare questa operazione. E 'garantito per essere unico (con limiti molto ragionevoli).

Risposto il 26/11/2008 a 03:19
fonte dall'utente

voti
1

Mi piace l'idea di Oddthinking, ma invece di scegliere la funzione hash più forte del mondo, si potrebbe semplicemente:

  • Generare il MD5 del dei primi 10 milioni di numeri (espressi come stringhe, + po 'di sale)
  • Verificare la presenza di duplicati in linea , vale a dire prima di andare in produzione (immagino non ci sarà alcuna)
  • Conservare i duplicati in un array da qualche parte
  • Quando l'applicazione viene avviata, caricare la matrice
  • Quando si desidera inserire un ID, scegliere il numero successivo, calcolare la sua MD5, controllare se è nella matrice, e se non si usa come l'ID nel database. In caso contrario, scegliere il numero successivo

MD5 di sono veloci, e controllando se una stringa appartiene ad un array eviterà una SELECT.

Risposto il 26/11/2008 a 03:41
fonte dall'utente

voti
3

Prova la dichiarazione in mysql SELEZIONA CAST (RAND () * 1000000 AS INT)

Risposto il 26/11/2008 a 08:51
fonte dall'utente

voti
1

In realtà ho già scritto un articolo su questo . Prende lo stesso approccio risposta di Robert Gould, ma inoltre mostra come accorciare un codice a blocchi per una lunghezza adeguata utilizzando xor piegatura, e quindi come generare le permutazioni su un intervallo che non è una potenza di 2, pur conservando la immobili unicità.

Risposto il 26/11/2008 a 11:13
fonte dall'utente

voti
0

Probabilmente non ho capito il tuo punto, ma per quanto riguarda auto_increments?

Risposto il 27/11/2008 a 19:11
fonte dall'utente

voti
1

Se davvero si vuole ottenere i numeri "casuali" modulo da 0 a 9 999 999, allora la soluzione è fare la "randomizzazione" una volta, e quindi memorizzare il risultato sul disco.

Non è difficile ottenere il risultato desiderato, ma ci penso più come "fare un lungo elenco con i numeri", che "ottenere un numero casuale".

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

È inoltre necessario un puntatore alla posizione corrente nei numeri $ (conservarlo in un database); iniziare con 0 ed incrementarlo ogni volta che è necessario un nuovo numero. (Oppure si potrebbe usare array_shift () o array_pop (), se non piace usare puntatori.)

Risposto il 27/11/2008 a 23:41
fonte dall'utente

voti
1

Un algoritmo corretto PRNG (Pseudo-Random Number Generator) avrà un tempo di ciclo durante il quale non sarà mai nello stesso stato. Se si espone l'intero stato del PRNG del numero recuperato da esso, si otterrà un numero garantito unico per il periodo del generatore.

Un semplice PRNG che fa questo è chiamato ' lineare congruential PRNG' che itera formula:

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

Utilizzando il giusto paio di fattori che si può ottenere un periodo di 2 ^ 30 (circa 1 miliardo) da un semplice PRNG con un 32 bit accumulatore. Si noti che è necessario un po '64 lungo tempo variabile temporanea per contenere la parte intermedia 'AX' di calcolo. La maggior parte se non tutti i compilatori C sosterrà questo tipo di dati. Si dovrebbe anche essere in grado di farlo con un tipo di dati numerici sulla maggior parte dei dialetti SQL.

Con i giusti valori di A e M possiamo ottenere un generatore di numeri casuali con buone proprietà statistiche e geometriche. C'è un famoso documento di questo scritto da Fishman e Moore.

Per M = 2 ^ 31-1 otteniamo possono utilizzare i valori di A seguito per ottenere un PRNG con un lungo periodo di bel (2 ^ 30 IIRC).

Buoni valori di A:

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

Si noti che questo tipo di generatore è (per definizione) non crittograficamente sicuro. Se si conosce l'ultimo numero generato da esso si può prevedere che cosa farà il prossimo. Purtroppo credo che non si può ottenere la sicurezza crittografica e garantita la non riproducibilità allo stesso tempo. Per un PRNG sia crittograficamente sicuro (es Blum Blum Shub ) non può esporre stato sufficiente un numero generato per consentire il successivo numero della sequenza da predire. Pertanto lo stato interno è più largo del numero generato e (in modo da avere una buona sicurezza) il periodo sarà più lungo del numero di possibili valori che possono essere generati. Ciò significa che il numero esposta non sarà unica entro il periodo.

Per ragioni analoghe stesso vale per generatori di lungo periodo come il Mersenne Twister.

Risposto il 27/11/2008 a 23:59
fonte dall'utente

voti
1

ci sono un paio di modi per andare su questo modo sarebbe di costruire una matrice con i numeri 0000000 attraverso 9999999 e quindi scegliere una scelta casuale di questi numeri in questo array e scambiare i valori numeri raccolti con il valore più alto Max quindi ridurre max da 1 e selezionare un altro membro casuale di questa matrice fino al nuovo massimo

di volta in volta la riduzione massima per uno

per esempio (in base): (a destra sono osservazioni che dovrebbero essere rimossi nel programma vero e proprio) Rndfunc è una chiamata alla funzione di generatore di numeri casuali tutto ciò che si sta utilizzando

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

poi continuare a fare questo per ogni numero che si desidera selezionare, ma è necessario avere la possibilità di utilizzare le matrici molto grandi

l'altro metodo sarebbe come segue: generare un numero e memorizzarlo in una matrice che può crescere dinamicamente poi, dopo che scegliere un nuovo numero e confrontarlo con il valore che è a metà strada dalla prima all'ultima elemento nella matrice in questo caso sarebbe il primo numero scelto se corrisponde scegliere un altro numero casuale, ordinare l'array in base alle dimensioni e se non v'è una corrispondenza quindi a seconda del tempo è maggiore o minore del numero lo comparate con andare su o giù l'elenco metà della metà della distanza, ogni volta che esso non corrisponde ed è maggiore o minore di quello che si sta confrontando a.

ogni volta dimezzare fino a raggiungere una dimensione gap di uno allora di controllare e arresterà in quanto non v'è alcuna corrispondenza, e quindi il numero viene aggiunto all'elenco e l'elenco viene rimescolato in ordine ascendente, così via e così via fino a sei fatto raccogliendo numeri casuali ... spero che questo aiuti ..

Risposto il 27/01/2012 a 14:05
fonte dall'utente

voti
0

Se si vuole garantire che i casuali numeri non stanno ripetendo, è necessario un numero di-generatore non ripetibile casuale (come descritto qui ).

L'idea di base è che la seguente formula seed * seed & psarà prodotta casuali numeri non multipli per qualsiasi ingresso x such that 2x < pe p - x * x % pproduce tutti gli altri numeri casuali aswell non ripetibile, ma solo se p = 3 mod 4. Quindi, in pratica tutto ciò che serve è un singolo primnumber più vicino al 9999999possibile. In questo modo lo sforzo può essere ridotto a un singolo campo di lettura, ma con il lato negativo che sia troppo grande ID vengono generati o troppo pochi gli ID saranno generati.

Questo algoritmo non permutare molto bene, quindi consiglio combinazione con uno o XOR aggiunta o qualche altro metodo per modificare il valore esatto senza distruggere il 1-a-1-relazione tra i semi e il loro valore generato.

Risposto il 04/10/2015 a 22:49
fonte dall'utente

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