关于三堆小球的 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的学习经验交流,无作他用。如需转载请注明出处。