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


1758

Припустимо, у мене є такий список:

foo = ['a', 'b', 'c', 'd', 'e']

Який найпростіший спосіб отримати об'єкт навмання зі цього списку?

Відповіді:


2676

Використовуйте random.choice()

import random

foo = ['a', 'b', 'c', 'd', 'e']
print(random.choice(foo))

Для криптографічно безпечних випадкових виборів (наприклад, для створення парольної фрази зі списку слів) використовуйтеsecrets.choice()

import secrets

foo = ['battery', 'correct', 'horse', 'staple']
print(secrets.choice(foo))

secretsє новим у Python 3.6, у старих версіях Python ви можете використовувати random.SystemRandomклас:

import random

secure_random = random.SystemRandom()
print(secure_random.choice(foo))

3
Чи робить два послідовних дзвінки random.choice(foo)повернення два різні результати?
Едуардо Піньятеллі

34
@EduardoPignatelli Кожен вибір є випадковим, тому він може повернути два різні результати, але залежно від початкового насіння це не гарантується. Якщо ви хочете вибрати n різних випадкових елементів зі списку lst , скористайтесяrandom.sample(lst, n)
Грем

6
на відповідну записку, Standard pseudo-random generators are not suitable for security/cryptographic purposes. ref
Xiao

184

Якщо ви хочете випадковим чином вибрати декілька елементів зі списку або вибрати елемент із набору, я рекомендую використовувати його random.sampleзамість.

import random
group_of_items = {1, 2, 3, 4}               # a sequence or set will work here.
num_to_select = 2                           # set the number to select here.
list_of_random_items = random.sample(group_of_items, num_to_select)
first_random_item = list_of_random_items[0]
second_random_item = list_of_random_items[1] 

Якщо ви витягуєте лише один елемент зі списку, вибір є менш незграбним, оскільки використання зразка матиме синтаксис random.sample(some_list, 1)[0]замість random.choice(some_list).

На жаль, вибір працює лише для одного виходу з послідовностей (таких як списки або кортежі). Хоча це random.choice(tuple(some_set))може бути варіант отримання одного предмета з набору.

EDIT: Використання секретів

Як багато хто зазначав, якщо вам потрібні більш безпечні псевдовипадкові зразки, вам слід використовувати модуль секретів:

import secrets                              # imports secure module.
secure_random = secrets.SystemRandom()      # creates a secure random object.
group_of_items = {1, 2, 3, 4}               # a sequence or set will work here.
num_to_select = 2                           # set the number to select here.
list_of_random_items = secure_random.sample(group_of_items, num_to_select)
first_random_item = list_of_random_items[0]
second_random_item = list_of_random_items[1]

EDIT: Пітонічний однолінійний

Якщо ви хочете отримати більше пітонічного однопластового для вибору декількох предметів, ви можете використовувати розпакування.

import random
first_random_item, second_random_item = random.sample(group_of_items, 2)

1
secretsМодуль BTW був доданий до стандартної бібліотеки Python у версії 3.6 python.org/dev/peps/pep-0506
and1er

160

Якщо вам також потрібен індекс, використовуйте random.randrange

from random import randrange
random_index = randrange(len(foo))
print(foo[random_index])

42

Станом на Python 3.6, ви можете використовувати secretsмодуль, який є кращим перед randomмодулем для криптографії або використання безпеки.

Щоб надрукувати випадковий елемент зі списку:

import secrets
foo = ['a', 'b', 'c', 'd', 'e']
print(secrets.choice(foo))

Щоб надрукувати випадковий індекс:

print(secrets.randbelow(len(foo)))

Детальніше див. PEP 506 .


33

Я пропоную сценарій для видалення випадково підібраних предметів зі списку, поки він не буде порожнім:

Підтримуйте a setі видаляйте випадково вибраний елемент (з choice), поки список не буде порожнім.

s=set(range(1,6))
import random

while len(s)>0:
  s.remove(random.choice(list(s)))
  print(s)

Три пробіги дають три різні відповіді:

>>> 
set([1, 3, 4, 5])
set([3, 4, 5])
set([3, 4])
set([4])
set([])
>>> 
set([1, 2, 3, 5])
set([2, 3, 5])
set([2, 3])
set([2])
set([])

>>> 
set([1, 2, 3, 5])
set([1, 2, 3])
set([1, 2])
set([1])
set([])

20
Або ви могли б просто раз і або ітерація його або виштовхнути його для отримання результатів. Це може призвести до абсолютно адекватного потоку "вибір випадковим чином без повторів", просто випадковість буде введена на початку. random.shufflelist
ShadowRanger

2
Теоретично ви можете використовувати метод pop () набору для видалення довільного елемента з набору та повернення його, але це, ймовірно, недостатньо випадково.
Жубарк

14
foo = ['a', 'b', 'c', 'd', 'e']
number_of_samples = 1

У python 2:

random_items = random.sample(population=foo, k=number_of_samples)

У пітоні 3:

random_items = random.choices(population=foo, k=number_of_samples)

6
Зверніть увагу, що random.choicesце заміна, поки random.sampleвона без заміни.
CentAu

1
Також зауважте, що random.choices доступний з 3.6 і пізніше, не раніше!
Кирило Н.

11

numpy рішення: numpy.random.choice

Для цього питання він працює так само, як прийнятий відповідь ( import random; random.choice()), але я додав його, оскільки програміст вже імпортував numpy(як я), а також є деякі відмінності між двома методами, які можуть стосуватися вашого фактичного випадку використання.

import numpy as np    
np.random.choice(foo) # randomly selects a single item

Для відтворюваності ви можете:

np.random.seed(123)
np.random.choice(foo) # first call will always return 'c'

Для зразків одного або декількох елементів , повернутих як array, передайте sizeаргумент:

np.random.choice(foo, 5)          # sample with replacement (default)
np.random.choice(foo, 5, False)   # sample without replacement

9

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

Припустимо, у мене є такий список:

foo = ['a', 'b', 'c', 'd', 'e']  

Який найпростіший спосіб отримати об'єкт навмання зі цього списку?

Якщо ви хочете, щоб вони були справді випадковими , то я пропоную secrets.choiceзі стандартної бібліотеки (Нове в Python 3.6.):

>>> from secrets import choice         # Python 3 only
>>> choice(list('abcde'))
'c'

Сказане вище еквівалентно моїй попередній рекомендації, використовуючи SystemRandomоб'єкт з randomмодуля choiceметодом - доступним раніше в Python 2:

>>> import random                      # Python 2 compatible
>>> sr = random.SystemRandom()
>>> foo = list('abcde')
>>> foo
['a', 'b', 'c', 'd', 'e']

І зараз:

>>> sr.choice(foo)
'd'
>>> sr.choice(foo)
'e'
>>> sr.choice(foo)
'a'
>>> sr.choice(foo)
'b'
>>> sr.choice(foo)
'a'
>>> sr.choice(foo)
'c'
>>> sr.choice(foo)
'c'

Якщо ви хочете детермінований псевдослучайний вибір, використовуйте choiceфункцію (яка насправді пов'язаний метод на Randomоб'єкт):

>>> random.choice
<bound method Random.choice of <random.Random object at 0x800c1034>>

Це здається випадковим, але насправді це не так, що ми можемо побачити, чи повторно його перезавантажували:

>>> random.seed(42); random.choice(foo), random.choice(foo), random.choice(foo)
('d', 'a', 'b')
>>> random.seed(42); random.choice(foo), random.choice(foo), random.choice(foo)
('d', 'a', 'b')
>>> random.seed(42); random.choice(foo), random.choice(foo), random.choice(foo)
('d', 'a', 'b')
>>> random.seed(42); random.choice(foo), random.choice(foo), random.choice(foo)
('d', 'a', 'b')
>>> random.seed(42); random.choice(foo), random.choice(foo), random.choice(foo)
('d', 'a', 'b')

Коментар:

Йдеться не про те, чи справді випадковий вибір є випадковим чи ні. Якщо ви зафіксуєте насіння, ви отримаєте відтворювані результати - і саме для цього насіння призначене. Ви також можете передати насіння в SystemRandom.sr = random.SystemRandom(42)

Що ж, так, ви можете передати йому "насіннєвий" аргумент, але ви побачите, що SystemRandomоб'єкт просто ігнорує його :

def seed(self, *args, **kwds):
    "Stub method.  Not used for a system random number generator."
    return None

8

якщо вам потрібен індекс, просто використовуйте:

import random
foo = ['a', 'b', 'c', 'd', 'e']
print int(random.random() * len(foo))
print foo[int(random.random() * len(foo))]

random.choice робить те саме :)


2
@tc. Насправді це робить по суті те саме. Реалізація random.choice(self, seq)є return seq[int(self.random() * len(seq))].
Вім

2
@wim Це трохи розчаровує, але дуже невтішно - це також визначення того, randrange()що означає, наприклад, що random.SystemRandom().randrange(3<<51)виявляє значну упередженість. Зітхнути ...
тс.

6
@ kevinsa5 Зрештою, це тому, що float(подвійний IEEE) може приймати лише кінцеве число значень у [0,1). Random.random()генерує свій вихід традиційним способом: виберіть випадкове ціле число [0, 2**53)і поділіть на 2**53(53 - кількість бітів удвічі). Таким чином, random()повертається 2 ** 53 рівномірних подвоєнь, і ви можете розділити це рівномірно на N виходів, лише якщо N - потужність 2. Ухил невеликий для малого N, але див collections.Counter(random.SystemRandom().randrange(3<<51)%6 for i in range(100000)).most_common(). (Java Random.nextInt () уникає таких упереджень.)
tc.

1
@tc. Я гадаю, що щось менше, ніж приблизно 2**40, (що становить 1099511627776), було б досить малим, щоб упередження не мали значення на практиці? Це дійсно слід зазначити в документації, тому що якщо хтось не є ретельним, він може не очікувати, що проблеми виникнуть з цієї частини коду.
Євгеній Сергєєв

@tc .: Власне, randomвикористовує getrandbitsдля отримання адекватної кількості бітів для отримання результату для більшого randranges ( random.choiceтакож це використовується). Це справедливо як для 2.7, так і для 3.5. Він використовується лише self.random() * len(seq)тоді, коли getrandbitsвін недоступний. Це не дурне, що ти думаєш.
ShadowRanger

7

Це код зі змінною, яка визначає випадковий індекс:

import random

foo = ['a', 'b', 'c', 'd', 'e']
randomindex = random.randint(0,len(foo)-1) 
print (foo[randomindex])
## print (randomindex)

Це код без змінної:

import random

foo = ['a', 'b', 'c', 'd', 'e']
print (foo[random.randint(0,len(foo)-1)])

І це код найкоротшим та найрозумнішим способом:

import random

foo = ['a', 'b', 'c', 'd', 'e']
print(random.choice(foo))

(пітон 2.7)


3

Наступний код демонструє, чи потрібно виробляти ті самі елементи. Ви також можете вказати, скільки зразків потрібно витягти. Метод повертає новий список , що містить елементи населення, залишаючи вихідне населення без змін. Отриманий список складається в порядку відбору, щоб усі підрозрізи також були дійсними випадковими вибірками.
sample

import random as random
random.seed(0)  # don't use seed function, if you want different results in each run
print(random.sample(foo,3))  # 3 is the number of sample you want to retrieve

Output:['d', 'e', 'a']

1

Випадковий вибір предмета:

import random

my_list = [1, 2, 3, 4, 5]
num_selections = 2

new_list = random.sample(my_list, num_selections)

Щоб зберегти порядок у списку, ви можете:

randIndex = random.sample(range(len(my_list)), n_selections)
randIndex.sort()
new_list = [my_list[i] for i in randIndex]

Дублікат https://stackoverflow.com/a/49682832/4383027


0

Ми також можемо це зробити, використовуючи randint.

from random import randint
l= ['a','b','c']

def get_rand_element(l):
    if l:
        return l[randint(0,len(l)-1)]
    else:
        return None

get_rand_element(l)

19
Чому на землі ти зробив би це так, коли є random.choice()і random.randrange()?
alexis

"random.choice ()" дасть вам "IndexError: список індексів поза діапазоном" у порожньому списку.
Абдул Маджед

6
Як слід: ось для чого винятки. Вибір із порожнього списку - помилка. Повернення Noneпросто натискає баночку до якоїсь випадкової пізнішої точки, де недійсний "елемент" запускає виняток; або ще гірше, ви отримуєте неправильну програму замість винятку, і навіть не знаєте її.
alexis

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.