Original story: [I tried a stochastic simulation of a bingo game with Python] (http://qiita.com/elzup/items/d532ffa1d326fbf75d01)
It was interesting to read, so I modified it a little while looking at Python's manners.
if __name__ == '__main__':
main()
I like to make * main () *, but by separating the processing when it is executed as a script, you can start an interactive shell in another terminal etc. and import it as a module for debugging. Convenient.
For example, try changing the implementation of * generate_card () *
def generate_card():
@@ -25,12 +33,22 @@
- cards = []
- for i in range(5):
- cards.extend(sample(range(15 * i + 1, 15 * (i + 1)), 5))
- cards[12] = HIT
- return cards
+ def sampling(k=5):
+ for i in range(k):
+ yield sample(range(15 * i + 1, 15 * (i + 1) + 1), k)
+
+ from itertools import chain
+ card = list(chain.from_iterable(sampling()))
+ card[12] = HIT
+ return card
Measure performance with ipython.
$ ipython3-3.4
In [1]: import orig_Bingo
In [2]: timeit -n 100 orig_Bingo.generate_card()
100 loops, best of 3: 86.4 us per loop
In [3]: import Bingo_kai
In [4]: timeit -n 100 Bingo_kai.generate_card()
100 loops, best of 3: 95.3 us per loop
Use argparse to handle the arguments, as long as you use * try ... except *. The usage of argparse is also standard, so once you get used to it, it's easy. As an aside, I recently saw cli tools like click and clip. However, I don't really understand the purpose and necessity.
parser = argparse.ArgumentParser()
parser.set_defaults(num=10000, times=5, verbose=False)
parser.add_argument('-n', '--num', type=int, help='set card number')
parser.add_argument('-t', '--times', type=int, help='set times')
parser.add_argument(
'-v', '--verbose', action='store_true',
help='set verbose mode (loglevel=DEBUG)')
Since I tried to process the arguments, I will try to switch the debug output using logging. How to use the logging module is also standard.
logger = getLogger(__name__)
_handler = StreamHandler()
_handler.setLevel(DEBUG)
logger.addHandler(_handler)
...
logger.setLevel(DEBUG)
Actually, I just wanted to say this in response to the ↓ of the original article.
I still don't know how to write a doc block, so I'll study
One of the features of Python is doctest. If you combine the document of how to use the function with a simple test, it may be two birds with one stone.
If you modify the logic of the original code so as not to mess with it, the doctest of the modified code is not good, but if you try to develop while writing the doctest, the perspective of the position to use will expand and the function will be easy to use naturally. And modules can be implemented.
Postscript: I revenged because it didn't work => I wrote a doctest in "I tried to simulate the probability of a bingo game with Python"
"""
>>> check_bingo([False, True, False])
False
>>> check_bingo([True] * 5)
True
>>> check_bingo([False, False, True, False, False] * 5)
True
>>> check_bingo([
... True, False, False, False, False,
... False, True, False, False, False,
... False, False, True, False, False,
... False, False, False, True, False,
... False, False, False, False, True,
... ])
True
"""
I'm using * random.sample *, so it's difficult to write a test without a mock ...: sweat:
When I tried it, I couldn't fix it so well that it matched what I wanted to say ...: disappointed_relieved:
This code runs on Python3.
Bingo_kai.py
# -*- coding: utf-8 -*-
u"""Script that simulates the probability of BINGO
input:
times =Number of times to turn the lottery machine, card_num =Number of cards
outpu:
Probability of getting even one
"""
import argparse
import sys
from logging import DEBUG, StreamHandler, getLogger
from random import sample
#hole
HIT = True
NOHIT = False
#Log output
logger = getLogger(__name__)
_handler = StreamHandler()
_handler.setLevel(DEBUG)
logger.addHandler(_handler)
def generate_card():
u"""Generate card
5x5 center(3 rows 3 columns)Card with a hole in
B(1st row) 1 - 15
I(2nd row) 16 - 30
N(3rd row) 31 - 45
G(4th row) 46 - 60
O(5th row) 61 - 75
:returns: array card, length=25
>>> from math import floor
>>> card = generate_card()
>>> card_len = len(card)
>>> card_len == 25
True
>>> card[floor(card_len / 2)]
True
"""
def sampling(k=5):
for i in range(k):
yield sample(range(15 * i + 1, 15 * (i + 1) + 1), k)
from itertools import chain
card = list(chain.from_iterable(sampling()))
card[12] = HIT
return card
def check_bingo(card):
u"""Whether you are BINGO
Judgment only
param: array
:returns: boolean
>>> check_bingo([False, True, False])
False
>>> check_bingo([True] * 5)
True
>>> check_bingo([False, False, True, False, False] * 5)
True
>>> check_bingo([
... True, False, False, False, False,
... False, True, False, False, False,
... False, False, True, False, False,
... False, False, False, True, False,
... False, False, False, False, True,
... ])
True
>>> check_bingo([
... False, False, False, False, True,
... False, False, False, True, False,
... False, False, True, False, False,
... False, True, False, False, False,
... True, False, False, False, False,
... ])
True
"""
if card.count(HIT) < 5:
return False
for i in range(5):
if all(card[i * 5:(i + 1) * 5]): # horizontal
return True
for i in range(5):
if all(card[i:i + 21:5]): # vertical
return True
if all(card[0:25:6]) or all(card[4:21:4]): # skew
return True
return False
def print_card(card):
msg = ''
for i, v in enumerate(card):
if v == HIT:
v = 'o'
elif v == NOHIT:
v = 'x'
msg += '%3s' % v
if i % 5 == 4:
msg += '\n'
logger.debug(msg)
def parse_argument(argv=None):
"""
>>> parse_argument([])
Namespace(num=10000, times=5, verbose=False)
>>> parse_argument(['-n', '3', '-t', '30', '-v'])
Namespace(num=3, times=30, verbose=True)
"""
parser = argparse.ArgumentParser()
parser.set_defaults(num=10000, times=5, verbose=False)
parser.add_argument('-n', '--num', type=int, help='set card number')
parser.add_argument('-t', '--times', type=int, help='set times')
parser.add_argument(
'-v', '--verbose', action='store_true',
help='set verbose mode (loglevel=DEBUG)')
args = parser.parse_args(sys.argv[1:] if argv is None else argv)
if args.verbose:
logger.setLevel(DEBUG)
return args
def do_bingo(args):
card = generate_card()
lots = dict(sample([(i, HIT) for i in range(1, 76)], args.times))
card_hole = [lots.get(i, NOHIT) for i in card]
logger.debug('lots: {}\n'.format(sorted(lots.keys())))
print_card(card)
print_card(card_hole)
return card_hole
def main():
args = parse_argument()
hit_count = 0
for i in range(args.num):
card_hole = do_bingo(args)
if check_bingo(card_hole):
hit_count += 1
print(str(args.times) + u"Probability of BINGO even one at the first time:")
p = hit_count / args.num
print("%s (%.1f %%)" % (str(p), p * 100))
if __name__ == '__main__':
main()
Like this.
$ python Bingo_kai.py -n 3 -t 45 -v
...
lots: [1, 3, 8, 9, 10, 11, 12, 14, 16, 17, 19, 21, 22, 24, 26, 28, 29, 30,
31, 32, 33, 35, 36, 37, 39, 40, 41, 45, 46, 47, 49, 52, 55, 57, 60,
61, 63, 64, 67, 68, 69, 71, 72, 73, 75]
3 11 5 o 12
20 18 16 26 17
37 42 o 32 40
50 47 48 51 49
67 66 70 74 64
o o x o o
x x o o o
o x o o o
x o x x o
o x x x o
Probability of even one BINGO at the 45th time:
0.6666666666666666 (66.7 %)
Recommended Posts