Walkthrough of Probably Dice 题目需求

``````def probability(dice_number, sides, target):
return 0.0
if __name__ == '__main__':
#These are only used for self-checking and are not necessary for auto-testing
def almost_equal(checked, correct, significant_digits=4):
precision = 0.1 ** significant_digits
return correct - precision < checked < correct + precision

assert(almost_equal(probability(2, 6, 3), 0.0556)), "Basic example"
assert(almost_equal(probability(2, 6, 4), 0.0833)), "More points"
assert(almost_equal(probability(2, 6, 7), 0.1667)), "Maximum for two 6-sided dice"
assert(almost_equal(probability(2, 3, 5), 0.2222)), "Small dice"
assert(almost_equal(probability(2, 3, 7), 0.0000)), "Never!"
assert(almost_equal(probability(3, 6, 7), 0.0694)), "Three dice"
assert(almost_equal(probability(10, 10, 50), 0.0375)), "Many dice, many sides"
``````

思路

``````[range(1, sides + 1)] * dice_number
``````

``````>>> [range(1, 6 + 1)] * 2

[[1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6]]
``````

Out:
[[1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6]]

• 我们生成的是包含两个子list的list。也就是说，里面只包含两个元素；
``````>>> import itertools
>>> [i for i in itertools.permutations([range(1, 6 + 1)] * 2)]

[([1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6]),
([1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6])]
``````
• 既是我们将列表做成一个包含12个元素的list，permutation会导致每个合理的组合出现两次。因为permutations并不区分两个dice，所以我们希望只得到(第一个骰子点数，第二个骰子点数)的情况，在permutations会出现（第二个骰子点数，第一个骰子点数）的情况。（千万不要直接运行，一共是12!个排列！）
``````[i for i in itertools.permutations(range(1, 6 + 1) * 2)]
``````

``````[i for i in itertools.product(*([range(1, 6 + 1)] * 2))]

[(1, 1),
(1, 2),
(1, 3),
(1, 4),
(1, 5),
(1, 6),
(2, 1),
(2, 2),
(2, 3),
(2, 4),
(2, 5),
(2, 6),
(3, 1),
(3, 2),
(3, 3),
(3, 4),
(3, 5),
(3, 6),
(4, 1),
(4, 2),
(4, 3),
(4, 4),
(4, 5),
(4, 6),
(5, 1),
(5, 2),
(5, 3),
(5, 4),
(5, 5),
(5, 6),
(6, 1),
(6, 2),
(6, 3),
(6, 4),
(6, 5),
(6, 6)]
``````

``````from itertools import product
def probability(dice_number, sides, target):
combinations = [i
for i in product(*([range(1, sides + 1)] * dice_number))
if sum(i) == target]
return round(len(combinations) / (sides ** dice_number), 4)
``````

``````from __future__ import division
def almost_equal(checked, correct, significant_digits=4):
precision = 0.1 ** significant_digits
return correct - precision < checked < correct + precision

%time assert(almost_equal(probability(2, 6, 3), 0.0556)), "Basic example"
%time assert(almost_equal(probability(2, 6, 4), 0.0833)), "More points"
%time assert(almost_equal(probability(2, 6, 7), 0.1667)), "Maximum for two 6-sided dice"
%time assert(almost_equal(probability(2, 3, 5), 0.2222)), "Small dice"
%time assert(almost_equal(probability(2, 3, 7), 0.0000)), "Never!"
%time assert(almost_equal(probability(3, 6, 7), 0.0694)), "Three dice"
CPU times: user 45 µs, sys: 1 µs, total: 46 µs
Wall time: 52 µs
CPU times: user 45 µs, sys: 1 µs, total: 46 µs
Wall time: 51 µs
CPU times: user 44 µs, sys: 1 µs, total: 45 µs
Wall time: 49.8 µs
CPU times: user 32 µs, sys: 1 µs, total: 33 µs
Wall time: 38.9 µs
CPU times: user 21 µs, sys: 1e+03 ns, total: 22 µs
Wall time: 26 µs
CPU times: user 97 µs, sys: 1e+03 ns, total: 98 µs
Wall time: 103 µs
``````

思路v2

``````from math import factorial
from itertools import combinations_with_replacement, groupby
def probability(dice_number, sides, target):
counter = 0
for i in combinations_with_replacement(range(1, sides + 1), dice_number):
if sum(i) == target:
itemCount = [len([k for k in j]) for _, j in groupby(i)]
counter += factorial(len(i)) / reduce(lambda x, y: x * y,
map(factorial, itemCount))
return round(counter / (sides ** dice_number), 4)
``````

``````%time assert(almost_equal(probability(2, 6, 3), 0.0556)), "Basic example"
%time assert(almost_equal(probability(2, 6, 4), 0.0833)), "More points"
%time assert(almost_equal(probability(2, 6, 7), 0.1667)), "Maximum for two 6-sided dice"
%time assert(almost_equal(probability(2, 3, 5), 0.2222)), "Small dice"
%time assert(almost_equal(probability(2, 3, 7), 0.0000)), "Never!"
%time assert(almost_equal(probability(3, 6, 7), 0.0694)), "Three dice"
%time assert(almost_equal(probability(10, 10, 50), 0.0375)), "Many dice, many sides"
CPU times: user 66 µs, sys: 12 µs, total: 78 µs
Wall time: 83.9 µs
CPU times: user 52 µs, sys: 1 µs, total: 53 µs
Wall time: 57.9 µs
CPU times: user 56 µs, sys: 0 ns, total: 56 µs
Wall time: 62 µs
CPU times: user 31 µs, sys: 1e+03 ns, total: 32 µs
Wall time: 301 µs
CPU times: user 19 µs, sys: 1 µs, total: 20 µs
Wall time: 23.8 µs
CPU times: user 83 µs, sys: 1e+03 ns, total: 84 µs
Wall time: 88 µs
CPU times: user 74.7 ms, sys: 8.61 ms, total: 83.3 ms
Wall time: 98.5 ms
``````

``````from math import factorial
from itertools import combinations_with_replacement, groupby

def probability(dice_number, sides, target):
counter = 0
for i in combinations_with_replacement(range(1, sides + 1), dice_number):
if sum(i) == target:
itemCount = [len([k for k in j]) for _, j in groupby(i)]
counter += factorial(len(i)) * 1.0 / reduce(lambda x, y: x * y,
map(factorial,
itemCount))
return round(counter * 1.0 / (sides ** dice_number), 4)
if __name__ == '__main__':
# These are only used for self-checking and are not necessary for
# auto-testing
def almost_equal(checked, correct, significant_digits=4):
precision = 0.1 ** significant_digits
return correct - precision < checked < correct + precision
assert(almost_equal(probability(2, 6, 3), 0.0556)), "Basic example"
assert(almost_equal(probability(2, 6, 4), 0.0833)), "More points"
assert(almost_equal(probability(2, 6, 7), 0.1667)
), "Maximum for two 6-sided dice"
assert(almost_equal(probability(2, 3, 5), 0.2222)), "Small dice"
assert(almost_equal(probability(2, 3, 7), 0.0000)), "Never!"
assert(almost_equal(probability(3, 6, 7), 0.0694)), "Three dice"
assert(almost_equal(probability(10, 10, 50), 0.0375)
), "Many dice, many sides"
``````