So here are the rules
I feel confident that this will give the correct deck sizes
import itertools
# define base deck
base_deck = [2, 3, 4, 5, 6, 7, 8, 9, 10]
# define deck sizes
deck_a_size = 2
deck_b_size = 3
# create decks
deck_a_cards = list(itertools.chain.from_iterable(itertools.repeat(base_deck, deck_a_size)))
deck_a_cards.extend(floater)
print(deck_a_cards)
deck_b_cards = list(itertools.chain.from_iterable(itertools.repeat(base_deck, deck_b_size)))
deck_a_cards.extend(floater)
print(deck_b_cards)
However, I am not sure if this will calculate the correct probabilities
for and a, b
import itertools
# define base deck
base_deck = [2, 3, 4, 5, 6, 7, 8, 9, 10]
# define deck sizes
deck_a_size = 1
deck_b_size = 3
# create decks
deck_a_cards = list(itertools.chain.from_iterable(itertools.repeat(base_deck, deck_a_size)))
deck_a_cards.extend(floater)
print(deck_a_cards)
deck_b_cards = list(itertools.chain.from_iterable(itertools.repeat(base_deck, deck_b_size)))
deck_b_cards.extend(floater)
print(deck_b_cards)
deck_a_success = 0
deck_b_success = 0
total_combinations = 0
for i in itertools.product(deck_a_cards, repeat=deck_a_size):
for j in itertools.product(deck_b_cards, repeat=deck_b_size):
deck_a_sum = sum(i)
deck_b_sum = sum(j)
if deck_a_sum > deck_b_sum:
deck_a_success += 1
elif deck_a_sum < deck_b_sum:
deck_b_success += 1
else:
deck_b_success += 1
total_combinations += 1
deck_a_prob = deck_a_success / total_combinations
deck_b_prob = deck_b_success / total_combinations
print("Probability of success with {} cards drawn:".format(deck_a_size + deck_b_size))
print("Deck A:", deck_a_prob)
print("Deck B:", deck_b_prob)
And finally there has to be a better way to do this, because this is horrendously poor time complexity.
Ok I have now also tried with Monte-Carlo simulations
for both size 2,3 and 1,3 I get the very similar values:
import random
base_deck = [2, 3, 4, 5, 6, 7, 8, 9, 10]
deck_a_size = 2
deck_b_size = 3
num_trials = 1000000
deck_a_wins = 0
deck_b_wins = 0
for i in range(num_trials):
deck_a_sum = sum(random.sample(base_deck, deck_a_size))
deck_b_sum = sum(random.sample(base_deck * deck_b_size, deck_b_size))
if deck_a_sum > deck_b_sum:
deck_a_wins += 1
elif deck_b_sum > deck_a_sum:
deck_b_wins += 1
deck_a_prob = deck_a_wins / num_trials
deck_b_prob = deck_b_wins / num_trials
print("Probability of success with {} cards drawn:".format(deck_a_size+deck_b_size))
print("Deck A:", deck_a_prob)
print("Deck B:", deck_b_prob)
Probability of success with 5 cards drawn: Deck A: 0.1316926655146557 Deck B: 0.8683073344853444 Run time 6.0s
With Monte-Calro
Probability of success with 5 cards drawn: Deck A: 0.121357 Deck B: 0.837629 Runtime 3.8s
##resolved
You results seem to be correct.
Since your code is using product (not permutations or combinations) I assume that when multiple draws are made, the drawn card is placed back in the deck before the next draw. This allows a simplification of the process by building a list of probabilities for each possible sum of a given deck size.
Based on these probabilities, each possible sum from one deck can be compared to the ones of the other deck and yield an outcome probability that we can then sum up to get the final result:
from itertools import product
floaters = [7,8,9,10]
baseDeck = [2, 3, 4, 5, 6, 7, 8, 9, 10]
Bs = len(baseDeck)
Fs = len(floaters)
def getSumProb(size):
sump = prob = {n:(size+(n in floaters))/(size*Bs+Fs) for n in baseDeck}
for _ in range(1,size):
sump,prev = dict(),sump
for (s,ps),(n,pn) in product(prev.items(),prob.items()):
sump[s+n] = sump.get(s+n,0) + ps * pn
return sump
The getSumProb
function return a dictionary with possible sums as keys and their corresponding probabilities as value.
It first computes the probability of each individual number taking into account the repetitions and increased chance of the floater values. It then "folds" this list of individual probabilities over themselves to get sum values (for the multiple draws) and their respective probabilities (i.e. sum of probability products that form final sums).
To compute the probability of deckA draws winning over deckB's, we simply add up the probabilities of each possible sum of deckA draw multiplied by the probabilities of deckB sums that are inferior.
output:
sumsA = getSumProb(2)
sumsB = getSumProb(3)
winAprob = sum(sumsA[sA]*sumsB[sB] for sA,sB in product(sumsA,sumsB) if sA>sB)
winBprob = 1 - winAprob
print(winAprob) # 0.13169266551465567
print(winBprob) # 0.8683073344853444
# runs in 0.0002 sec. on my laptop
If you need to isolate the cases where the two values are equal, you can compute the probability of equal sums separately and deduce winBprob from that
equalProb = sum( pA*sumsB.get(sA,0) for sA,pA in sumsA.items() )
winBprob = 1 - winAprob - equalProb
print(equalProb) # 0.04061934507371048
print(winBprob) # 0.8276879894116339
Note that, if the draw process does not place back the drawn card in the deck before the next draw, the sum probabilities will need to be calculated differently. This, however, would only require a few changes to the getSumProb
function.