Làm thế nào để chọn ngẫu nhiên một mục từ danh sách?


1760

Giả sử tôi có danh sách sau:

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

Cách đơn giản nhất để lấy một mục ngẫu nhiên từ danh sách này là gì?

Câu trả lời:


2678

Sử dụng random.choice()

import random

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

Đối với các lựa chọn ngẫu nhiên được bảo mật bằng mật mã (ví dụ: để tạo cụm mật khẩu từ danh sách từ), hãy sử dụngsecrets.choice()

import secrets

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

secretslà phiên bản mới trong Python 3.6, trên các phiên bản cũ hơn của Python, bạn có thể sử dụng random.SystemRandomlớp:

import random

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

3
Có thực hiện hai cuộc gọi random.choice(foo)trả lại liên tiếp hai kết quả khác nhau?
Eduardo Pignatelli

34
@EduardoPignatelli Mỗi lựa chọn là ngẫu nhiên, vì vậy nó có thể trả về hai kết quả khác nhau, nhưng tùy thuộc vào hạt giống bắt đầu, nó không được đảm bảo. Nếu bạn muốn chọn n yếu tố ngẫu nhiên khác biệt từ một danh sách lst , sử dụngrandom.sample(lst, n)
Graham

6
trên một lưu ý liên quan, Standard pseudo-random generators are not suitable for security/cryptographic purposes. ref
Xiao

184

Nếu bạn muốn chọn ngẫu nhiên nhiều mục trong danh sách hoặc chọn một mục từ một bộ, tôi khuyên bạn nên sử dụng random.samplethay thế.

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] 

Nếu bạn chỉ lấy một mục duy nhất từ ​​danh sách, thì sự lựa chọn sẽ ít rắc rối hơn, vì sử dụng mẫu sẽ có cú pháp random.sample(some_list, 1)[0]thay vì random.choice(some_list).

Thật không may, sự lựa chọn chỉ hoạt động cho một đầu ra duy nhất từ ​​các chuỗi (chẳng hạn như danh sách hoặc bộ dữ liệu). Mặc dù random.choice(tuple(some_set))có thể là một tùy chọn để nhận một mục duy nhất từ ​​một bộ.

EDIT: Sử dụng bí mật

Như nhiều người đã chỉ ra, nếu bạn yêu cầu các mẫu giả ngẫu nhiên an toàn hơn, bạn nên sử dụng mô-đun bí mật:

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: Pythonic One-liner

Nếu bạn muốn có một lớp lót pythonic hơn để chọn nhiều mục, bạn có thể sử dụng giải nén.

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

1
secretsMô-đun BTW đã được thêm vào thư viện chuẩn Python trong phiên bản 3.6 python.org/dev/peps/pep-0506
and1er

160

Nếu bạn cũng cần chỉ mục, sử dụng random.randrange

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

42

Kể từ Python 3.6, bạn có thể sử dụng secretsmô-đun, tốt hơn randommô-đun cho việc sử dụng mật mã hoặc bảo mật.

Để in một phần tử ngẫu nhiên từ danh sách:

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

Để in một chỉ mục ngẫu nhiên:

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

Để biết chi tiết, xem PEP 506 .


33

Tôi đề xuất một kịch bản để xóa các mục được chọn ngẫu nhiên khỏi danh sách cho đến khi trống:

Duy trì a setvà xóa phần tử nhặt ngẫu nhiên (với choice) cho đến khi danh sách trống.

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

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

Ba lần chạy cho ba câu trả lời khác nhau:

>>> 
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
Hoặc bạn có thể chỉ random.shufflelistmột lần và một trong hai lặp nó hay bật nó tạo ra kết quả. Hoặc là sẽ dẫn đến một luồng "chọn ngẫu nhiên không lặp lại" hoàn toàn đầy đủ, chỉ là sự ngẫu nhiên sẽ được giới thiệu ngay từ đầu.
ShadowRanger

2
Về mặt lý thuyết bạn có thể sử dụng phương thức pop () của một tập hợp để loại bỏ một phần tử tùy ý khỏi một tập hợp và trả về nó, nhưng có lẽ nó không đủ ngẫu nhiên.
Joubarc

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

Trong trăn 2:

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

Trong trăn 3:

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

6
Lưu ý rằng random.choicesvới sự thay thế trong khi random.samplekhông có sự thay thế.
CentAu

1
Cũng lưu ý rằng Random.choices có sẵn từ 3.6 trở lên, không phải trước đó!
Cyril N.

11

numpy giải pháp: numpy.random.choice

Đối với câu hỏi này, nó hoạt động giống như câu trả lời được chấp nhận ( import random; random.choice()), nhưng tôi đã thêm nó bởi vì lập trình viên có thể đã nhập numpy(như tôi) và cũng có một số khác biệt giữa hai phương thức có thể liên quan đến trường hợp sử dụng thực tế của bạn.

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

Đối với khả năng tái tạo, bạn có thể làm:

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

Đối với các mẫu của một hoặc nhiều mục , được trả lại dưới dạng array, vượt qua sizeđối số:

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

9

Làm thế nào để chọn ngẫu nhiên một mục từ danh sách?

Giả sử tôi có danh sách sau:

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

Cách đơn giản nhất để lấy một mục ngẫu nhiên từ danh sách này là gì?

Nếu bạn muốn gần thực sự ngẫu nhiên , thì tôi đề xuất secrets.choicetừ thư viện chuẩn (Mới trong Python 3.6.):

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

Ở trên tương đương với khuyến nghị trước đây của tôi, sử dụng một SystemRandomđối tượng từ randommô-đun với choicephương thức - có sẵn trước đó trong Python 2:

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

Và bây giờ:

>>> 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'

Nếu bạn muốn lựa chọn giả ngẫu nhiên xác định, hãy sử dụng choicehàm (đây thực sự là một phương thức ràng buộc trên một Randomđối tượng):

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

Điều này có vẻ ngẫu nhiên, nhưng thực tế không phải vậy, mà chúng ta có thể thấy nếu chúng ta lặp lại nó nhiều lần:

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

Một lời bình luận:

Đây không phải là về việc Random.choice có thực sự ngẫu nhiên hay không. Nếu bạn sửa hạt giống, bạn sẽ nhận được kết quả có thể lặp lại - và đó là hạt giống được thiết kế cho. Bạn cũng có thể chuyển một hạt giống đến SystemRandom.sr = random.SystemRandom(42)

Vâng, vâng, bạn có thể truyền cho nó một đối số "hạt giống", nhưng bạn sẽ thấy rằng SystemRandomđối tượng chỉ đơn giản bỏ qua nó :

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

8

Nếu bạn cần chỉ mục, chỉ cần sử dụng:

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

Random.choice cũng vậy :)


2
@tc. Trên thực tế, nó về cơ bản là giống nhau. Thực hiện random.choice(self, seq)return seq[int(self.random() * len(seq))].
wim

2
@wim Đó là một chút thất vọng, nhưng rất điều đáng thất vọng là đó cũng là định nghĩa về randrange()mà phương tiện ví dụ như random.SystemRandom().randrange(3<<51)triển lãm thiên vị đáng kể. Thở dài ...
tc.

6
@ kevinsa5 Cuối cùng, bởi vì float(một nhân đôi của IEEE) chỉ có thể lấy một số lượng giá trị hữu hạn trong [0,1). Random.random()tạo đầu ra của nó theo cách truyền thống: chọn một số nguyên ngẫu nhiên [0, 2**53)và chia cho 2**53(53 là số bit trong một lần). Vì vậy, random()trả về 2 ** 53 nhân đôi có thể trang bị và bạn có thể chia đều số này thành N đầu ra chỉ khi N là lũy thừa của 2. Độ lệch nhỏ đối với N nhỏ, nhưng xem collections.Counter(random.SystemRandom().randrange(3<<51)%6 for i in range(100000)).most_common(). (Random'nextInt () của Java tránh sự thiên vị như vậy.)
tc.

1
@tc. Tôi cho rằng bất cứ điều gì ít hơn về 2**40, (đó là 1099511627776), sẽ đủ nhỏ để sự thiên vị không quan trọng trong thực tế? Điều này thực sự cần được chỉ ra trong tài liệu, bởi vì nếu ai đó không tỉ mỉ, họ có thể không mong đợi các vấn đề đến từ phần mã này.
Evgeni Sergeev

@tc.: Trên thực tế, randomsử dụng getrandbitsđể có đủ số bit để tạo kết quả cho randranges lớn hơn ( random.choicecũng đang sử dụng số đó). Điều này đúng trên cả 2.7 và 3.5. Nó chỉ sử dụng self.random() * len(seq)khi getrandbitskhông có sẵn. Nó không làm điều ngu ngốc mà bạn nghĩ nó là.
ShadowRanger

7

Đây là mã có một biến xác định chỉ số ngẫu nhiên:

import random

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

Đây là mã không có biến:

import random

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

Và đây là mã theo cách ngắn nhất và thông minh nhất để làm điều đó:

import random

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

(trăn 2.7)


3

Các mã sau đây chứng minh nếu bạn cần sản xuất các mặt hàng tương tự. Bạn cũng có thể chỉ định số lượng mẫu bạn muốn trích xuất.
Các samplephương thức trả về một danh sách mới có chứa các yếu tố từ dân, vừa rời khỏi dân gốc không thay đổi. Danh sách kết quả theo thứ tự lựa chọn sao cho tất cả các lát con cũng sẽ là các mẫu ngẫu nhiên hợp lệ.

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

Lựa chọn vật phẩm ngẫu nhiên:

import random

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

new_list = random.sample(my_list, num_selections)

Để giữ thứ tự của danh sách, bạn có thể làm:

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

Bản sao của https://stackoverflow.com/a/49682832/4383027


0

Chúng tôi cũng có thể làm điều này bằng cách sử dụng 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
Tại sao trên trái đất bạn sẽ làm theo cách này, khi có random.choice()random.randrange()?
alexis

"Random.choice ()" sẽ cung cấp cho bạn "IndexError: liệt kê danh sách ngoài phạm vi" trong danh sách trống.
Abdul Majeed

6
Vì nó nên: đó là những gì ngoại lệ dành cho. Chọn từ một danh sách trống là một lỗi. Trả lại Nonechỉ cần đóng hộp đến một số điểm ngẫu nhiên sau đó trong đó "phần tử" không hợp lệ kích hoạt một ngoại lệ; hoặc tệ hơn nữa là bạn nhận được một chương trình không chính xác thay vì một ngoại lệ và thậm chí bạn không biết nó.
alexis

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.