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% |
The input of my function is the probabilities and the output is the cards dealt represented as:
probabilities = [
[0.0 , 0.6666, 0.3333],
[0.3333, 0.3333, 0.3333],
[0.6666, 0.0 , 0.3333],
]
# example result:
result = [
1, # Player 1 - King
2, # Player 2 - Queen
0, # Player 3 - Ace
]
I use a weighted version of random choice to sample according to the rows of probabilities. But that is not the problem.
The problem is that a card can only be at a single player so just using weighted choice doesn't work.
I tried a few ways of dealing the cards but the resulting distribution is not the input distribution I used as a parameter in this case for example.
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.
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.
Both of these gave the wrong distribution of cards when averaging together loads of runs.
How is it possible to pick random numbers like this?
If I only had to pick a card for a single player I would just use some kind of weighted choice like it was suggested. But that doesn't work when there are multiple cards to choose because when a card is picked that card cannot possible be at the other players.
Here is the code for the second version I mentioned:
import random
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]
print(distribution)
# prints:
# [[0.0, 0.66734, 0.33266], [0.50087, 0.16556, 0.33357], [0.49913, 0.1671, 0.33377]]
# which is not even close to probabilities
Aucun commentaire:
Enregistrer un commentaire