Moved to stats.stackexchange
I am trying to write a function that deals cards to players given a probability distribution. This can be thought of as a dealer who is cheating and wants to give certain cards to certain players but doesn't want to be obvious.
For example with a three card deck and three players:
| Ace | King | Queen | |
|---|---|---|---|
| Player 1 | 0% | 66% | 33% |
| Player 2 | 33% | 33% | 33% |
| Player 3 | 66% | 0% | 33% |
Choosing a card based on weights is not the problem. The problem is that when a card is chosen then that card cannot be at another player.
Choosing one by one can lead to situations when the remaining player has 0% probability to get the remaining card. In the above example - what if Player 1 gets a Queen, Player 2 gets an Ace. Then Player 3 cannot get any card as his King probability is 0%.
One way I tried is to deal the cards per player. Then check if there are duplicates and if there are I deal again till there are no duplicate cards. The resulting distribution does not follow what I want.
The reason for this (in the example above) is that the re-dealing would happen more often for Player 1 getting Queen because the chance of a duplicate in that case is 66% unlike when Player 1 gets a King (33%). This skews the resulting distribution.
Another way that I tried is to deal Player 1's card. Then remove that from the possible Player 2 cards and deal a card out of that for Player 2. The card for player 3 is the remaining card. This I saw is clearly stupid because Player 3 even had Kings which he should have with 0% probability.
Here is the code for the second version I mentioned:
import random
# returns a list of three cards:
# 0 is an Ace, 1 is a King and 2 is a Queen
def sample(probabilities):
p1_card = random.choices([0, 1, 2], weights=probabilities[0])[0]
p2_possible_cards = [0, 1, 2]
p2_weights = probabilities[1][:]
p2_possible_cards.pop(p1_card)
p2_weights.pop(p1_card)
p2_card = random.choices(p2_possible_cards, weights=p2_weights)[0]
if 0 not in [p1_card, p2_card]:
p3_card = 0
elif 1 not in [p1_card, p2_card]:
p3_card = 1
else:
p3_card = 2
return [p1_card, p2_card, p3_card]
probabilities = [
[0.0, 0.666666, 0.333333],
[0.333333, 0.333333, 0.333333],
[0.666666, 0.0, 0.333333],
]
distribution = [
[0.0, 0.0, 0.0],
[0.0, 0.0, 0.0],
[0.0, 0.0, 0.0],
]
for _ in range(100_000):
for card, dist in zip(sample(probabilities), distribution):
dist[card] += 1
distribution = [[card / 100_000 for card in player] for player in distribution]
# distribution is:
# [
# [0.0 , 0.66734, 0.33266],
# [0.50087, 0.16556, 0.33357],
# [0.49913, 0.16710, 0.33377],
# ]
# which is not even close to probabilities