Saya perlu menulis versi acak dari random.choice (setiap elemen dalam daftar memiliki probabilitas berbeda untuk dipilih). Inilah yang saya pikirkan:
def weightedChoice(choices):
"""Like random.choice, but each element can have a different chance of
being selected.
choices can be any iterable containing iterables with two items each.
Technically, they can have more than two items, the rest will just be
ignored. The first item is the thing being chosen, the second item is
its weight. The weights can be any numeric values, what matters is the
relative differences between them.
"""
space = {}
current = 0
for choice, weight in choices:
if weight > 0:
space[current] = choice
current += weight
rand = random.uniform(0, current)
for key in sorted(space.keys() + [current]):
if rand < key:
return choice
choice = space[key]
return None
Fungsi ini tampaknya terlalu rumit bagi saya, dan jelek. Saya berharap semua orang di sini dapat menawarkan beberapa saran untuk memperbaikinya atau cara lain untuk melakukan ini. Efisiensi bagi saya tidak sepenting kebersihan kode dan keterbacaan.
python
optimization
Colin
sumber
sumber
random.choices
untuk panggilan individu. Jika Anda membutuhkan banyak hasil acak, sangat penting untuk memilih semuanya sekaligus dengan menyesuaikannumber_of_items_to_pick
. Jika Anda melakukannya, ini adalah urutan besarnya lebih cepat.len(list_of_candidates)
, dan kemudian lakukanlist_of_candidates[draw]
Sejak Python 3.6 ada metode
choices
darirandom
modul.Perhatikan bahwa
random.choices
sampel akan diganti dengan per dokumen :Jika Anda perlu mengambil sampel tanpa penggantian, maka sebagai status jawaban brilian @ ronan-paixão , Anda dapat menggunakan
numpy.choice
, yangreplace
argumennya mengontrol perilaku tersebut.sumber
random.choices
tidak, jadi tentu saja itu lebih lambat pada daftar item 8 kecil, dan jika Anda memilih 10k kali dari daftar seperti itu, Anda benar. Tetapi untuk kasus-kasus ketika daftar lebih besar (tergantung pada bagaimana Anda menguji, saya melihat break point antara 100-300 elemen),np.random.choice
mulai mengunggulirandom.choices
oleh celah yang cukup lebar. Sebagai contoh, termasuk langkah normalisasi bersama dengan panggilan numpy, saya mendapatkan speedup hampir 4x lebihrandom.choices
untuk daftar elemen 10k.sumber
upto +=w; if upto > r
if r < 0
r <= 0
. Pertimbangkan satu set input 1 item, dan gulungan 1,0. Pernyataan itu akan gagal. Saya memperbaiki kesalahan itu dalam jawaban.# pragma: no branch
0.0 <= x < total
.Jika Anda perlu membuat lebih dari satu pilihan, bagi ini menjadi dua fungsi, satu untuk membangun bobot kumulatif dan lainnya untuk membagi dua ke titik acak.
sumber
O(n)
karena perhitungan distribusi kumulatif.random()
tidak dapat mengembalikan 1.0. Per dokumen, ia mengembalikan hasil dalam interval setengah-terbuka[0.0, 1.0)
, yang mengatakan bahwa ia dapat mengembalikan tepat 0,0, tetapi tidak dapat mengembalikan tepat 1,0. Nilai terbesar yang dapat dikembalikan adalah 0,99999999999999988897769753748434595763683319091796875 (yang dicetak Python sebagai 0,999999999999999999, dan merupakan float 64-bit terbesar kurang dari 1).Jika Anda tidak keberatan menggunakan numpy, Anda dapat menggunakan numpy.random.choice .
Sebagai contoh:
Jika Anda tahu berapa banyak pilihan yang harus Anda buat sebelumnya, Anda bisa melakukannya tanpa loop seperti ini:
sumber
Mentah, tetapi mungkin cukup:
Apakah itu bekerja?
Cetakan:
Asumsikan bahwa semua bobot adalah bilangan bulat. Mereka tidak perlu menambahkan hingga 100, saya hanya melakukan itu untuk membuat hasil tes lebih mudah diinterpretasikan. (Jika bobot adalah angka floating point, kalikan semuanya dengan 10 berulang hingga semua bobot> = 1.)
sumber
[[]]*10
- semua elemen di daftar luar menunjuk ke daftar yang sama.int
Anda masih mendapatkan banyak referensi ke objek yang sama dengan melakukan sesuatu seperti[id(x) for x in ([99**99] * 100)]
dan mengamati yangid
mengembalikan alamat memori yang sama pada setiap panggilan.Jika Anda memiliki kamus berbobot alih-alih daftar, Anda dapat menulis ini
Catatan yang
[k for k in items for dummy in range(items[k])]
menghasilkan daftar ini['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'c', 'b', 'b', 'b', 'b', 'b']
sumber
Pada Python
v3.6
,random.choices
dapat digunakan untuk mengembalikanlist
elemen ukuran tertentu dari populasi tertentu dengan bobot opsional.populasi :
list
berisi pengamatan unik. (Jika kosong, naikkanIndexError
)bobot : Lebih tepatnya bobot relatif yang dibutuhkan untuk membuat pilihan.
cum_weights : bobot kumulatif diperlukan untuk membuat pilihan.
k : ukuran (
len
) dari yanglist
akan dikeluarkan. (Defaultlen()=1
)Beberapa Peringatan:
1) Itu menggunakan sampling tertimbang dengan penggantian sehingga barang yang ditarik akan diganti nanti. Nilai-nilai dalam urutan bobot itu sendiri tidak penting, tetapi rasio relatifnya tidak.
Tidak seperti
np.random.choice
yang hanya dapat mengambil probabilitas sebagai bobot dan juga yang harus memastikan penjumlahan probabilitas individu hingga 1 kriteria, tidak ada peraturan seperti itu di sini. Selama mereka termasuk tipe numerik (int/float/fraction
kecualiDecimal
tipe), ini akan tetap bekerja.2) Jika bobot atau cum_weights tidak ditentukan, pemilihan dilakukan dengan probabilitas yang sama. Jika urutan bobot disediakan, panjangnya harus sama dengan urutan populasi .
Menentukan bobot dan cum_weights memunculkan a
TypeError
.3) cum_weights biasanya merupakan hasil dari
itertools.accumulate
fungsi yang sangat berguna dalam situasi seperti itu.Jadi, baik memasok
weights=[12, 12, 4]
ataucum_weights=[12, 24, 28]
untuk kasus kami yang dibuat menghasilkan hasil yang sama dan yang terakhir tampaknya lebih cepat / efisien.sumber
Berikut adalah versi yang disertakan dalam pustaka standar untuk Python 3.6:
Sumber: https://hg.python.org/cpython/file/tip/Lib/random.py#l340
sumber
sumber
Saya mungkin sudah terlambat untuk menyumbangkan sesuatu yang bermanfaat, tetapi di sini cuplikan sederhana, pendek, dan sangat efisien:
Tidak perlu mengurutkan probabilitas Anda atau membuat vektor dengan cmf Anda, dan itu berakhir setelah menemukan pilihannya. Memori: O (1), waktu: O (N), dengan rata-rata waktu berjalan ~ N / 2.
Jika Anda memiliki bobot, cukup tambahkan satu baris:
sumber
np.random.choice
,. Tapi yang lebih menarik, ada mode kegagalan di mana ini menimbulkan pengecualian. Melakukanprobabilities = weights / sum(weights)
tidak menjamin bahwaprobabilities
akan berjumlah 1; misalnya, jikaweights
ini[1,1,1,1,1,1,1]
kemudianprobabilities
hanya akan berjumlah 0,9999999999999998, lebih kecil dari nilai pengembalian sebesar mungkinrandom.random
(yaitu 0,999999999999999999). Makachoice <= cmf
tidak pernah puas.Jika daftar pilihan tertimbang Anda relatif statis, dan Anda ingin sering mengambil sampel, Anda dapat melakukan satu langkah preprocessing O (N), dan kemudian melakukan seleksi dalam O (1), menggunakan fungsi-fungsi dalam jawaban terkait ini .
sumber
Saya melihat utas lainnya yang runcing dan menghasilkan variasi dalam gaya pengkodean saya, ini mengembalikan indeks pilihan untuk tujuan penghitungan, tetapi mudah untuk mengembalikan string (komentar pengembalian alternatif):
sumber
Itu tergantung pada berapa kali Anda ingin sampel distribusi.
Misalkan Anda ingin mencicipi distribusi K kali. Kemudian, kompleksitas waktu yang digunakan
np.random.choice()
setiap waktu adalahO(K(n + log(n)))
kapann
jumlah item dalam distribusi.Dalam kasus saya, saya perlu sampel distribusi yang sama beberapa kali dari urutan 10 ^ 3 di mana n adalah urutan 10 ^ 6. Saya menggunakan kode di bawah ini, yang mengkompilasi distribusi kumulatif dan sampel dalam
O(log(n))
. Kompleksitas waktu keseluruhan adalahO(n+K*log(n))
.sumber
Jika Anda memiliki Python 3, dan takut menginstal
numpy
atau menulis loop Anda sendiri, Anda dapat melakukannya:Karena Anda dapat membangun apa pun dari sekantong adaptor pipa ledeng! Meskipun ... aku harus mengakui bahwa jawaban Ned, meski sedikit lebih lama, lebih mudah dimengerti.
sumber
Solusi umum:
sumber
Ini adalah versi lain dari weighted_choice yang menggunakan numpy. Lulus dalam vektor bobot dan akan mengembalikan array 0 yang berisi 1 yang menunjukkan bin mana yang dipilih. Kode default untuk hanya membuat satu pengundian tetapi Anda dapat meneruskan dalam jumlah pengundian yang akan dibuat dan jumlah per bin yang ditarik akan dikembalikan.
Jika vektor bobot tidak menjumlahkan ke 1, vektor akan dinormalisasi sehingga tidak.
sumber
Cara lain untuk melakukan ini, dengan asumsi kita memiliki bobot pada indeks yang sama dengan elemen dalam array elemen.
Sekarang mari kita asumsikan, kita harus mencicipi 3 item dalam 1 percobaan. Anda dapat mengasumsikan bahwa ada tiga bola R, G, B yang hadir dalam jumlah besar dalam perbandingan bobotnya yang diberikan oleh susunan bobot, berikut ini adalah hasil yang mungkin:
Anda juga bisa memikirkan jumlah item yang akan dipilih sebagai jumlah uji binomial / multinomial dalam satu set. Jadi, contoh di atas masih bisa berfungsi sebagai
sumber
Ada kuliah tentang hal ini oleh Sebastien Thurn dalam kursus Udacity gratis AI untuk Robotika. Pada dasarnya ia membuat array melingkar dari bobot yang diindeks menggunakan operator mod
%
, menetapkan variabel beta ke 0, secara acak memilih indeks, untuk loop melalui N di mana N adalah jumlah indeks dan dalam loop untuk kenaikan pertama beta dengan rumus:beta = beta + sampel seragam dari {0 ... 2 * Weight_max}
dan kemudian bersarang di dalam for loop, loop sementara per di bawah ini:
Kemudian ke indeks berikutnya untuk sampel berdasarkan probabilitas (atau probabilitas normalisasi dalam kasus yang disajikan dalam kursus).
Tautan kuliah: https://classroom.udacity.com/courses/cs373/lessons/48704330/concepts/487480820923
Saya masuk ke Udacity dengan akun sekolah saya jadi jika tautannya tidak berfungsi, itu adalah Pelajaran 8, video nomor 21 dari Kecerdasan Buatan untuk Robotika di mana dia memberi kuliah tentang filter partikel.
sumber
Salah satu caranya adalah dengan mengacak total semua bobot dan kemudian menggunakan nilai-nilai sebagai titik batas untuk setiap var. Berikut ini adalah implementasi kasar sebagai generator.
sumber
Menggunakan numpy
sumber
np.random.choice
, sebagaimana disebutkan dalam jawaban yang diterima yang sudah ada di sini sejak 2014. Apa gunanya bergulir sendiri?Saya perlu melakukan sesuatu seperti ini sangat cepat sangat sederhana, dari mencari ide saya akhirnya membuat template ini. Idenya adalah menerima nilai-nilai tertimbang dalam bentuk json dari api, yang di sini disimulasikan oleh dikt.
Kemudian terjemahkan ke dalam daftar di mana setiap nilai berulang secara proporsional dengan bobotnya, dan gunakan saja random.choice untuk memilih nilai dari daftar.
Saya mencoba menjalankannya dengan 10, 100 dan 1000 iterasi. Distribusi tampaknya cukup solid.
sumber
Saya tidak suka sintaksis dari semua itu. Saya benar-benar ingin menentukan item apa saja dan beratnya masing-masing. Saya menyadari bahwa saya dapat menggunakan
random.choices
tetapi sebaliknya saya dengan cepat menulis kelas di bawah ini.sumber
Berikan random.choice () dengan daftar pra-tertimbang:
Solusi & Tes:
Keluaran:
sumber