Algorithme pour générer un nombre aléatoire

voix
7

Je cherche à générer un nombre aléatoire et d'émettre à une table dans une base de données pour un user_id particulier. Le hic est, le même nombre ne peut pas être utilisé deux fois. Il y a un million de façons de le faire, mais je suis en espérant que quelqu'un très vif sur des algorithmes a une façon intelligente de résoudre le problème dans une solution élégante en ce que les critères suivants sont respectés:

1) La moindre quantité de requêtes à la base de données sont effectués. 2) La moindre quantité de ramper à travers une structure de données dans la mémoire est effectuée.

Essentiellement, l'idée est de faire ce qui suit

1) Créer un nombre aléatoire 0-9999999
2) Vérifiez la base de données pour voir si le numéro existe
OU
2) Interroger la base de données pour tous les numéros
3) Voir si les matchs de résultats renvoyés tout ce qui venait de la db
4) Si elle correspond, répétez étape 1, sinon, problème est résolu.

Merci.

Créé 26/11/2008 à 02:44
source utilisateur
Dans d'autres langues...                            


17 réponses

voix
1

Je pense que vous constaterez que vous ne voulez vraiment pas faire cela. Comme les chiffres de l'augmentation de la base de données, vous pouvez passer trop de temps dans le « assurez-vous que ce nombre ne sont pas prises » boucle.

Personnellement, j'ai eu la chance avec hash comme une alternative, mais de trouver une meilleure solution, je vraiment besoin de savoir pourquoi vous voulez le faire de cette façon.

Créé 26/11/2008 à 02:51
source utilisateur

voix
1

Mon expérience a été tout simplement en utilisant le RNG en PHP. Je trouve que l'utilisation d'une certaine taille du nombre (j'utilise un int, j'ai donc un maximum de 4G). J'ai couru quelques tests et a constaté que, en moyenne, en 500.000 itérations, je suis 120 copies simples. Je ne ai jamais eu un triple après avoir exécuté la boucle un tas de fois. Ma « solution » était alors il suffit d'insérer et de vérifier si elle échoue, générer un nouvel ID et aller à nouveau.

Mon conseil est de faire la même chose et voir ce que votre taux de collision etc. et voir si elle est acceptable pour votre cas.

Ce n'est pas optimale, donc si quelqu'un a des suggestions que je suis à la recherche aussi :)

EDIT: Je suis limité à un ID à 5 chiffres ([a-zA-Z0-9] {5,5}), plus l'identifiant (combinaison plus, les quelques collisions). Un md5 du courrier électronique serait en conflit presque jamais, par exemple.

Créé 26/11/2008 à 02:51
source utilisateur

voix
17

Pas votre algorithme n'est pas extensible. Ce que je l'ai fait avant est d'émettre des numéros en série (1 à chaque fois) et les transmettre ensuite à travers une opération XOR pour pêle-mêle les bits me donnant ainsi un nombre apparemment au hasard. Bien sûr, ils ne sont pas vraiment au hasard, mais ils regardent ainsi les yeux des utilisateurs.


[Modifier] Informations complémentaires

La logique de cet algorithme va comme ceci vous utilisez une séquence connue pour générer des nombres uniques et les manipuler de façon déterministe, donc ils ne regardent pas plus de série. La solution générale est d'utiliser une certaine forme de cryptage, qui dans mon cas était un flipflop XOR, parce que son aussi vite qu'il peut obtenir, et il remplit la garantie que les chiffres ne seront jamais entrer en collision.

Cependant, vous pouvez utiliser d'autres formes de chiffrement, si vous voulez préférez encore plus aléatoires chiffres à la recherche, sur la vitesse (dites que vous n'avez pas besoin de générer beaucoup ids à la fois). Maintenant, le point important dans le choix d'un algorithme de chiffrement est « la garantie que les chiffres ne seront jamais entrer en collision ». Et une façon de prouver si un algorithme de chiffrement peut répondre à cette garantie est de vérifier si les deux le nombre initial et le résultat du chiffrement ont le même nombre de bits, et que l'algorithme est réversible (bijection).

[Merci à Adam Liss & CesarB pour exapanding sur la solution]

Créé 26/11/2008 à 02:51
source utilisateur

voix
1

Le problème est que si vous générer des nombres aléatoires est est très possible de produire des doublons infinatly.

toutefois:

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

Bien que cela va faire ce que vous voulez aussi, il est une mauvaise idée que ce ne sera pas pour longtemps l'échelle, eventualy votre tableau arrivera à grande et il faudra un temps extrêmement long pour générer un hasard qui n'est pas déjà dans votre db .

Créé 26/11/2008 à 02:55
source utilisateur

voix
2

En supposant:

  • Le caractère aléatoire est nécessaire pour l'unicité, et non pour la sécurité
  • Votre user_id est 32 bits
  • Votre limite de 9999999 était juste un exemple

On pourrait faire quelque chose simple que d'avoir le nombre aléatoire en tant que nombre entier de 64 bits, les 32 bits supérieurs contenant l'estampille temporelle (à insert de ligne) et les 32 bits inférieurs du user_id. Ce serait unique, même pour plusieurs lignes avec le même utilisateur, à condition que vous utilisez une résolution appropriée sur votre horodatage selon la façon dont vous ajoutez souvent de nouvelles lignes pour le même utilisateur. Mélanger avec une contrainte unique sur la colonne aléatoire et attraper une telle erreur dans votre logique et puis juste une nouvelle tentative.

Créé 26/11/2008 à 03:00
source utilisateur

voix
1

Il est facile de concevoir un générateur de nombres pseudo - aléatoires avec une longue période de non - répétition; par exemple celui - ci , qui est utilisé pour la même chose que vous voulez pour.

BTW, pourquoi ne pas émettre que la séquence du code d'utilisateur?

Créé 26/11/2008 à 03:02
source utilisateur

voix
0

PHP a déjà une fonction pour cela, uniqid . Il génère un UUID standard qui est très bien si vous devez accéder aux données d'ailleurs. Ne pas réinventer la roue.

Créé 26/11/2008 à 03:06
source utilisateur

voix
6

Vous voulez une solution over-the-top?

Je suppose que le hasard ne vise pas à être un cryptage de qualité, mais juste assez pour deviner la longévité décourager d'un utilisateur, par user_id.

Au cours du développement, générer une liste de tous les 10 millions de numéros sous forme de chaîne.

En option, effectuer une transformation simple, comme l'ajout d'une chaîne constante au milieu. (Ceci est juste au cas où le résultat est trop prévisible.)

Les passer dans un outil qui génère des fonctions de hachage parfaites , telles que gperf .

Le code résultant peut être utilisé pour encoder rapidement id lors de l'exécution de l'utilisateur en une valeur de hachage unique qui est garanti de ne pas entrer en conflit avec d'autres valeurs de hachage.

Créé 26/11/2008 à 03:16
source utilisateur

voix
17

Pourquoi ne pas simplement utiliser un GUID? La plupart des langues doivent avoir un moyen intégré pour le faire. Il est garanti d'être unique (avec des limites très raisonnables).

Créé 26/11/2008 à 03:19
source utilisateur

voix
1

J'aime l'idée de Oddthinking, mais au lieu de choisir la fonction de hachage la plus forte dans le monde, vous pouvez simplement:

  • Générer le MD5 de des 10 premiers millions de chiffres (exprimés sous forme de chaînes, + sel)
  • Vérifiez les doublons en ligne , à savoir avant d' aller dans la production (je suppose qu'il n'y aura pas)
  • Conserver les doublons dans un tableau quelque part
  • Lorsque votre application démarre, charger le tableau
  • Lorsque vous souhaitez insérer une carte d'identité, choisissez le numéro suivant, calculer son MD5, vérifier si elle est dans le tableau, et si elle ne l'utiliser comme l'ID dans la base de données. Sinon, choisissez le numéro suivant

MD5 de rapides, et vérifier si une chaîne appartient à un tableau vous évitera un SELECT.

Créé 26/11/2008 à 03:41
source utilisateur

voix
3

Essayez la déclaration CAST mysql SELECT (RAND () * 1000000 AS INT)

Créé 26/11/2008 à 08:51
source utilisateur

voix
1

Je l' ai fait déjà écrit un article à ce sujet . Il faut la même approche que la réponse de Robert Gould, mais montre en outre comment raccourcir un chiffrement par bloc à une longueur appropriée en utilisant le pliage de XOR, puis comment générer les permutations sur une plage qui ne sont pas une puissance de 2, tout en préservant la propriété d' unicité.

Créé 26/11/2008 à 11:13
source utilisateur

voix
0

Je ne doute pas saisi votre point, mais qu'en est-auto_increments?

Créé 27/11/2008 à 19:11
source utilisateur

voix
1

Si vous voulez vraiment obtenir des chiffres « aléatoires » sous forme de 0 à 9 999 999, la solution est de faire la « randomisation » une fois, puis stocker le résultat sur votre disque.

Ce n'est pas difficile d'obtenir le résultat que vous voulez, mais je pense plus comme « faire une longue liste avec des numéros », que « obtenir un nombre aléatoire ».

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

Vous devez également un pointeur sur la position actuelle du nombre de $ (le stocker dans une base de données); commencer par 0 et incrémenter chaque fois que vous avez besoin d'un nouveau numéro. (Ou vous pouvez utiliser array_shift () ou array_pop (), si vous ne souhaitez utiliser des pointeurs.)

Créé 27/11/2008 à 23:41
source utilisateur

voix
1

Un algorithme approprié PRNG (générateur de nombres pseudo aléatoires) aura un temps de cycle au cours de laquelle il ne sera jamais dans le même état. Si vous exposez tout l'état du PRNG du nombre récupéré de celui-ci, vous obtiendrez un nombre garanti unique pour la période du générateur.

Un PRNG simple qui fait ce qu'on appelle le « linéaire Congruential PRNG » une formule , qui le fait :

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

En utilisant la bonne paire de facteurs, vous pouvez obtenir une période de 2 ^ 30 (environ 1 milliard) d'un PRNG simple avec un accumulateur 32 bits. Notez que vous aurez besoin d'une variable temporaire long long 64 bits pour maintenir la partie « AX » intermédiaire du calcul. La plupart, sinon tous les compilateurs C soutiendront ce type de données. Vous devez également être en mesure de le faire avec un type de données numériques sur la plupart des dialectes SQL.

Avec les bonnes valeurs de A et M, nous pouvons obtenir un générateur de nombres aléatoires avec de bonnes propriétés statistiques et géométriques. Il y a un célèbre sur ce écrit par Fishman et Moore.

M = 2 ^ 31-1 nous obtenons peut utiliser les valeurs de A ci-dessous pour obtenir un PRNG avec une longue période agréable (2 ^ 30 IIRC).

De bonnes valeurs de A:

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

Notez que ce type de générateur est (par définition) ne cryptographiquement sûr. Si vous connaissez le dernier numéro généré par ce que vous pouvez prédire ce qu'il fera ensuite. Malheureusement , je crois que vous ne pouvez pas obtenir la sécurité cryptographique et la non-reproductibilité garantie en même temps. Pour un PRNG être cryptographiquement sûre (par exemple Blum Blum Shub ) il ne peut pas exposer l' état suffisant dans un certain nombre généré pour permettre le numéro suivant dans la séquence à prédire. Par conséquent , l'état interne est plus large que le nombre généré et (afin d'avoir une bonne sécurité) la période sera plus longue que le nombre de valeurs possibles qui peuvent être générés. Cela signifie que le nombre exposé ne sera pas unique dans la période.

Pour des raisons similaires , la même chose est vraie des générateurs de longue période, comme le Mersenne Twister.

Créé 27/11/2008 à 23:59
source utilisateur

voix
1

il y a deux manières d'aller à ce une façon serait de construire un tableau avec les numéros 0000000 par 9999999 puis choisir un choix aléatoire de ces chiffres dans ce tableau et remplacez les valeurs de nombres cueillies à la valeur la plus élevée Max réduire Max par 1 et choisir un autre membre aléatoire de ce tableau à nouveau maximum

chaque fois que la réduction au maximum par une

par exemple (en base): (à droite sont des commentaires qui doivent être supprimés dans le programme réel) Rndfunc est un appel à quelque fonction de générateur de nombres aléatoires que vous utilisez

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

puis continuez à faire cela pour chaque numéro que vous souhaitez choisir, mais vous aurez besoin d'avoir la possibilité d'utiliser des tableaux très grands

l'autre méthode serait la suivante: générer un nombre et la stocker dans un tableau qui peut se développer de façon dynamique puis après que choisir un nouveau numéro et la comparer à la valeur qui est à mi-chemin du premier au dernier élément du tableau dans ce cas il serait le premier numéro choisi si elle correspond à choisir un autre nombre aléatoire, trier le tableau selon la taille et s'il n'y a pas un match en fonction puis sur le temps, il est plus ou moins que le numéro que vous avez comparé avec vous allez vers le haut ou vers le bas dans la moitié de la liste de moitié de la distance, chaque fois qu'il ne correspond pas à et est plus ou moins que ce que vous comparez à.

chaque fois que réduire de moitié jusqu'à atteindre une taille de l'espace d'un puis de vérifier une fois et d'arrêter car il n'y a pas de correspondance, puis le numéro est ajouté à la liste et la liste est procédé à un remaniement dans l'ordre croissant, ainsi de suite et ainsi de suite jusqu'à ce que vous êtes fait la cueillette des nombres aléatoires ... espérons que cette aide ..

Créé 27/01/2012 à 14:05
source utilisateur

voix
0

Si vous voulez vous assurer que les nombres aléatoires ne sont pas répéter, vous avez besoin d' un générateur de nombres aléatoires non-répétition (comme décrit ici ).

L'idée de base est que la formule suivante seed * seed & psera produit des nombres aléatoires non-répétition pour toute entrée x such that 2x < pet p - x * x % pproduit tous les autres nombres aléatoires aswell non-répétition, mais seulement si p = 3 mod 4. Donc , fondamentalement tout ce que vous avez besoin est un primnumber aussi près 9999999que possible. De cette façon , l'effort peut être réduit à un champ en lecture seule, mais avec l'inconvénient que soit ID trop gros sont générés ou trop peu ID seront générés.

Cet algorithme ne permute pas très bien, donc je vous recommande la combinaison avec soit XOR ou plus ou d'une autre approche pour changer la valeur exacte sans détruire le 1 à 1 relation entre les semences et leur valeur générée.

Créé 04/10/2015 à 22:49
source utilisateur

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