Một phiên bản có trọng số của Random.choice


245

Tôi cần phải viết một phiên bản có trọng số của Random.choice (mỗi thành phần trong danh sách có xác suất khác nhau để được chọn). Đây là những gì tôi nghĩ ra:

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

Chức năng này có vẻ quá phức tạp đối với tôi và xấu xí. Tôi hy vọng mọi người ở đây có thể đưa ra một số gợi ý về việc cải thiện nó hoặc các cách khác để làm điều này. Hiệu quả không quan trọng đối với tôi như sự sạch sẽ và dễ đọc của mã.

Câu trả lời:


297

Kể từ phiên bản 1.7.0, NumPy có choicechức năng hỗ trợ phân phối xác suất.

from numpy.random import choice
draw = choice(list_of_candidates, number_of_items_to_pick,
              p=probability_distribution)

Lưu ý rằng đó probability_distributionlà một chuỗi theo cùng một thứ tự list_of_candidates. Bạn cũng có thể sử dụng từ khóa replace=Falseđể thay đổi hành vi để các mục đã vẽ không bị thay thế.


11
Theo thử nghiệm của tôi, đây là một thứ tự cường độ chậm hơn so random.choicesvới các cuộc gọi riêng lẻ. Nếu bạn cần nhiều kết quả ngẫu nhiên, điều thực sự quan trọng là chọn tất cả chúng cùng một lúc bằng cách điều chỉnh number_of_items_to_pick. Nếu bạn làm như vậy, đó là một thứ tự cường độ nhanh hơn.
jpmc26

2
Điều này không hoạt động với các bộ dữ liệu, v.v. ("ValueError: a phải là 1 chiều"), vì vậy trong trường hợp đó, người ta có thể yêu cầu numpy chọn chỉ mục vào danh sách, tức là len(list_of_candidates), sau đó làmlist_of_candidates[draw]
xjcl 17/03/19

218

Vì Python 3.6 có một phương thức choicestừ randommô-đun.

Python 3.6.1 (v3.6.1:69c0db5050, Mar 21 2017, 01:21:04)
Type 'copyright', 'credits' or 'license' for more information
IPython 6.0.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import random

In [2]: random.choices(
...:     population=[['a','b'], ['b','a'], ['c','b']],
...:     weights=[0.2, 0.2, 0.6],
...:     k=10
...: )

Out[2]:
[['c', 'b'],
 ['c', 'b'],
 ['b', 'a'],
 ['c', 'b'],
 ['c', 'b'],
 ['b', 'a'],
 ['c', 'b'],
 ['b', 'a'],
 ['c', 'b'],
 ['c', 'b']]

Lưu ý rằng random.choicessẽ lấy mẫu với sự thay thế , theo các tài liệu :

Trả về một kdanh sách kích thước của các yếu tố được lựa chọn từ dân số với sự thay thế.

Nếu bạn cần lấy mẫu mà không cần thay thế, thì trạng thái câu trả lời xuất sắc của @ ronan-paixão , bạn có thể sử dụng numpy.choice, replaceđối số của nó kiểm soát hành vi đó.


4
Điều này nhanh hơn nhiều so với numpy.random.choice. Chọn từ danh sách 8 vật phẩm có trọng số 10.000 lần, numpy.random.choice mất 0,3361 giây trong khi dưới dạng Random.choices mất 0,0416 giây, nhanh hơn khoảng 8 lần.
Mã Anton

@AntonCodes Ví dụ này là cherry được chọn. numpy sẽ có một số chi phí không đổi trong thời gian random.choiceskhông thành công, do đó, tất nhiên nó sẽ chậm hơn trong danh sách 8 mục rất nhỏ và nếu bạn chọn 10 nghìn lần từ danh sách đó, bạn đã đúng. Nhưng đối với các trường hợp khi danh sách lớn hơn (tùy thuộc vào cách bạn đang kiểm tra, tôi thấy các điểm dừng giữa 100-300 phần tử), np.random.choicebắt đầu vượt trội hơnrandom.choices bởi khoảng cách khá rộng. Ví dụ, bao gồm bước chuẩn hóa cùng với lệnh gọi nhanh, tôi nhận được tốc độ tăng gần gấp 4 lần random.choicescho danh sách các phần tử 10k.
ggorlen

Đây phải là câu trả lời mới dựa trên sự cải thiện hiệu suất mà @AntonCodes đã báo cáo.
Wayne Workman

132
def weighted_choice(choices):
   total = sum(w for c, w in choices)
   r = random.uniform(0, total)
   upto = 0
   for c, w in choices:
      if upto + w >= r:
         return c
      upto += w
   assert False, "Shouldn't get here"

10
Bạn có thể bỏ một thao tác và tiết kiệm một chút thời gian bằng cách đảo ngược các câu lệnh bên trong vòng lặp for:upto +=w; if upto > r
knite

5
lưu một biến bằng cách xóa tối đa và chỉ giảm r theo trọng số mỗi lần. Sự so sánh là sau đóif r < 0
JnBrymn

@JnBrymn Bạn cần kiểm tra r <= 0. Hãy xem xét một bộ đầu vào gồm 1 mục và một cuộn là 1.0. Sự khẳng định sẽ thất bại sau đó. Tôi đã sửa lỗi đó trong câu trả lời.
moooeeeep

1
@Sardathrion bạn có thể sử dụng một pragma để đánh dấu vòng lặp for là một phần:# pragma: no branch
Ned Batchelder

1
@ mLstudent33 Tôi không sử dụng Udacity.
Mã Anton

70
  1. Sắp xếp các trọng số thành một phân phối tích lũy.
  2. Sử dụng Random.random () để chọn một float ngẫu nhiên 0.0 <= x < total.
  3. Tìm kiếm bản phân phối bằng cách sử dụng bisect.bisect như được hiển thị trong ví dụ tại http://docs.python.org/dev/l Library / bisect.html # other-examples .
from random import random
from bisect import bisect

def weighted_choice(choices):
    values, weights = zip(*choices)
    total = 0
    cum_weights = []
    for w in weights:
        total += w
        cum_weights.append(total)
    x = random() * total
    i = bisect(cum_weights, x)
    return values[i]

>>> weighted_choice([("WHITE",90), ("RED",8), ("GREEN",2)])
'WHITE'

Nếu bạn cần thực hiện nhiều lựa chọn, hãy chia phần này thành hai hàm, một để xây dựng các trọng số tích lũy và một để chia thành một điểm ngẫu nhiên.


5
Điều này hiệu quả hơn câu trả lời của Ned. Về cơ bản, thay vì thực hiện tìm kiếm tuyến tính (O (n)) thông qua các lựa chọn, anh ta thực hiện tìm kiếm nhị phân (O (log n)). +1!
NHDaly

chỉ số tuple ngoài phạm vi nếu ngẫu nhiên () xảy ra để trả về 1.0
Jon Vaughan

10
Điều này vẫn chạy trong O(n)vì tính toán phân phối tích lũy.
Lev Levitsky

6
Giải pháp này tốt hơn trong trường hợp cần nhiều cuộc gọi đến weighted_choice cho cùng một bộ lựa chọn. Trong trường hợp đó, bạn có thể tạo tổng tích lũy một lần và thực hiện tìm kiếm nhị phân trên mỗi cuộc gọi.
A-mốt

1
@JonVaughan random() không thể trả lại 1.0. Theo các tài liệu, nó trả về một kết quả trong khoảng thời gian nửa mở [0.0, 1.0), nghĩa là nó có thể trả về chính xác 0,0, nhưng không thể trả về chính xác 1.0. Giá trị lớn nhất mà nó có thể trả về là 0.99999999999999988897769753748434595763683319091796875 (mà Python in là 0.9999999999999999 và là số float lớn nhất 64 bit nhỏ hơn 1).
Đánh dấu Amery

21

Nếu bạn không phiền khi sử dụng numpy, bạn có thể sử dụng numpy.random.choice .

Ví dụ:

import numpy

items  = [["item1", 0.2], ["item2", 0.3], ["item3", 0.45], ["item4", 0.05]
elems = [i[0] for i in items]
probs = [i[1] for i in items]

trials = 1000
results = [0] * len(items)
for i in range(trials):
    res = numpy.random.choice(items, p=probs)  #This is where the item is selected!
    results[items.index(res)] += 1
results = [r / float(trials) for r in results]
print "item\texpected\tactual"
for i in range(len(probs)):
    print "%s\t%0.4f\t%0.4f" % (items[i], probs[i], results[i])

Nếu bạn biết có bao nhiêu lựa chọn bạn cần thực hiện trước, bạn có thể thực hiện mà không cần một vòng lặp như thế này:

numpy.random.choice(items, trials, p=probs)

15

Thô, nhưng có thể là đủ:

import random
weighted_choice = lambda s : random.choice(sum(([v]*wt for v,wt in s),[]))

Nó có hoạt động không?

# define choices and relative weights
choices = [("WHITE",90), ("RED",8), ("GREEN",2)]

# initialize tally dict
tally = dict.fromkeys(choices, 0)

# tally up 1000 weighted choices
for i in xrange(1000):
    tally[weighted_choice(choices)] += 1

print tally.items()

Bản in:

[('WHITE', 904), ('GREEN', 22), ('RED', 74)]

Giả sử rằng tất cả các trọng số là số nguyên. Họ không phải thêm tới 100, tôi chỉ cần làm điều đó để làm cho kết quả kiểm tra dễ diễn giải hơn. (Nếu trọng số là số dấu phẩy động, hãy nhân tất cả chúng với 10 lần cho đến khi tất cả các trọng số> = 1.)

weights = [.6, .2, .001, .199]
while any(w < 1.0 for w in weights):
    weights = [w*10 for w in weights]
weights = map(int, weights)

1
Mặc dù vậy, tôi không chắc mình có thể cho rằng tất cả các trọng số đều là số nguyên.
Colin

1
Có vẻ như các đối tượng của bạn sẽ được nhân đôi trong ví dụ này. Điều đó không hiệu quả (và chức năng chuyển đổi trọng số thành số nguyên) cũng vậy. Tuy nhiên, giải pháp này là một lớp lót tốt nếu trọng số nguyên nhỏ.
wei2912

Nguyên thủy sẽ được nhân đôi, nhưng các đối tượng sẽ chỉ có các tham chiếu trùng lặp, không phải chính các đối tượng. (đây là lý do tại sao bạn không thể tạo danh sách các danh sách bằng cách sử dụng [[]]*10- tất cả các yếu tố trong danh sách bên ngoài đều trỏ đến cùng một danh sách.
PaulMcG

@PaulMcG Không; không có gì ngoài tài liệu tham khảo sẽ được nhân đôi. Hệ thống kiểu của Python không có khái niệm nguyên thủy. Bạn có thể xác nhận rằng ngay cả với ví dụ như intbạn vẫn nhận được nhiều tài liệu tham khảo cho cùng một đối tượng bằng cách thực hiện một cái gì đó giống như [id(x) for x in ([99**99] * 100)]và quan sát idtrả về cùng một địa chỉ bộ nhớ trên mỗi cuộc gọi.
Đánh dấu Amery

14

Nếu bạn có một từ điển có trọng số thay vì một danh sách, bạn có thể viết nó

items = { "a": 10, "b": 5, "c": 1 } 
random.choice([k for k in items for dummy in range(items[k])])

Lưu ý rằng [k for k in items for dummy in range(items[k])]tạo ra danh sách này['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'c', 'b', 'b', 'b', 'b', 'b']


10
Điều này hoạt động với tổng giá trị dân số nhỏ, nhưng không phải cho các bộ dữ liệu lớn (ví dụ dân số Hoa Kỳ theo tiểu bang cuối cùng sẽ tạo ra một danh sách làm việc với 300 triệu mục trong đó).
Ryan

@Ryan Thật vậy. Nó cũng không hoạt động đối với các trọng số không nguyên, đó là một kịch bản thực tế khác (ví dụ: nếu bạn có các trọng số của bạn được biểu thị dưới dạng xác suất lựa chọn).
Đánh dấu Amery

12

Kể từ Python v3.6, random.choicescó thể được sử dụng để trả về một listtrong các phần tử có kích thước được chỉ định từ dân số đã cho với các trọng số tùy chọn.

random.choices(population, weights=None, *, cum_weights=None, k=1)

  • dân số : listchứa những quan sát độc đáo. (Nếu trống, tăng IndexError)

  • trọng lượng : chính xác hơn trọng lượng tương đối cần thiết để thực hiện lựa chọn.

  • cum_weights : trọng số tích lũy cần thiết để thực hiện các lựa chọn.

  • k : kích thước ( len) của đầu listra. (Mặc định len()=1)


Vài cảnh báo:

1) Nó sử dụng lấy mẫu có trọng số với sự thay thế để các vật phẩm được rút ra sẽ được thay thế sau đó. Bản thân các giá trị trong chuỗi trọng số không quan trọng, nhưng tỷ lệ tương đối của chúng thì không.

Không giống như np.random.choicechỉ có thể đảm nhận các xác suất là trọng số và cũng phải đảm bảo tổng các xác suất riêng lẻ lên tới 1 tiêu chí, không có quy định nào như vậy ở đây. Miễn là chúng thuộc về loại số ( int/float/fractionngoại trừ Decimalloại), chúng vẫn sẽ hoạt động.

>>> import random
# weights being integers
>>> random.choices(["white", "green", "red"], [12, 12, 4], k=10)
['green', 'red', 'green', 'white', 'white', 'white', 'green', 'white', 'red', 'white']
# weights being floats
>>> random.choices(["white", "green", "red"], [.12, .12, .04], k=10)
['white', 'white', 'green', 'green', 'red', 'red', 'white', 'green', 'white', 'green']
# weights being fractions
>>> random.choices(["white", "green", "red"], [12/100, 12/100, 4/100], k=10)
['green', 'green', 'white', 'red', 'green', 'red', 'white', 'green', 'green', 'green']

2) Nếu không chỉ định trọng số hay cum_weights , các lựa chọn được thực hiện với xác suất bằng nhau. Nếu một chuỗi trọng số được cung cấp, nó phải có cùng độ dài với chuỗi dân số .

Chỉ định cả trọng sốcum_weights tăng a TypeError.

>>> random.choices(["white", "green", "red"], k=10)
['white', 'white', 'green', 'red', 'red', 'red', 'white', 'white', 'white', 'green']

3) cum_weights thường là kết quả của itertools.accumulatechức năng thực sự tiện dụng trong các tình huống như vậy.

Từ các tài liệu liên kết:

Trong nội bộ, các trọng số tương đối được chuyển đổi thành các trọng số tích lũy trước khi thực hiện các lựa chọn, do đó việc cung cấp các trọng số tích lũy giúp tiết kiệm công việc.

Vì vậy, việc cung cấp weights=[12, 12, 4]hoặc cum_weights=[12, 24, 28]cho trường hợp giả định của chúng tôi tạo ra kết quả tương tự và trường hợp sau có vẻ nhanh hơn / hiệu quả hơn.


11

Đây là phiên bản đang được đưa vào thư viện chuẩn cho Python 3.6:

import itertools as _itertools
import bisect as _bisect

class Random36(random.Random):
    "Show the code included in the Python 3.6 version of the Random class"

    def choices(self, population, weights=None, *, cum_weights=None, k=1):
        """Return a k sized list of population elements chosen with replacement.

        If the relative weights or cumulative weights are not specified,
        the selections are made with equal probability.

        """
        random = self.random
        if cum_weights is None:
            if weights is None:
                _int = int
                total = len(population)
                return [population[_int(random() * total)] for i in range(k)]
            cum_weights = list(_itertools.accumulate(weights))
        elif weights is not None:
            raise TypeError('Cannot specify both weights and cumulative weights')
        if len(cum_weights) != len(population):
            raise ValueError('The number of weights does not match the population')
        bisect = _bisect.bisect
        total = cum_weights[-1]
        return [population[bisect(cum_weights, random() * total)] for i in range(k)]

Nguồn: https://hg.python.org/cpython/file/tip/Lib/random.py#l340


2
import numpy as np
w=np.array([ 0.4,  0.8,  1.6,  0.8,  0.4])
np.random.choice(w, p=w/sum(w))

2

Tôi có lẽ đã quá muộn để đóng góp bất cứ điều gì hữu ích, nhưng đây là một đoạn đơn giản, ngắn và rất hiệu quả:

def choose_index(probabilies):
    cmf = probabilies[0]
    choice = random.random()
    for k in xrange(len(probabilies)):
        if choice <= cmf:
            return k
        else:
            cmf += probabilies[k+1]

Không cần phải sắp xếp xác suất của bạn hoặc tạo một vectơ với cmf của bạn và nó chấm dứt một khi nó tìm thấy sự lựa chọn của mình. Bộ nhớ: O (1), thời gian: O (N), với thời gian chạy trung bình ~ N / 2.

Nếu bạn có trọng lượng, chỉ cần thêm một dòng:

def choose_index(weights):
    probabilities = weights / sum(weights)
    cmf = probabilies[0]
    choice = random.random()
    for k in xrange(len(probabilies)):
        if choice <= cmf:
            return k
        else:
            cmf += probabilies[k+1]

1
Một số điều là sai với điều này. Nhìn bề ngoài, có một số tên biến được đánh máy và không có lý do nào được đưa ra cho việc sử dụng này np.random.choice. Nhưng thú vị hơn, có một chế độ thất bại trong đó điều này làm phát sinh một ngoại lệ. Làm probabilities = weights / sum(weights)không đảm bảo rằng probabilitiessẽ tổng hợp tới 1; Ví dụ, nếu weights[1,1,1,1,1,1,1]sau đó probabilitiessẽ chỉ tổng hợp để ,9999999999999998, nhỏ hơn so với giá trị trả về có thể lớn nhất củarandom.random (đó là ,9999999999999999). Sau đó choice <= cmflà không bao giờ được hài lòng.
Mark Amery

2

Nếu danh sách các lựa chọn có trọng số của bạn tương đối tĩnh và bạn muốn lấy mẫu thường xuyên, bạn có thể thực hiện một bước tiền xử lý O (N), sau đó thực hiện lựa chọn trong O (1), sử dụng các hàm trong câu trả lời liên quan này .

# run only when `choices` changes.
preprocessed_data = prep(weight for _,weight in choices)

# O(1) selection
value = choices[sample(preprocessed_data)][0]

1

Tôi đã xem chủ đề khác và đưa ra biến thể này trong phong cách mã hóa của mình, điều này trả về chỉ số lựa chọn cho mục đích kiểm đếm, nhưng thật đơn giản để trả về chuỗi (thay thế trả về nhận xét):

import random
import bisect

try:
    range = xrange
except:
    pass

def weighted_choice(choices):
    total, cumulative = 0, []
    for c,w in choices:
        total += w
        cumulative.append((total, c))
    r = random.uniform(0, total)
    # return index
    return bisect.bisect(cumulative, (r,))
    # return item string
    #return choices[bisect.bisect(cumulative, (r,))][0]

# define choices and relative weights
choices = [("WHITE",90), ("RED",8), ("GREEN",2)]

tally = [0 for item in choices]

n = 100000
# tally up n weighted choices
for i in range(n):
    tally[weighted_choice(choices)] += 1

print([t/sum(tally)*100 for t in tally])

1

Nó phụ thuộc vào số lần bạn muốn lấy mẫu phân phối.

Giả sử bạn muốn lấy mẫu phân phối K lần. Sau đó, độ phức tạp thời gian sử dụng np.random.choice()mỗi lần là O(K(n + log(n)))khin số lượng vật phẩm trong phân phối.

Trong trường hợp của tôi, tôi cần lấy mẫu phân phối giống nhau nhiều lần theo thứ tự 10 ^ 3 trong đó n là thứ tự 10 ^ 6. Tôi đã sử dụng mã dưới đây, tiền mã hóa phân phối tích lũy và lấy mẫu O(log(n)). Tổng thể thời gian phức tạp là O(n+K*log(n)).

import numpy as np

n,k = 10**6,10**3

# Create dummy distribution
a = np.array([i+1 for i in range(n)])
p = np.array([1.0/n]*n)

cfd = p.cumsum()
for _ in range(k):
    x = np.random.uniform()
    idx = cfd.searchsorted(x, side='right')
    sampled_element = a[idx]

1

Nếu bạn tình cờ có Python 3 và sợ cài đặt numpyhoặc viết các vòng lặp của riêng bạn, bạn có thể làm:

import itertools, bisect, random

def weighted_choice(choices):
   weights = list(zip(*choices))[1]
   return choices[bisect.bisect(list(itertools.accumulate(weights)),
                                random.uniform(0, sum(weights)))][0]

Bởi vì bạn có thể xây dựng bất cứ thứ gì từ một túi bộ điều hợp ống nước! Mặc dù ... tôi phải thừa nhận rằng câu trả lời của Ned, trong khi hơi dài hơn, lại dễ hiểu hơn.


0

Một giải pháp chung:

import random
def weighted_choice(choices, weights):
    total = sum(weights)
    treshold = random.uniform(0, total)
    for k, weight in enumerate(weights):
        total -= weight
        if total < treshold:
            return choices[k]

0

Đây là một phiên bản khác của weighted_choice sử dụng numpy. Vượt qua trong vectơ trọng số và nó sẽ trả về một mảng 0 chứa 1 cho biết thùng nào được chọn. Mã mặc định chỉ thực hiện một lần rút nhưng bạn có thể vượt qua số lần rút được thực hiện và số lượng trên mỗi thùng được rút sẽ được trả về.

Nếu vectơ trọng số không bằng 1, nó sẽ được chuẩn hóa.

import numpy as np

def weighted_choice(weights, n=1):
    if np.sum(weights)!=1:
        weights = weights/np.sum(weights)

    draws = np.random.random_sample(size=n)

    weights = np.cumsum(weights)
    weights = np.insert(weights,0,0.0)

    counts = np.histogram(draws, bins=weights)
    return(counts[0])

0

Một cách khác để làm điều này, giả sử chúng ta có trọng số ở cùng chỉ số với các phần tử trong mảng phần tử.

import numpy as np
weights = [0.1, 0.3, 0.5] #weights for the item at index 0,1,2
# sum of weights should be <=1, you can also divide each weight by sum of all weights to standardise it to <=1 constraint.
trials = 1 #number of trials
num_item = 1 #number of items that can be picked in each trial
selected_item_arr = np.random.multinomial(num_item, weights, trials)
# gives number of times an item was selected at a particular index
# this assumes selection with replacement
# one possible output
# selected_item_arr
# array([[0, 0, 1]])
# say if trials = 5, the the possible output could be 
# selected_item_arr
# array([[1, 0, 0],
#   [0, 0, 1],
#   [0, 0, 1],
#   [0, 1, 0],
#   [0, 0, 1]])

Bây giờ hãy giả sử, chúng ta phải lấy ra 3 mục trong 1 thử nghiệm. Bạn có thể giả sử rằng có ba quả bóng R, G, B có số lượng lớn theo tỷ lệ trọng lượng của chúng được đưa ra bởi mảng trọng lượng, điều sau đây có thể xảy ra:

num_item = 3
trials = 1
selected_item_arr = np.random.multinomial(num_item, weights, trials)
# selected_item_arr can give output like :
# array([[1, 0, 2]])

bạn cũng có thể nghĩ số lượng vật phẩm được chọn là số lượng thử nghiệm nhị thức / đa thức trong một bộ. Vì vậy, ví dụ trên có thể vẫn hoạt động như

num_binomial_trial = 5
weights = [0.1,0.9] #say an unfair coin weights for H/T
num_experiment_set = 1
selected_item_arr = np.random.multinomial(num_binomial_trial, weights, num_experiment_set)
# possible output
# selected_item_arr
# array([[1, 4]])
# i.e H came 1 time and T came 4 times in 5 binomial trials. And one set contains 5 binomial trails.

0

Có bài giảng về điều này của Sebastien Thurn trong khóa học Udacity miễn phí AI cho Robotics. Về cơ bản, anh ta tạo ra một mảng tròn của các trọng số được lập chỉ mục bằng cách sử dụng toán tử mod% , đặt một biến beta thành 0, chọn ngẫu nhiên một chỉ số, cho các vòng lặp qua N trong đó N là số chỉ số và trong vòng lặp đầu tiên tăng beta theo công thức:

beta = beta + mẫu thống nhất từ ​​{0 ... 2 * Trọng lượng_max}

và sau đó lồng trong vòng lặp for, một vòng lặp while bên dưới:

while w[index] < beta:
    beta = beta - w[index]
    index = index + 1

select p[index]

Sau đó, vào chỉ mục tiếp theo để lấy mẫu lại dựa trên xác suất (hoặc xác suất chuẩn hóa trong trường hợp được trình bày trong khóa học).

Liên kết bài giảng: https://grouproom.udacity.com/cifts/cs373/lessons/48704330/con accept / 487480820923

Tôi đang đăng nhập vào Udacity bằng tài khoản của trường, vì vậy nếu liên kết không hoạt động, đó là Bài 8, video số 21 của Trí tuệ nhân tạo cho Robotics, nơi anh ấy đang giảng về các bộ lọc hạt.


-1

Một cách là chọn ngẫu nhiên trên tổng tất cả các trọng số và sau đó sử dụng các giá trị làm điểm giới hạn cho mỗi var. Đây là một thực hiện thô như là một máy phát điện.

def rand_weighted(weights):
    """
    Generator which uses the weights to generate a
    weighted random values
    """
    sum_weights = sum(weights.values())
    cum_weights = {}
    current_weight = 0
    for key, value in sorted(weights.iteritems()):
        current_weight += value
        cum_weights[key] = current_weight
    while True:
        sel = int(random.uniform(0, 1) * sum_weights)
        for key, value in sorted(cum_weights.iteritems()):
            if sel < value:
                break
        yield key

-1

Sử dụng numpy

def choice(items, weights):
    return items[np.argmin((np.cumsum(weights) / sum(weights)) < np.random.rand())]

NumPy đã có np.random.choice, như đã đề cập trong câu trả lời được chấp nhận có ở đây từ năm 2014. Điểm quan trọng của bạn là gì?
Đánh dấu Amery

-1

Tôi cần phải làm một cái gì đó như thế này thực sự nhanh chóng thực sự đơn giản, từ việc tìm kiếm ý tưởng cuối cùng tôi đã xây dựng mẫu này. Ý tưởng được nhận các giá trị trọng số dưới dạng json từ api, ở đây được mô phỏng bởi dict.

Sau đó dịch nó thành một danh sách trong đó mỗi giá trị lặp lại tỷ lệ thuận với trọng số của nó và chỉ cần sử dụng Random.choice để chọn một giá trị từ danh sách.

Tôi đã thử nó chạy với 10, 100 và 1000 lần lặp. Sự phân phối có vẻ khá vững chắc.

def weighted_choice(weighted_dict):
    """Input example: dict(apples=60, oranges=30, pineapples=10)"""
    weight_list = []
    for key in weighted_dict.keys():
        weight_list += [key] * weighted_dict[key]
    return random.choice(weight_list)

-1

Tôi không thích cú pháp của bất kỳ ai trong số đó. Tôi thực sự muốn chỉ định các mục là gì và trọng số của từng mục. Tôi nhận ra tôi có thể đã sử dụng random.choicesnhưng thay vào đó tôi đã nhanh chóng viết lớp bên dưới.

import random, string
from numpy import cumsum

class randomChoiceWithProportions:
    '''
    Accepts a dictionary of choices as keys and weights as values. Example if you want a unfair dice:


    choiceWeightDic = {"1":0.16666666666666666, "2": 0.16666666666666666, "3": 0.16666666666666666
    , "4": 0.16666666666666666, "5": .06666666666666666, "6": 0.26666666666666666}
    dice = randomChoiceWithProportions(choiceWeightDic)

    samples = []
    for i in range(100000):
        samples.append(dice.sample())

    # Should be close to .26666
    samples.count("6")/len(samples)

    # Should be close to .16666
    samples.count("1")/len(samples)
    '''
    def __init__(self, choiceWeightDic):
        self.choiceWeightDic = choiceWeightDic
        weightSum = sum(self.choiceWeightDic.values())
        assert weightSum == 1, 'Weights sum to ' + str(weightSum) + ', not 1.'
        self.valWeightDict = self._compute_valWeights()

    def _compute_valWeights(self):
        valWeights = list(cumsum(list(self.choiceWeightDic.values())))
        valWeightDict = dict(zip(list(self.choiceWeightDic.keys()), valWeights))
        return valWeightDict

    def sample(self):
        num = random.uniform(0,1)
        for key, val in self.valWeightDict.items():
            if val >= num:
                return key

-1

Cung cấp Random.choice () với danh sách có trọng số trước:

Giải pháp & Kiểm tra:

import random

options = ['a', 'b', 'c', 'd']
weights = [1, 2, 5, 2]

weighted_options = [[opt]*wgt for opt, wgt in zip(options, weights)]
weighted_options = [opt for sublist in weighted_options for opt in sublist]
print(weighted_options)

# test

counts = {c: 0 for c in options}
for x in range(10000):
    counts[random.choice(weighted_options)] += 1

for opt, wgt in zip(options, weights):
    wgt_r = counts[opt] / 10000 * sum(weights)
    print(opt, counts[opt], wgt, wgt_r)

Đầu ra:

['a', 'b', 'b', 'c', 'c', 'c', 'c', 'c', 'd', 'd']
a 1025 1 1.025
b 1948 2 1.948
c 5019 5 5.019
d 2008 2 2.008
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.