我有一个程序,我使用collections.Counter
跟踪各种事物的成功 - thing 的每次成功都会增加相应的计数器:
import collections
scoreboard = collections.Counter()
if test(thing):
scoreboard[thing]+ = 1
然后,为了将来的测试,我想倾向于那些已经取得最大成功的 thing 。 Counter.elements()
似乎是理想的,因为它返回的元素(以任意顺序)重复多次等于计数。所以我想我可以这样做:
import random
nextthing=random.choice(scoreboard.elements())
但不,那引发 TypeError:'itertools.chain'类型的对象没有len()。好的,random.choice
can't work with iterators。但是,在这种情况下,长度是已知的(或可知的) - 它是sum(scoreboard.values())
。
我知道迭代未知长度列表并随机选择元素的基本算法,但我怀疑有更优雅的东西。我该怎么办?
答案 0 :(得分:8)
使用itertools.islice
获取可迭代的第N个项目,您可以相当轻松地执行此操作:
>>> import random
>>> import itertools
>>> import collections
>>> c = collections.Counter({'a': 2, 'b': 1})
>>> i = random.randrange(sum(c.values()))
>>> next(itertools.islice(c.elements(), i, None))
'a'
答案 1 :(得分:4)
您可以在list()
中包装迭代器,将其转换为random.choice()
的列表:
nextthing = random.choice(list(scoreboard.elements()))
这里的缺点是,这会扩展内存中的列表,而不是像通常使用迭代器那样逐项访问它。
如果你想迭代地解决这个问题,this algorithm可能是个不错的选择。
答案 2 :(得分:3)
以下内容将获得一个随机项目,其中分数是返回该项目的频率的权重。
import random
def get_random_item_weighted(scoreboard):
total_scoreboard_value = sum(scoreboard.values())
item_loc = random.random() * total_scoreboard_value
current_loc = 0
for item, score in scoreboard.items():
current_loc += score
if current_loc > item_loc:
return item
例如,如果有2个项目:
item1的得分为5
item2的得分为10
item2的返回次数是item1的两倍
答案 3 :(得分:1)
迭代的另一种变体:
import collections
from collections import Counter
import random
class CounterElementsRandomAccess(collections.Sequence):
def __init__(self, counter):
self._counter = counter
def __len__(self):
return sum(self._counter.values())
def __getitem__(self, item):
for i, el in enumerate(self._counter.elements()):
if i == item:
return el
scoreboard = Counter('AAAASDFQWERQWEQWREAAAAABBBBCCDDVBSDF')
score_elements = CounterElementsRandomAccess(scoreboard)
for i in range(10):
print random.choice(score_elements)
答案 4 :(得分:1)
另一种变体, 安装有点麻烦,但查找是对数复杂的(适用于需要多次查找时):
import itertools
import random
from collections import Counter
from bisect import bisect
counter = Counter({"a": 5, "b": 1, "c": 1})
#setup
most_common = counter.most_common()
accumulated = list(itertools.accumulate([x[1] for x in most_common])) # i.e. [5, 6, 7]
total_size = accumulated[-1]
# lookup
i = random.randrange(total_size)
print(most_common[bisect(accumulated, i)])
答案 5 :(得分:1)
这里有很多过时的答案。
给出具有相应相对概率的选择字典(在您的情况下可以算),您可以使用Python 3.6中添加的新的 random.choices ,如下所示:
add_filter( 'woocommerce_quantity_input_min','woocommerce_quantity_input_min_callback', 10, 2 );
function woocommerce_quantity_input_min_callback( $min, $product ) {
$min = 6;
return $min;
}