198 votes

Comment créer une liste de nombres aléatoires sans doublons ?

J'ai essayé d'utiliser random.randint(0, 100) mais certains numéros sont les mêmes. Existe-t-il une méthode/module pour créer une liste de nombres aléatoires uniques ?

Note : Le code suivant est sur la base d'une réponse et a été ajouté après que la réponse ait été postée. Il ne fait pas partie de la question ; il constitue la solution.

def getScores():
    # open files to read and write
    f1 = open("page.txt", "r");
    p1 = open("pgRes.txt", "a");

    gScores = [];
    bScores = [];
    yScores = [];

    # run 50 tests of 40 random queries to implement "bootstrapping" method 
    for i in range(50):
        # get 40 random queries from the 50
        lines = random.sample(f1.readlines(), 40);

2 votes

S'ils sont uniques, ils peuvent être véritablement aléatoires dans le bon contexte. Comme un échantillon aléatoire d'index sans remplacement peut encore être complètement aléatoire.

5voto

Mitch Wheat Points 169614

Si la liste de N nombres de 1 à N est générée de manière aléatoire, alors oui, il est possible que certains nombres soient répétés.

Si vous souhaitez obtenir une liste de nombres de 1 à N dans un ordre aléatoire, remplissez un tableau avec des nombres entiers de 1 à N, puis utilisez une commande de type Le remaniement Fisher-Yates ou de Python random.shuffle() .

5voto

Handcraftsman Points 3166

Si vous avez besoin d'échantillonner des nombres extrêmement importants, vous ne pouvez pas utiliser range

random.sample(range(10000000000000000000000000000000), 10)

parce qu'il jette :

OverflowError: Python int too large to convert to C ssize_t

En outre, si random.sample ne peut pas produire le nombre d'articles que vous souhaitez parce que la gamme est trop petite

 random.sample(range(2), 1000)

il jette :

 ValueError: Sample larger than population

Cette fonction résout les deux problèmes :

import random

def random_sample(count, start, stop, step=1):
    def gen_random():
        while True:
            yield random.randrange(start, stop, step)

    def gen_n_unique(source, n):
        seen = set()
        seenadd = seen.add
        for i in (i for i in source() if i not in seen and not seenadd(i)):
            yield i
            if len(seen) == n:
                break

    return [i for i in gen_n_unique(gen_random,
                                    min(count, int(abs(stop - start) / abs(step))))]

Utilisation avec des nombres extrêmement grands :

print('\n'.join(map(str, random_sample(10, 2, 10000000000000000000000000000000))))

Résultat de l'échantillon :

7822019936001013053229712669368
6289033704329783896566642145909
2473484300603494430244265004275
5842266362922067540967510912174
6775107889200427514968714189847
9674137095837778645652621150351
9969632214348349234653730196586
1397846105816635294077965449171
3911263633583030536971422042360
9864578596169364050929858013943

Utilisation lorsque la plage est inférieure au nombre d'éléments demandés :

print(', '.join(map(str, random_sample(100000, 0, 3))))

Résultat de l'échantillon :

2, 0, 1

Il fonctionne également avec des plages et des étapes négatives :

print(', '.join(map(str, random_sample(10, 10, -10, -2))))
print(', '.join(map(str, random_sample(10, 5, -5, -2))))

Résultats de l'échantillon :

2, -8, 6, -2, -4, 0, 4, 10, -6, 8
-3, 1, 5, -1, 3

0 votes

Et si vous générez plus de 8 milliards de chiffres, tôt ou tard, vu deviendra trop gros

0 votes

Cette réponse présente un grave défaut pour les grands échantillons. La probabilité de collision croît linéairement à chaque étape. J'ai publié une solution utilisant un générateur contructif linéaire qui a une surcharge de mémoire de O(1) et O(k) étapes nécessaires pour générer k nombres. Cette solution peut être résolue de manière beaucoup plus efficace !

0 votes

Cette réponse est définitivement meilleure si vous voulez générer un nombre de séquences aléatoires de l'ordre de la longueur de la séquence ! La méthode LCG est moins "aléatoire" lorsqu'il s'agit de générer plusieurs séquences uniques.

4voto

Sheva Kadu Points 43

Voici une toute petite fonction que j'ai réalisée, j'espère que cela vous aidera !

import random
numbers = list(range(0, 100))
random.shuffle(numbers)

2voto

Vinicius Torino Points 21

Une fonction très simple qui résout également votre problème

from random import randint

data = []

def unique_rand(inicial, limit, total):

        data = []

        i = 0

        while i < total:
            number = randint(inicial, limit)
            if number not in data:
                data.append(number)
                i += 1

        return data

data = unique_rand(1, 60, 6)

print(data)

"""

prints something like 

[34, 45, 2, 36, 25, 32]

"""

1voto

aak318 Points 139

La réponse fournie aquí fonctionne très bien en ce qui concerne le temps ainsi que la mémoire, mais il est un peu plus compliqué car il utilise des constructions python comme le yield. Le site réponse plus simple fonctionne bien dans la pratique, mais le problème avec cette réponse est qu'elle peut générer de nombreux nombres entiers erronés avant de construire réellement l'ensemble requis. Essayez-le avec PopulationSize = 1000, SampleSize = 999. En théorie, il y a une chance qu'elle ne se termine pas.

La réponse ci-dessous résout les deux problèmes, car elle est déterministe et quelque peu efficace. bien qu'actuellement elle ne soit pas aussi efficace que les deux autres.

def randomSample(populationSize, sampleSize):
  populationStr = str(populationSize)
  dTree, samples = {}, []
  for i in range(sampleSize):
    val, dTree = getElem(populationStr, dTree, '')
    samples.append(int(val))
  return samples, dTree

où les fonctions getElem, percolateUp sont définies ci-dessous

import random

def getElem(populationStr, dTree, key):
  msd  = int(populationStr[0])
  if not key in dTree.keys():
    dTree[key] = range(msd + 1)
  idx = random.randint(0, len(dTree[key]) - 1)
  key = key +  str(dTree[key][idx])
  if len(populationStr) == 1:
    dTree[key[:-1]].pop(idx)
    return key, (percolateUp(dTree, key[:-1]))
  newPopulation = populationStr[1:]
  if int(key[-1]) != msd:
    newPopulation = str(10**(len(newPopulation)) - 1)
  return getElem(newPopulation, dTree, key)

def percolateUp(dTree, key):
  while (dTree[key] == []):
    dTree[key[:-1]].remove( int(key[-1]) )
    key = key[:-1]
  return dTree

Enfin, le temps moyen était d'environ 15 ms pour une grande valeur de n, comme le montre le graphique ci-dessous,

In [3]: n = 10000000000000000000000000000000

In [4]: %time l,t = randomSample(n, 5)
Wall time: 15 ms

In [5]: l
Out[5]:
[10000000000000000000000000000000L,
 5731058186417515132221063394952L,
 85813091721736310254927217189L,
 6349042316505875821781301073204L,
 2356846126709988590164624736328L]

0 votes

Vous pensez que la réponse est compliquée ? C'est quoi alors ? Et puis il y a la autre réponse qui génère de nombreux "faux entiers". J'ai exécuté votre implémentation avec l'exemple d'entrée que vous avez donné (populationSize = 1000, sampleSize = 999). Votre version appelle la fonction random.randint 3996 fois, alors que l'autre l'a fait environ 6000 fois. Ce n'est pas une si grande amélioration que ça, hein ?

0 votes

@kyrill, votre point de vue sur cette réponse

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X