algorithm - What's the pythonic way to run a lottery? -
i need pick several random items weighted set. items higher weight more picked. decided model after lottery. feel solution makes c++, don't think makes python.
what's pythonic way of doing this?
def _lottery_winners_by_participants_and_ticket_counts(participants_and_ticket_counts, number_of_winners): """ returns list of winning participants in lottery. in lottery, participant can have multiple tickets, , participants can win once. participants_and_ticket_counts list of (participant, ticket_count) number_of_winners maximum number of lottery winners """ if len(participants_and_ticket_counts) <= number_of_winners: return [p (p, _) in participants_and_ticket_counts] winners = [] _ in range(number_of_winners): total_tickets = sum(tc (_, tc) in participants_and_ticket_counts) winner = random.randrange(0, total_tickets) ticket_count_offset = 0 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 edit: sorry forgot earlier, weight integer in thousands.
edit: think have final solution based on comment of @flo
notes
i'm working in python 2.7, created own accumulate(). works differently (and think better) accumulate() in python 3. version can accumulate iterable of tuples based on add function.
i have special knowledge participants_and_ticket_counts mutable list , not used after _lottery_winners_by_participants_and_ticket_counts() called. that's why can pop() it.
here's solution:
def _lottery_winners_by_participants_and_ticket_counts(participants_and_ticket_counts, number_of_winners): """ returns list of winning participants in lottery. in lottery, participant can have multiple tickets, , participants can win once. participants_and_ticket_counts list of (participant, ticket_count) number_of_winners maximum number of lottery winners """ def _accumulate(iterable, func): total = 0 element in iterable: total = func(total, element) yield total if len(participants_and_ticket_counts) <= number_of_winners: return list(winner (winner, _) in participants_and_ticket_counts) winners = list() _ 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 thanks help!
numpy.random.choice has nice solution this. here's how can use it:
>>> import numpy np >>> numpy.random import choice >>> names = ['harry', 'sally', 'joe', 'bob', 'angela', 'jack', 'jill', 'jeff'] >>> weights = [1,4,6,3,5,7,10,14] >>> p = np.array(weights, dtype=float) / sum(weights) >>> p array([ 0.02, 0.08, 0.12, 0.06, 0.1 , 0.14, 0.2 , 0.28]) >>> choice(names, size=5, p=p) array(['jill', 'jack', 'jeff', 'jeff', 'angela'], dtype='|s6') >>> choice(names, size=5, p=p) array(['jill', 'jack', 'joe', 'jill', 'sally'], dtype='|s6') >>> choice(names, size=5, p=p) array(['jack', 'angela', 'joe', 'sally', 'jill'], dtype='|s6') however, function added in numpy 1.7. if have older version, can copy function: http://pastebin.com/f5gti0qj
Comments
Post a Comment