Bagaimana cara memilih item secara acak dari daftar?


1760

Asumsikan saya memiliki daftar berikut:

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

Apa cara paling sederhana untuk mengambil item secara acak dari daftar ini?

Jawaban:


2678

Menggunakan random.choice()

import random

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

Untuk pilihan acak yang aman secara kriptografis (mis. Untuk membuat frasa sandi dari daftar kata) gunakansecrets.choice()

import secrets

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

secretsbaru dalam Python 3.6, pada Python versi lama Anda bisa menggunakan random.SystemRandomkelas:

import random

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

3
Apakah dua panggilan berturut-turut random.choice(foo)menghasilkan dua hasil yang berbeda?
Eduardo Pignatelli

34
@EduardoPignatelli Setiap pilihan adalah acak, sehingga dapat menghasilkan dua hasil yang berbeda, tetapi tergantung pada benih awal, itu tidak dijamin. Jika Anda ingin memilih n elemen acak yang berbeda dari daftar lst , penggunaanrandom.sample(lst, n)
Graham

6
pada catatan terkait, Standard pseudo-random generators are not suitable for security/cryptographic purposes. ref
Xiao

184

Jika Anda ingin secara acak memilih lebih dari satu item dari daftar, atau memilih item dari set, saya akan merekomendasikan menggunakan random.samplesebagai gantinya.

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] 

Jika Anda hanya menarik satu item dari daftar, pilihannya kurang kikuk, karena menggunakan sampel akan memiliki sintaks random.sample(some_list, 1)[0]sebagai ganti random.choice(some_list).

Sayangnya, pilihan hanya berfungsi untuk satu output dari urutan (seperti daftar atau tupel). Meskipun random.choice(tuple(some_set))mungkin menjadi opsi untuk mendapatkan satu item dari set.

EDIT: Menggunakan Rahasia

Seperti yang telah ditunjukkan oleh banyak orang, jika Anda memerlukan sampel pseudorandom yang lebih aman, Anda harus menggunakan modul rahasia:

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

Jika Anda menginginkan satu baris yang lebih pythonic untuk memilih beberapa item, Anda dapat menggunakan pembongkaran.

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

1
secretsModul BTW telah ditambahkan ke pustaka standar Python di versi 3.6 python.org/dev/peps/pep-0506
and1er

160

Jika Anda juga membutuhkan indeks, gunakan random.randrange

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

42

Pada Python 3.6 Anda dapat menggunakan secretsmodul, yang lebih disukai daripada randommodul untuk kriptografi atau penggunaan keamanan.

Untuk mencetak elemen acak dari daftar:

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

Untuk mencetak indeks acak:

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

Untuk detailnya, lihat PEP 506 .


33

Saya mengusulkan skrip untuk menghapus item yang diambil secara acak dari daftar sampai kosong:

Pertahankan setdan hapus elemen yang diambil secara acak (dengan choice) sampai daftar kosong.

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

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

Tiga langkah memberikan tiga jawaban berbeda:

>>> 
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
Atau Anda bisa saja random.shuffleyanglist sekali dan baik iterate atau pop untuk menghasilkan hasil. Entah akan menghasilkan aliran "pilih acak secara memadai", hanya saja keacakan akan diperkenalkan di awal.
ShadowRanger

2
Secara teoritis Anda dapat menggunakan metode pop () dari suatu himpunan untuk menghapus elemen sembarang dari suatu himpunan dan mengembalikannya, tetapi itu mungkin tidak cukup acak.
Joubarc

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

Dengan python 2:

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

Dengan python 3:

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

6
Perhatikan bahwa random.choicesdengan penggantian sementara random.sampletanpa penggantian.
CentAu

1
Perhatikan juga bahwa random.choices tersedia mulai 3.6 dan lebih baru, bukan sebelumnya!
Cyril N.

11

numpy larutan: numpy.random.choice

Untuk pertanyaan ini, kerjanya sama dengan jawaban yang diterima ( import random; random.choice()), tetapi saya menambahkannya karena pemrogram mungkin sudah mengimpor numpy(seperti saya) & juga ada beberapa perbedaan antara dua metode yang mungkin terkait dengan kasus penggunaan aktual Anda.

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

Untuk reproduktifitas, Anda dapat melakukan:

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

Untuk sampel satu atau lebih item , dikembalikan sebagai array, lulus sizeargumen:

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

9

Bagaimana cara memilih item secara acak dari daftar?

Asumsikan saya memiliki daftar berikut:

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

Apa cara paling sederhana untuk mengambil item secara acak dari daftar ini?

Jika Anda ingin mendekati benar-benar acak , maka saya sarankan secrets.choicedari perpustakaan standar (Baru dalam Python 3.6.):

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

Di atas sama dengan rekomendasi saya sebelumnya, menggunakan SystemRandomobjek dari randommodul dengan choicemetode - tersedia sebelumnya di Python 2:

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

Dan sekarang:

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

Jika Anda ingin pemilihan acak semu deterministik, gunakan choicefungsi (yang sebenarnya merupakan metode terikat pada Randomobjek):

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

Tampaknya acak, tetapi sebenarnya tidak, yang bisa kita lihat jika kita pasang kembali berulang kali:

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

Komentar:

Ini bukan tentang apakah random.choice benar-benar acak atau tidak. Jika Anda memperbaiki benih, Anda akan mendapatkan hasil yang dapat direproduksi - dan untuk itulah benih dirancang. Anda dapat mengirimkan seed ke SystemRandom juga.sr = random.SystemRandom(42)

Ya, Anda bisa memberikan argumen "seed", tetapi Anda akan melihat bahwa SystemRandomobjek mengabaikannya :

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

8

jika Anda membutuhkan indeks, gunakan saja:

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

random.choice melakukan hal yang sama :)


2
@tc. Sebenarnya, hal itu pada dasarnya melakukan hal yang sama. Implementasi random.choice(self, seq)adalah return seq[int(self.random() * len(seq))].
wim

2
@ wim Itu sedikit mengecewakan, tetapi hal yang sangat mengecewakan adalah itu juga definisi randrange()yang berarti misalnya random.SystemRandom().randrange(3<<51)menunjukkan bias yang signifikan. Huh ...
tc.

6
@ kevinsa5 Pada akhirnya itu karena float(ganda IEEE) hanya dapat mengambil sejumlah nilai hingga [0,1). Random.random()menghasilkan output dengan cara tradisional: pilih bilangan bulat acak [0, 2**53)dan bagi dengan 2**53(53 adalah jumlah bit dalam dua kali lipat). Jadi, random()kembalikan 2 ** 53 ganda yang dapat disetel, dan Anda dapat membaginya secara merata menjadi keluaran N hanya jika N berkekuatan 2. Biasnya kecil untuk N kecil, tetapi lihat collections.Counter(random.SystemRandom().randrange(3<<51)%6 for i in range(100000)).most_common(). (Java's Random.nextInt () menghindari bias semacam itu.)
tc.

1
@tc. Saya kira sesuatu yang kurang dari sekitar 2**40, (yang 1099511627776), akan cukup kecil untuk bias tidak menjadi masalah dalam praktek? Ini harus benar-benar ditunjukkan dalam dokumentasi, karena jika seseorang tidak teliti, mereka mungkin tidak mengharapkan masalah datang dari bagian kode mereka.
Evgeni Sergeev

@ tc .: Sebenarnya, randomgunakan getrandbitsuntuk mendapatkan jumlah bit yang memadai untuk menghasilkan hasil untuk randranges yang lebih besar ( random.choicejuga menggunakannya). Ini berlaku untuk 2.7 dan 3.5. Ini hanya digunakan self.random() * len(seq)saat getrandbitstidak tersedia. Anda tidak melakukan hal bodoh yang Anda pikirkan.
ShadowRanger

7

Ini adalah kode dengan variabel yang mendefinisikan indeks acak:

import random

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

Ini adalah kode tanpa variabel:

import random

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

Dan ini adalah kode dengan cara terpendek dan paling cerdas untuk melakukannya:

import random

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

(python 2.7)


3

Kode berikut menunjukkan jika Anda perlu membuat barang yang sama. Anda juga dapat menentukan berapa banyak sampel yang ingin Anda ekstrak.
The sampleMetode mengembalikan daftar baru yang berisi elemen dari populasi sementara meninggalkan penduduk asli tidak berubah. Daftar yang dihasilkan dalam urutan seleksi sehingga semua sub-irisan juga akan menjadi sampel acak yang valid.

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

Pemilihan item acak:

import random

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

new_list = random.sample(my_list, num_selections)

Untuk mempertahankan urutan daftar, Anda dapat melakukan:

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

Gandakan https://stackoverflow.com/a/49682832/4383027


0

Kita juga bisa melakukan ini menggunakan 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
Mengapa kamu mau melakukannya dengan cara ini, ketika ada random.choice()dan random.randrange()?
alexis

"random.choice ()" akan memberi Anda "IndexError: daftar indeks di luar kisaran" pada daftar kosong.
Abdul Majeed

6
Sebagaimana mestinya: itulah pengecualian untuk. Memilih dari daftar kosong adalah kesalahan. Mengembalikan Nonehanya menendang kaleng ke beberapa titik nanti acak di mana "elemen" yang tidak valid memicu pengecualian; atau lebih buruk lagi, Anda mendapatkan program yang salah, bukan pengecualian, dan Anda bahkan tidak mengetahuinya.
alexis

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.