Как создать список случайных чисел без дубликатов?

Я попытался использовать random.randint(0, 100), но некоторые цифры были одинаковыми. Есть ли метод/модуль для создания списка уникальных случайных чисел?

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);

Ответ 1

Это вернет список из 10 номеров, выбранных из диапазона от 0 до 99, без дубликатов.

import random
random.sample(range(100), 10)

Что касается вашего конкретного примера кода, вы, вероятно, захотите прочитать все строки из файла один раз, а затем выбрать случайные строки из сохраненного списка в памяти. Например:

all_lines = f1.readlines()
for i in range(50):
    lines = random.sample(all_lines, 40)

Таким образом, вам нужно только прочитать файл из файла один раз, до вашего цикла. Это гораздо эффективнее, чем для поиска в начале файла и f1.readlines() вызова f1.readlines() для каждой итерации цикла.

Ответ 2

Сначала вы можете создать список чисел от a до b, где a и b являются соответственно наименьшими и наибольшими числами в вашем списке, затем перетасуйте его алгоритмом Fisher-Yates или используя метод Python random.shuffle.

Ответ 3

Решение, представленное в этом ответе, работает, но может быть проблематично память, если размер выборки мал, но население огромно (например, random.sample(insanelyLargeNumber, 10)).

Чтобы исправить это, я бы пошел с этим:

answer = set()
sampleSize = 10
answerSize = 0

while answerSize < sampleSize:
    r = random.randint(0,100)
    if r not in answer:
        answerSize += 1
        answer.add(r)

# answer now contains 10 unique, random integers from 0.. 100

Ответ 4

Вы можете использовать функцию тасования из случайного модуля следующим образом:

import random

my_list = list(xrange(1,100)) # list of integers from 1 to 99
                              # adjust this boundaries to fit your needs
random.shuffle(my_list)
print my_list # <- List of unique random numbers

Обратите внимание, что метод shuffle не возвращает какой-либо список, как можно было бы ожидать, он только перетасовывает список, переданный по ссылке.

Ответ 5

Итак, я понимаю, что этой должности 6 лет, но есть еще один ответ с (как правило) лучшим алгоритмическим исполнением, хотя и менее практичным с большими накладными расходами.

Другие ответы включают метод тасования и метод "попробуй до действительного" с использованием наборов.

Если мы произвольно выбираем K целых чисел без замены из интервала 0... n- 1, то метод тасования использует операции O (N) и O (N), что раздражает, если мы выбираем небольшой K из больших N. Метод set использует только память O (K), но имеет наихудший случай O (inf), ожидаемый O (n * log (n)) для K, близкого к N. (Представьте, что вы пытаетесь случайно получить последний номер из двух разрешенных ответы, уже отобранные 999998, для k = n- 1 = 10 ^ 6).

Таким образом, метод set отлично подходит для K ~ 1, и метод shuffle отлично подходит для K ~ N. Оба используют ожидаемые вызовы> K RNG.

По-другому; вы можете притворяться, что нужно перетасовать Fisher-Yates, и для каждого нового случайного выбора выполните операцию бинарного поиска на уже выбранных элементах, чтобы найти значение, которое вы получите, если бы вы фактически хранили массив всех элементов, которые вы haven еще не выбран.

Если ваши уже выбранные значения равны [2,4], а ваш генератор случайных чисел выплескивает 2 в интервале (N - num_already_selected), вы притворяетесь, что выбираете из [0,1,3,5,6,...] путем подсчета значений, меньших, чем уже выбранный ответ. В этом случае ваше третье выбранное значение будет равно 3. Затем на следующем шаге, если ваше случайное число равно 2, оно будет отображаться в 5 (в притворном списке [0,1,5,6]), поскольку (потенциальный индекс 5 в отсортированном списке уже выбранных значений [2,3,4], который равен 3) + 2 = 5.

Поэтому сохраните уже выбранные значения в сбалансированном двоичном дереве поиска, сохраните ранг (количество значений меньше этого значения) на каждом узле, выберите случайное число R из диапазона (0... n- (уже выбранное число )). Затем спуститесь к дереву, как если бы искали, но ваше значение поиска - R плюс ранг любого узла, на котором вы находитесь. Когда вы достигаете листового узла, добавьте случайное число в ранг этого узла и вставьте сумму в сбалансированное двоичное дерево.

Когда у вас есть K-элементы, прочитайте их с дерева в массив и перетасуйте (если порядок важен).

Это занимает память O (K), производительность O (K * log (K)) и точно вызовы K randint.

Пример реализации случайной выборки (нет n- случайного окончательного упорядочения, но вы можете перетасовать O (K) после), O (k) и O (klog ^ 2 (k)) (не O (klog (k)), потому что мы не можем настраивать балансированные бинарные деревья для этой реализации):

from sortedcontainers import SortedList


def sample(n, k):
    '''
    Return random k-length-subset of integers from 0 to n-1. Uses only O(k) 
    storage. Bounded k*log^2(k) worst case. K RNG calls. 
    '''
    ret = SortedList()
    for i in range(k):
        to_insert = random.randint(0, n-1 - len(ret))
        to_insert = binsearch_adding_rank(ret, to_insert)
        ret.add(to_insert)

    return ret

def binsearch_adding_rank(A, v):
    l, u = 0, len(A)-1
    m=0
    while l <= u:
        m = l+(u-l)//2
        if v + m >= A[m]:
            l = m+1
            m+=1 # We're binary searching for partitions, so if the last step was to the right then add one to account for offset because that where our insert would be.
        elif v+m < A[m]:
            u = m-1
    return v+m

И чтобы показать достоверность:

Если бы мы делали перетасовку рыбаков-ятов, уже выбрав [1,4,6,7,8,9,15,16], со случайным числом 5, наш еще не выбранный массив выглядел бы как [0, 2,3,5,10,11,12,...], поэтому элемент 5 равен 11. Таким образом, наша функция поиска бинов должна возвращать 11, учитывая 5 и [1,4,6,7,8,9,15, 16]:

assert binsearch_adding_rank([1,4,6,7,8,9,15,16], 5) == 11

Обратный [1,2,3] - [0,4,5,6,7,8,...], 5-й элемент которого равен 8, поэтому:

assert binsearch_adding_rank([1,2,3], 5) == 8

Обратный [2,3,5] равен [0,1,4,6,...], 1-й элемент которого (еще) 1, поэтому:

assert binsearch_adding_rank([2,3,5], 1) == 1

Обратный - [0,6,7,8,...], третий элемент - 8 и:

assert binsearch_adding_rank([1,2,3,4,5,10], 3) == 8

И для проверки общей функции:

# Edge cases: 
assert sample(50, 0) == []
assert sample(50, 50) == list(range(0,50))

# Variance should be small and equal among possible values:
x = [0]*10
for i in range(10_000):
    for v in sample(10, 5):
        x[v] += 1
for v in x:
    assert abs(5_000 - v) < 250, v
del x

# Check for duplication: 

y = sample(1500, 1000)
assert len(frozenset(y)) == len(y)
del y

На практике, однако, используйте метод тасования для K ~> N/2 и установленный метод для K ~ <N/2.

edit: Здесь другой способ сделать это с помощью рекурсии! O (k * log (n)) Я думаю.

def divide_and_conquer_sample(n, k, l=0):
    u = n-1
    # Base cases:
    if k == 0:
        return []
    elif k == n-l:
        return list(range(l, n))
    elif k == 1:
        return [random.randint(l, u)]

    # Compute how many left and how many right:
    m = l + (u-l)//2
    k_right = 0
    k_left = 0
    for i in range(k):
        # Base probability: (# of available values in right interval) / (total available values)
        if random.random() <= (n-m - k_right)/(n-l-k_right-k_left):
            k_right += 1
        else:
            k_left += 1
    # Recur
    return divide_and_conquer_sample(n, k_right, m) + divide_and_conquer_sample(m, k_left, l)

Ответ 6

Если список N чисел от 1 до N генерируется случайным образом, то да, есть вероятность, что некоторые числа могут быть повторены.

Если вам нужен список чисел от 1 до N в случайном порядке, заполните массив целыми числами от 1 до N, а затем используйте Shuffle Fisher-Yates или Python random.shuffle().

Ответ 7

Если вам нужно пробовать очень большие числа, вы не можете использовать range

random.sample(range(10000000000000000000000000000000), 10)

потому что он бросает:

OverflowError: Python int too large to convert to C ssize_t

Кроме того, если random.sample не может указать количество элементов, которые вы хотите из-за слишком малого диапазона

 random.sample(range(2), 1000)

он бросает:

 ValueError: Sample larger than population

Эта функция устраняет обе проблемы:

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))))]

Использование с чрезвычайно большими номерами:

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

Пример результата:

7822019936001013053229712669368
6289033704329783896566642145909
2473484300603494430244265004275
5842266362922067540967510912174
6775107889200427514968714189847
9674137095837778645652621150351
9969632214348349234653730196586
1397846105816635294077965449171
3911263633583030536971422042360
9864578596169364050929858013943

Использование, где диапазон меньше количества запрошенных элементов:

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

Пример результата:

2, 0, 1

Он также работает с отрицательными диапазонами и шагами:

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

Примеры результатов:

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

Ответ 8

Генератор линейных конгруэнтных псевдослучайных чисел

O (1) Память

O (k) Операции

Эта проблема может быть решена с помощью простого линейного конгруэнтного генератора. Это требует постоянных затрат памяти (8 целых) и не более 2 * (длина последовательности) вычислений.

Все другие решения используют больше памяти и больше вычислений! Если вам нужно всего несколько случайных последовательностей, этот метод будет значительно дешевле. Для последовательностей длиной k, если вы хотите сгенерировать порядка k уникальных последовательностей или более, я рекомендую принятое решение с использованием random.sample(range(N),k) поскольку этот встроенный метод был оптимизирован в python для скорости.

Код

# Return a randomized "range" using a Linear Congruential Generator
# to produce the number sequence. Parameters are the same as for 
# python builtin "range".
#   Memory  -- storage for 8 integers, regardless of parameters.
#   Compute -- at most 2*"maximum" steps required to generate sequence.
#
def random_range(start, stop=None, step=None):
    import random, math
    # Set a default values the same way "range" does.
    if (stop == None): start, stop = 0, start
    if (step == None): step = 1
    # Use a mapping to convert a standard range into the desired range.
    mapping = lambda i: (i*step) + start
    # Compute the number of numbers in this range.
    maximum = (stop - start) // step
    # Seed range with a random integer.
    value = random.randint(0,maximum)
    # 
    # Construct an offset, multiplier, and modulus for a linear
    # congruential generator. These generators are cyclic and
    # non-repeating when they maintain the properties:
    # 
    #   1) "modulus" and "offset" are relatively prime.
    #   2) ["multiplier" - 1] is divisible by all prime factors of "modulus".
    #   3) ["multiplier" - 1] is divisible by 4 if "modulus" is divisible by 4.
    # 
    offset = random.randint(0,maximum) * 2 + 1      # Pick a random odd-valued offset.
    multiplier = 4*(maximum//4) + 1                 # Pick a multiplier 1 greater than a multiple of 4.
    modulus = int(2**math.ceil(math.log2(maximum))) # Pick a modulus just big enough to generate all numbers (power of 2).
    # Track how many random numbers have been returned.
    found = 0
    while found < maximum:
        # If this is a valid value, yield it in generator fashion.
        if value < maximum:
            found += 1
            yield mapping(value)
        # Calculate the next value in the sequence.
        value = (value*multiplier + offset) % modulus

использование

Использование этой функции "random_range" такое же, как и для любого генератора (например, "range"). Пример:

# Show off random range.
print()
for v in range(3,6):
    v = 2**v
    l = list(random_range(v))
    print("Need",v,"found",len(set(l)),"(min,max)",(min(l),max(l)))
    print("",l)
    print()

Пример результатов

Required 8 cycles to generate a sequence of 8 values.
Need 8 found 8 (min,max) (0, 7)
 [1, 0, 7, 6, 5, 4, 3, 2]

Required 16 cycles to generate a sequence of 9 values.
Need 9 found 9 (min,max) (0, 8)
 [3, 5, 8, 7, 2, 6, 0, 1, 4]

Required 16 cycles to generate a sequence of 16 values.
Need 16 found 16 (min,max) (0, 15)
 [5, 14, 11, 8, 3, 2, 13, 1, 0, 6, 9, 4, 7, 12, 10, 15]

Required 32 cycles to generate a sequence of 17 values.
Need 17 found 17 (min,max) (0, 16)
 [12, 6, 16, 15, 10, 3, 14, 5, 11, 13, 0, 1, 4, 8, 7, 2, ...]

Required 32 cycles to generate a sequence of 32 values.
Need 32 found 32 (min,max) (0, 31)
 [19, 15, 1, 6, 10, 7, 0, 28, 23, 24, 31, 17, 22, 20, 9, 27, ...]

Required 64 cycles to generate a sequence of 33 values.
Need 33 found 33 (min,max) (0, 32)
 [11, 13, 0, 8, 2, 9, 27, 6, 29, 16, 15, 10, 3, 14, 5, 24, ...]

Ответ 9

Если вы хотите убедиться, что добавляемые числа уникальны, вы можете использовать Установить объект

если используется 2,7 или более, или импортируйте модуль sets, если нет.

Как отмечали другие, это означает, что цифры не являются действительно случайными.

Ответ 10

Для быстрого ответа вы можете использовать библиотеку Numpy, как показано ниже -

Данный фрагмент кода содержит список из 6 уникальных между диапазоном от 0 до 5. Вы можете настроить параметры для вашего комфорта.

import numpy as np
import random
a = np.linspace( 0, 5, 6 )
random.shuffle(a)
print(a)

Выход

[ 2.  1.  5.  3.  4.  0.]

Он не устанавливает никаких ограничений, как мы видим в random.sample, как указано здесь.

Надеюсь, это немного поможет.

Ответ 11

Ответ, представленный здесь, работает очень хорошо в отношении времени, а также памяти, но немного сложнее, поскольку он использует сложные конструкции Python, такие как yield. Более простой ответ хорошо работает на практике, но проблема с этим ответом состоит в том, что он может генерировать много ложных целых чисел перед тем, как на самом деле создать требуемый набор. Попробуйте это с PopSystemSize = 1000, sampleSize = 999. Теоретически, есть вероятность, что он не прекратится.

Ответ ниже касается обеих проблем, поскольку он является детерминированным и в некоторой степени эффективным, хотя в настоящее время не так эффективен, как другие два.

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

где функции getElem, percolateUp определены ниже

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

Наконец, время в среднем составляло около 15 мс для большого значения n, как показано ниже,

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]

Ответ 12

Очень простая функция, которая также решает вашу проблему

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]

"""

Ответ 13

Из CLI в win xp:

python -c "import random; print(sorted(set([random.randint(6,49) for i in range(7)]))[:6])"

В Канаде у нас есть лото 6/49. Я просто завершаю вышеуказанный код в lotto.bat и запускаю C:\home\lotto.bat или просто C:\home\lotto.

Поскольку random.randint часто повторяет число, я использую set с range(7), а затем сокращаю его до 6.

Иногда, если число повторяется более чем в 2 раза, результирующая длина списка будет меньше 6.

EDIT: Однако random.sample(range(6,49),6) - правильный способ.

Ответ 14

import random
result=[]
for i in range(1,50):
    rng=random.randint(1,20)
    result.append(rng)