16

私は、さまざまなことの成功を追跡するプログラムを持っています。物事が成功するcollections.Counterたびに、対応するカウンターがインクリメントされます。

import collections
scoreboard = collections.Counter()

if test(thing):
    scoreboard[thing]+ = 1

次に、今後のテストのために、最も成功したものに偏らせたいと思います。Counter.elements()カウントに等しい回数繰り返された要素を(任意の順序で)返すため、これには理想的であるように思われました。だから私はただできると思った:

import random
nextthing=random.choice(scoreboard.elements())

しかし、それはTypeError: object of type 'itertools.chain' has no len() を発生させます。わかりましたのでrandom.choice、 iterators では動作しません。しかし、この場合、長さはわかっている (またはわかっている) — それはsum(scoreboard.values()).

長さが不明なリストを繰り返し処理し、要素をランダムに選択するための基本的なアルゴリズムは知っていますが、もっと洗練されたものがあるのではないかと思います。私はここで何をすべきですか?

4

6 に答える 6

7

itertools.isliceiterable の 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'
于 2012-01-31T19:06:58.183 に答える
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 の 2 倍の頻度で返されます

于 2012-01-31T18:44:56.343 に答える
3

イテレータをラップして、次list()のリストに変換できrandom.choice()ます。

nextthing = random.choice(list(scoreboard.elements()))

ここでの欠点は、イテレータで通常取得するようにアイテムごとにリストにアクセスするのではなく、メモリ内のリストを展開することです。

これを繰り返し解決したい場合は、おそらくこのアルゴリズムが適しています。

于 2012-01-31T18:12:16.863 に答える
2

別のバリアントであるセットアップは少し面倒ですが、ルックアップは対数的に複雑です (複数のルックアップが必要な場合に適しています)。

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)])
于 2017-07-11T13:20:45.283 に答える
0

反復を伴う別のバリアント:

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)
于 2012-01-31T19:10:46.843 に答える