关于三堆小球的 python 实现
今天看到一个比较有意思的思维题:
游戏规则: 有三堆小球, 数量分别为3个, 5个, 7个, 如下:
。。。
。。。。。
。。。。。。
两人轮流从三堆小球里面的其中一堆拿走小球, 拿走的数量不限, 但至少拿走一个。
胜负条件: 拿走所有球里面最后一个的玩家为输家
要求, 请通过算法计算出如何拿小球才能保证胜率最高。
我是这样思考的:
既然取球可分胜负, 则一定存在某种形式的球堆, 无程序可破, 先取球者必输
那么程序设计思路:
step 1: 找出必败的序列
step 2: 分析当前球堆, 找出离之最近的必败形式
step 3: 设法将最近必败形式留给对手
step 4: 取球的同时避免对手将必败序列留给自己
就通过封装类,实现了取球过程,希望能与您交流。
import random
class Balls(object):
'''球堆类
优化取球方法,尽可能取胜
既然取球可分胜负, 则一定存在某种形式的球堆, 无程序可破, 先取球者必输
那么程序设计思路:
step 1: 找出必败的序列
step 2: 分析当前球堆, 找出离之最近的必败形式
step 3: 设法将最近必败形式留给对手
step 4: 取球的同时避免对手将必败序列留给自己
实现细节:
全局变量 BAD_BALLS 记录所有的必败形式
make_bad_balls() 寻找所有所有必败形式
'''
def __new__(cls, *args, **kwargs):
if len(args) - 3:
print('Orignal Arguments not enough!')
elif args[0] > 3 or args[1] > 5 or args[2] > 7:
print('({},{},{}) out of range(3, 5, 7)'.format(*args))
else:
return super().__new__(cls)
def __init__(self, *args):
self.pile_one = args[0]
self.pile_two = args[1]
self.pile_three = args[2]
self.lst = list(args)
self.set = sorted(self.lst)
self.num = sum(args)
def __repr__(self):
return "Balls({},{},{})".format(
self.pile_one, self.pile_two, self.pile_three)
def __sub__(self, balls):
return self.num - balls.num
def __eq__(self, ball):
if self.set == ball.set:
return True
else:
return False
def take_ball_once(self, ball):
"""返回取球一次后所有剩余的可能"""
odds_lst = []
for index in range(len(ball.set)):
for num in range(1, ball.set[index] + 1):
lst = list(ball.set)
lst[index] -= num
if not sum(lst):
# 如果是只有一列, 则有可能一下子取完, 这种形式是无效的
break
lst.sort()
new_balls = Balls(*lst)
odds_lst.append(new_balls)
# print("in take_ball_once! odds_lst:", ball, odds_lst)
return odds_lst
def judge(self, ball):
"""如果取球一次后所有的可能都存在至少一种可能
一步可以达到坏球, 则这个形式也是坏球
"""
odds_lst = self.take_ball_once(ball)
n_odds = len(odds_lst)
# state 标记 对应的 odd 是否可取, True表示可取
odds_state = [True] * n_odds
for index in range(n_odds):
# 对于每一种剩余的形式, 再取一次, 只要存在一种可能与坏球相等, 则这种剩余不可取
odd_odds_lst = self.take_ball_once(odds_lst[index])
for odd_odd in odd_odds_lst:
for bad_ball in BAD_BALLS:
# if odd_odd.num <= bad_ball.num:
# break
if odd_odd == bad_ball:
# 某种剩余的剩余与坏球形式一样,则这种剩余不可取
odds_state[index] = False
# print(ball, odds_lst[index], odd_odd,
# bad_ball, odd_odd == bad_ball)
break
if not odds_state[index]:
break
# 如果所有的剩余都不可取, 则这种形式是坏球
return sum(odds_state)
def split(self, n_balls):
factors = []
for n_three in range(1, min(n_balls, 7) + 1):
for n_two in range(min([n_three, 5, n_balls - n_three]) + 1):
n_one = n_balls - n_two - n_three
if n_one <= 3 and n_two >= n_one:
factors.append(Balls(n_one, n_two, n_three))
return factors
def make_bad_balls(self):
for n_balls in range(2, MAX_BALL_NUM + 1):
factors = self.split(n_balls)
for fac in factors:
if not self.judge(fac):
# print('fac', fac)
BAD_BALLS.append(fac)
print('make bad_balls OK!')
def take_balls(self):
"""如果遇到坏球, 自己尽可能制造对手错误的机会"""
factors = self.take_ball_once(self)
for fac in factors:
for bad_ball in BAD_BALLS:
if fac == bad_ball:
return fac
else:
print("Bad luck! {} is a type of bad_ball! You'll loss!".format(self))
if self.num == 1:
return None
print("You may try to make some chance that could to mislead "
"your Opponent!".format(self))
index = BAD_BALLS.index(self)
next_bad_ball = BAD_BALLS[index - 1]
for fac in factors:
if fac - next_bad_ball >= 2:
return fac
else:
index -= 1
if index:
next_bad_ball = BAD_BALLS[index]
return factors[0]
# bad_balls = [Balls(0, 0, 1), Balls(1, 1, 1), Balls(0, 2, 2),
# Balls(1, 2, 3), Balls(0, 3, 3), Balls(0, 4, 4),
# Balls(1, 4, 5), Balls(0, 5, 5), Balls(2, 4, 6),
# Balls(3, 5, 6), Balls(3, 4, 7), Balls(2, 5, 7)]
MAX_BALL_NUM = 15
orign = Balls(0, 0, 1)
BAD_BALLS = [orign]
balls = Balls(3, 5, 7)
balls.make_bad_balls()
print(BAD_BALLS)
print('-----------------')
print(balls)
while balls:
balls = balls.take_balls()
print('take your balls: ', balls)
测试结果:
Balls(3,5,7)
take your balls: Balls(2,5,7)
Bad luck! Balls(2,5,7) is a type of bad_ball! You'll loss!
You may try to make some chance that could to mislead your Opponent!
take your balls: Balls(2,5,6)
take your balls: Balls(2,4,6)
Bad luck! Balls(2,4,6) is a type of bad_ball! You'll loss!
You may try to make some chance that could to mislead your Opponent!
take your balls: Balls(2,2,6)
take your balls: Balls(0,2,2)
Bad luck! Balls(0,2,2) is a type of bad_ball! You'll loss!
You may try to make some chance that could to mislead your Opponent!
take your balls: Balls(0,1,2)
take your balls: Balls(0,0,1)
Bad luck! Balls(0,0,1) is a type of bad_ball! You'll loss!
take your balls: None
最后,版权声明:
本blog是笔者根据在网络上看到的思维题编写的python代码, 谨用于关于python的学习经验交流,无作他用。如需转载请注明出处。