加重セットからランダムなアイテムをいくつか選ぶ必要があります。重量が大きいアイテムは、選択される可能性が高くなります。抽選でモデル化することにしました。私のソリューションは良いC++になると思いますが、良いpythonにはならないでしょう。
これを行うためのPythonの方法は何ですか?
def _lottery_winners_by_participants_and_ticket_counts(participants_and_ticket_counts, number_of_winners):
"""
Returns a list of winning participants in a lottery. In this lottery,
participant can have multiple tickets, and participants can only win
once.
participants_and_ticket_counts is a list of (participant, ticket_count)
number_of_winners is the maximum number of lottery winners
"""
if len(participants_and_ticket_counts) <= number_of_winners:
return [p for (p, _) in participants_and_ticket_counts]
winners = []
for _ in range(number_of_winners):
total_tickets = sum(tc for (_, tc) in participants_and_ticket_counts)
winner = random.randrange(0, total_tickets)
ticket_count_offset = 0
for participant_ticket_count in participants_and_ticket_counts:
(participant, ticket_count) = participant_ticket_count
if winner < ticket_count + ticket_count_offset:
winners.append(participant)
participants_and_ticket_counts.remove(participant_ticket_count)
break
ticket_count_offset += ticket_count
return winners
編集:申し訳ありませんが、これを以前に忘れましたが、重みは数千の整数である可能性があります。
編集: @Floのコメントに基づいた最終的な解決策があると思います
ノート
私はPython2.7で作業しているので、独自のaccumulate()を作成しました。Python 3のaccumulate()とは動作が異なります(そして私はより良いと思います)。私のバージョンは、add関数に基づいて反復可能なタプルから蓄積できます。
また、participants_and_ticket_countsは変更可能なリストであり、_lottery_winners_by_participants_and_ticket_counts()が呼び出された後は使用されないという特別な知識もあります。だから私はそれをpop()することができます。
これが私の解決策です:
def _lottery_winners_by_participants_and_ticket_counts(participants_and_ticket_counts, number_of_winners):
"""
Returns a list of winning participants in a lottery. In this lottery,
participant can have multiple tickets, and participants can only win once.
participants_and_ticket_counts is a list of (participant, ticket_count)
number_of_winners is the maximum number of lottery winners
"""
def _accumulate(iterable, func):
total = 0
for element in iterable:
total = func(total, element)
yield total
if len(participants_and_ticket_counts) <= number_of_winners:
return list(winner for (winner, _) in participants_and_ticket_counts)
winners = list()
for _ in range(number_of_winners):
accumulation = list(_accumulate(participants_and_ticket_counts, lambda total, ptc: total + ptc[1]))
winning_number = random.randrange(0, accumulation[-1])
index_of_winner = bisect.bisect(accumulation, winning_number)
(winner, _) = participants_and_ticket_counts.pop(index_of_winner)
winners.append(winner)
return winners
みんなの助けに感謝します!