2015-05-27 4 views
2

Я хотел бы создать список из x случайных целых чисел, выбранных из интервала [0, n [(n обычно намного больше x), в результате чего некоторые числа этот интервал следует игнорировать. Я реализовал, что следующим образом:Python: создать список случайных целых чисел, где пространство состояний ограничено

from random import randint 

def createRandomList(n, x, ignore=[]): 
    myList = [] 
    while len(myList) < x: 
     tempr = randint(0,n-1) 
     if tempr not in ignore: 
      myList.append(tempr) 
    return myList 

Когда я затем вызвать

l = createRandomList(5,2,ignore=[2,3]) 

я получить, например,

l = [1,4] #2 and 3 should not appear 

или

l = [0,1] 

или

l = [4,4] 

или ...

То есть желаемый результат, однако, там быстрее/более компактный способ сделать это ?

EDIT: Все эти решения работают нормально, поэтому мне пришлось выполнить некоторые сравнения скорости, чтобы решить, какой из них принять. Оказывается - не удивительно, - что генерировать все допустимые значения заранее, а затем выбирать из них, очень неэффективно при больших значениях n, а while-loops легко выигрывают. Поэтому я принял hgwells ответ, так как его версия не только быстрее, чем мой while-loop, но и потребляет меньше памяти.

Большое спасибо за ответы; Я мог бы многому научиться у всех из них!

+1

Если вам не нужен весь список сразу, вы можете использовать генераторы. https://docs.python.org/2/howto/functional.html –

+2

Это может сделать это немного быстрее, если 'ignore' был' set', а не 'list'. – Moshe

+0

@RobertJacobs: Спасибо за предложение, я прочитаю об этом. – Cleb

ответ

1

Здесь находится генератор. Но я действительно не знаю, насколько это улучшит ваше решение

from random import randint 

def randGen(n, x, ignore=[]): 
    index = 0 
    while index < x: 
     temp = randint(0, n-1) 
     if temp not in ignore: 
      index += 1 
      # yield the temp value and wait for 
      # the next call 
      yield temp 

# you could now create your list 
# myList = [i for i in randGen(5, 2, [2,3])] 
# or as Mark pointed out 
myList = list(randGen(5,2,[2,3])) 
print(myList) 

# or use the generator items when you need them 
for i in randGen(5, 2, [2,3]): 
    # do something with i 
    print(i) 
+1

Я продолжаю забывать, что вы можете делать 'list (...)' вместо '[i for i in ...]'. –

+0

Спасибо за предоставление такого примера. Я буду отвечать и решать позже, на что ответить, потому что мне нравятся все три из них;) – Cleb

1

В зависимости от значений n, x и ignore, это могло бы быть более эффективным, чтобы создать список всех допустимых значений и использовать повторные вызовы random.choice(), чтобы создать свой список.

Например, один (хотя и медленно) реализация будет:

def createRandomList(n, x, ignore=[]): 
    srcList = [i for i in range(n) if i not in ignore] 
    destList = [random.choice(srcList) for i in range(x)] 
    return destList 
1
from random import randint 

def createRandomList(n, x, ignore=[]): 
    available_numbers = [elem for elem in range(n) if elem not in ignore] 
    myList = [available_numbers[randint(0, len(available_numbers) - 1)] for _ in range(x)] 
    return myList 

В этом способе сначала создать список чисел от 0 до N-1, без чисел игнорировать. После этого вы выбрали x номеров из этого списка.

+0

Это прекрасно работает. В качестве альтернативы можно заменить доступные_значения [randint (0, len (available_numbers) - 1) по выбору (available_numbers). Не уверен, что лучше. Я подожду, согласившись на этот ответ, некоторое время, чтобы узнать, добавляет ли кто-нибудь сообщение на основе генератора, упомянутое в комментариях выше. – Cleb

1

подход itertools основанного генератор:

from itertools import count, islice, ifilterfalse # just 'filterfalse' in Py3 
from random import randint 
def random_list(limit, size, ignore={}): # ignore should be a set for O(1) lookups 
    ints = (randint(0, limit-1) for _ in count()) # generate randints in range 
    filtered = ifilterfalse(ignore.__contains__, ints) # filter out the rejects 
    for i in islice(filtered, size): # limit the size to what we want and yield 
     yield i 
    # in Python 3 you don't need the for-loop, simply: 
    # yield from islice(filtered, size) 

print list(random_list(5, 2, {2, 3}) 
# [1, 4] 

Это может быть просто уложено в однострочник, но нарушение его делает лучше читаемость:

l = list(islice(ifilterfalse({2, 3}.__contains__, (randint(0, 4) for _ in count())), 2)) 
+0

Это очень поучительный пример, спасибо за это! На данный момент я пока переставлю его и решит позже, какой ответ принять. – Cleb

Смежные вопросы