Как создать список номеров без дубликатов?


Я пробовал использовать 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);
11 52

11 ответов:

это вернет список из 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() раз для каждой итерации цикла.

вы можете сначала создать список чисел из a до b, где a и b соответственно самые маленькие и самые большие числа в вашем списке, а затем перемешать его с Фишер-Йейтс алгоритм или использование питона random.shuffle метод.

решение представлено в ответ работает, но это может стать проблематичным с памятью, если размер выборки невелик, но население огромно (например 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

можно использовать перетасовка С случайные модуль такой:

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 не возвращает никакого списка, как можно было бы ожидать, он только перемешивает список, переданный по ссылке.

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

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

Если вам нужно пробовать очень большие числа, вы не можете использовать 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

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

другие ответы включают метод shuffle и метод "try until valid" с использованием наборов.

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

значит установить способ нормально для K~1, и способ перетасовать нормально для K~Н. Как ожидается, использовать >к ГСЧ звонки.

другой способ; вы можете претендует чтобы сделать Фишер-Йейтс перемешать, и для каждого нового случайного выбора, выполните бинарная операция поиска по уже выбранным элементам, чтобы найти значение, которое вы б вам если вы на самом деле хранить массив всех элементов, которые вы еще не выбрали.

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

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

как только у вас есть k элементов, считайте их с дерева в массив и перемешайте (если порядок важен).

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

пример реализация случайной выборки (неслучайный окончательный порядок, но вы можете 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's 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. Таким образом, наша binsearch-функция должна возвращать 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,...], 3-й элемент - 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 ~

правка: вот еще один способ сделать это с помощью рекурсии! 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)

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

Если используется 2.7 или выше, или импортировать модуль наборов, если нет.

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

из CLI в win xp:

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

в Канаде у нас есть лото 649. Я просто обернуть выше код в лото.летучая мышь и бежать C:\home\lotto.bat или просто C:\home\lotto.

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

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

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

можно использовать включает в себя библиотеки для быстрого ответа, как показано ниже

данный фрагмент кода перечисляет вниз 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.]

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

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

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