魔方吧·中文魔方俱乐部

标题: 抓石子争单双博弈题 [打印本页]

作者: 钟七珍    时间: 2011-9-1 00:49:12     标题: 抓石子争单双博弈题

此题原来是一堆。改编成三堆,增加了难度:
  有A、B、C三堆石子,每堆的数量均为27个。两人轮流取石子,每次可取走的数为:A组1-3颗,B组1-4颗,C组1-5颗。但每次取石子时,只能在一堆里面取,不能同时取两堆或三堆。石子全部取完后,以总数为双数者获胜。问:应该先取还是后取才能获胜?取的过程中又应该怎样应对?

  若将此题作一般化设置:
  有A、B、C、……共n堆石子(n≥2),每堆的数量分别为N1、N2、N3、……、Nn,Ni可以任意或相等,且总数:N总=N1+N2+N3+……+Nn 是奇数。两人轮流取石子,,每次至少取一颗,最多可取:A堆M1颗,B堆M2颗,C堆M3颗……Mn颗,Mi可以任意或相等。但每次取石子时,只能在一堆里面取,不能同时取两堆或两堆以上。石子全部取完后,以拿取石子总数为双数者获胜。问:应该先取还是后取才能获胜?取的过程中又应该怎样应对?
  又:若把以拿取总数为双数者获胜,改为以拿取总数为单数者获胜,又该怎样取?

  此题目与本版置顶的《分珍珠》一题相似,但取胜条件由“谁取得最后一颗”改为“谁取得的总数为双数”,难度似乎增加了一点。

[ 本帖最后由 钟七珍 于 2011-9-12 11:27 编辑 ]
作者: jinlijie    时间: 2011-9-1 14:06:50

原来看到过好像有必胜方法的。。。。。。
作者: yq_118    时间: 2011-9-2 13:02:47

写了个程序,确实是先手必胜,第一步可以在B里取3个。
作者: 钟七珍    时间: 2011-9-11 15:03:20

关于此题,人工智能编程网友改编了一个简化版。我与他对了一局,并有一些讨论。实录如下(括号中是我的编辑按语):

钟七珍 40楼
7楼的简化版:
  有A、B、C三堆石子,每堆的数量 la,lb,lc 个。两人轮流取石子,每次可取走的数为:A组1-3颗,B组1-4颗,C组1-5颗。但每次取石子时,只能在一堆里面取,不能同时取两堆或三堆。石子全部取完后,以总数为双数者获胜。现出3题,问:先者赢还是输?如果赢的话,第一步怎样拿?
  题2:4 5 6
  题3:4 6 7
  题4:7 7 7
望各位指教!
———————————————————————————————————
  题2和题3,我得出的解是先行者胜。题4还未思考出应对策略。
2011-09-10 20:16

钟七珍 43楼
试一下吧,就题2。怎么样?
2011-09-11 01:48

人工智能编程0 44楼
好吧!@钟七珍
2011-09-11 11:43

钟七珍 46楼
44楼先行!(各人一先。下一局我先)
2011-09-11 11:54

人工智能编程0 47楼
好!题2:我先下,当中拿掉2。
4 3 6
r:2 z:0
z:拿
2011-09-11 11:59 (钟按:当他先手拿成 4 3 6 后,我就知道他胜了)

钟七珍 48楼
4 3 5
r:2 z:1
r:拿
2011-09-11 12:04

人工智能编程0 49楼
4 2 5
r:3 z:1
z:拿
2011-09-11 12:12 (钟按:这一步,他可以拿成 4 3 0,这样,胜负立判,赢得更快。还有,我发现他应对慢,我就怀疑他是用电脑编程在与我对局)

钟七珍 50楼
3 2 5
r:3 z:2
r:拿
2011-09-11 12:14

人工智能编程0 51楼
3 1 5
r:4 z:2
z:拿
2011-09-11 12:20

钟七珍 52楼
3 1 4
r:3 z:3
r:拿
2011-09-11 12:21

钟七珍 53楼
更正:
3 1 4
r:4 z:3
r:拿
2011-09-11 12:22

人工智能编程0 54楼
3 0 4
r:5 z:3
z:拿
2011-09-11 12:24

钟七珍 55楼
你赢了!  
看来确实是先拿者胜。
2011-09-11 12:27

人工智能编程0 56楼
引用 钟七珍 (55楼) 你赢了!   看来确实是先拿者胜。
是先拿者胜的。
2011-09-11 12:29

钟七珍 57楼
但我没有找到数学分析方法,与二进制分析挂不上钩?望老师赐教!
2011-09-11 12:31

人工智能编程0 58楼
我也不会2进制方法,但是我知道2进制方法肯定存在,能解数量上亿的棋局。
2011-09-11 12:35

钟七珍 59楼
哦。那你是针对具体题目,每一步用电脑编程应对的吗?
2011-09-11 12:38

人工智能编程0 60楼
我是用电脑硬算的,数据小点还行,数据一大就玩完。。。
两进制解法数量能到大上亿,几乎无限。。。

我不会,但是我知道肯定存在。。。
2011-09-11 12:50

钟七珍 61楼
所以,7楼的三道题目用电脑编程易解,而1楼的三堆27颗解起来就难了!
  找出通解,尚须时日。
  继续努力!互勉!
2011-09-11 13:00

人工智能编程0 62楼
好!
2011-09-11 13:11

(原帖见:http://tieba.baidu.com/p/1196491373
作者: yq_118    时间: 2011-9-11 19:48:04

这个问题规模很小,只有27^3=19683。用递归轻松搞定。 调用 s(27, 27, 27, 0) 返回的是 (27, 24, 27),表示第一步在B里取3个,以后根据对手的决策反复调用s就行了。
  1. #!/usr/bin/python3 -i


  2. def solver(a_max, b_max, c_max):
  3.     mem = {(0, 0, 0, 0): 'win',
  4.            (0, 0, 0, 1): 'lose'}

  5.     def adj(a, b, c):
  6.         return ([(i, b, c) for i in range(max(0, a - a_max), a)] +
  7.                 [(a, i, c) for i in range(max(0, b - b_max), b)] +
  8.                 [(a, b, i) for i in range(max(0, c - c_max), c)])

  9.     def rec(x):
  10.         if x not in mem:
  11.             p = ((sum(x) + 1) % 2,)
  12.             for y in adj(*x[:3]):
  13.                 if rec(y + p) == 'lose':
  14.                     mem[x] = y
  15.                     break
  16.             else:
  17.                 mem[x] = 'lose'
  18.         return mem[x]

  19.     return lambda a, b, c, p: rec((a, b, c, p))


  20. s = solver(3, 4, 5)
  21. print(s(27, 27, 27, 0))
复制代码

[ 本帖最后由 yq_118 于 2011-9-11 20:40 编辑 ]
作者: 钟七珍    时间: 2011-9-11 20:19:09

谢谢LS!不过,我看不懂。我只学过一点BASIC语言。这个是C语言吧?
作者: yq_118    时间: 2011-9-11 20:43:18

这个是Python写的,用C的话要考虑太多与问题无关的东西。
作者: lulijie    时间: 2011-9-11 22:37:13

用f(a,b,c)表示A堆为a,B堆为b,C堆为c时,先行方取石子最后能取得的结果。
f(a,b,c)=0    表示先行方必得到偶数结果
f(a,b,c)=1    表示先行方必得到奇数结果
f(a,b,c)=2    表示奇偶结果由先行方决定
f(a,b,c)=3    表示奇偶结果由后行方决定
-------------------------------------------------------------
电脑编程(我用的是VB6.0,楼主要的话可以向我索取),f(27,27,27)之内的所有结果见附件。
下面摘取其中的部分结果。
f(4,5,6)=2
f(4,6,7)=2
f(7,7,7)=2
说明楼主后来举的三个简单些的题目,无论最后是偶数胜还是奇数胜,都是先行方胜。
而f(27,27,27)=0    即先行方必得偶数。
说明偶数胜,则先行方胜,奇数胜则先行方败。

答案.rar (16.48 KB, 下载次数: 6)

附件: 答案.rar (2011-9-11 22:37:13, 16.48 KB) / 下载次数 6
http://bbs.mf8-china.com/forum.php?mod=attachment&aid=MTU4NzY2fDUzYTBjMDRmfDE3NDAxNjIwODB8MHww
作者: lulijie    时间: 2011-9-11 22:45:19

f(27,24,27)=1,说明先行方必得奇数。
所以若结果偶数胜,那么 (27,27,27)---->(27,24,27)就能胜利。
而(27,27,27)所能演变的所有其他结果f(a,b,c)都等于2,所以其他取法都不能取得胜利。
而若结果为奇数胜,那么后行方必得到奇数结果,所以先行方必败。
作者: lulijie    时间: 2011-9-11 23:02:04

f(6,3,4)=3
说明无论是奇数胜还是偶数胜,先行方必败。
作者: lulijie    时间: 2011-9-11 23:36:37

f(a,b,c)=0    表示先行方必得到偶数结果
f(a,b,c)=1    表示先行方必得到奇数结果
f(a,b,c)=2    表示奇偶结果由先行方决定
f(a,b,c)=3    表示奇偶结果由后行方决定
-----------------------------------
递推方法的思路:
f(0,0,0)=0
(a,b,c)---->(x,y,z)
若所有能演变的(x,y,z)中,f()值都等于0或2(至少有一个值为0),那么f(a,b,c)=(a+b+c) mod 2   
若所有能演变的(x,y,z)中,f()值都等于1或2(至少有一个值为1),那么f(a,b,c)=(a+b+c-1) mod 2
若所有能演变的(x,y,z)中,f()值至少有一个为0,且至少有1个为1),那么f(a,b,c)=2
若所有能演变的(x,y,z)中,f()值至少有一个为3,那么f(a,b,c)=2
若所有能演变的(x,y,z)中,f()值都等于2,那么f(a,b,c)=3
作者: 钟七珍    时间: 2011-9-12 01:30:09     标题: 回复 11# 的帖子

先生的几篇回帖,本人受益不少。尽管有些内容还不明白,尚须时间消化。
f(a,b,c)=0(或等于1,2,3)是如何算出来的?比如一堆时,f(a)=? 或两堆时,f(a,b)=?
望先生不吝指教!
作者: lulijie    时间: 2011-9-12 10:52:02

比如初始,(0,0,0)
那么先行方只能拿到0个,即必拿到偶数,所以f(0,0,0)=0
------------------------------------------------------------------------------
1: 下面递推f(1,0,0)
(1,0,0)只能变为(0,0,0)
面临(0,0,0)局面的是对方,它只能拿到偶数,所以面临(1,0,0)的先行方必拿到奇数。    (1+0+0) mod 2=1
所以f(1,0,0)=1
同理 f(0,1,0)=1;f(0,0,1)=1
------------------------------------------------------------
2:下面递推f(2,0,0)
(2,0,0)可变为(1,0,0),(0,0,0)
因为f(1,0,0)=1,f(0,0,0)=0
所以先行方即可以使对手面临必奇局面(1,0,0),也可以让对手面临必偶局面(0,0,0),
也就是说自己拿奇数还是偶数由自己决定,所以f(2,0,0)=2
--------------------------------------------------------------------------------
3:下面递推f(3,0,0)
(3,0,0)可变为(2,0,0),(1,0,0),(0,0,0)
因为f(2,0,0)=2,f(1,0,0)=1,f(0,0,0)=0,所以f(3,0,0)=2
   如果结果偶数胜,那么先行方需---->(1,0,0)才能胜,
   如果结果奇数胜,那么---->(0,0,0)才能胜。
   无论哪种情况都不能---->(2,0,0),因为这样取奇取偶由对方决定了。
-------------------------------------------------------------------------------------------
4.下面递推f(4,0,0)
(4,0,0)可变为(3,0,0),(2,0,0),(1,0,0),变不了(0,0,0),因为m=3.
因为f(3,0,0)=2,f(2,0,0)=2,f(1,0,0)=1
所有先行方只有---->(1,0,0)一条路,即对手必奇,那么自己也必奇。   (4+0+0-1) mod 2=1
不能---->(3,0,0)或(2,0,0),否则奇偶由对手决定了。
---------------------------------------------------------------------
依此类推......
(1,1,0)
因为f(0,1,0)=1,f(1,0,0)=1
所以,f(1,1,0)=((1+1+0-1) mod 2)=1
------
(2,1,0)
因为f(1,1,0)=1,f(0,1,0)=1,f(2,0,0)=2
所以,f(2,1,0)=((2+1+0-1) mod 2)=0
........
作者: lulijie    时间: 2011-9-12 10:57:43

只要把递推的思路编程,递推过程交给电脑就万事大吉了,不过要把结果记录下来,否则白干。
作者: 钟七珍    时间: 2011-9-12 10:59:35

  返回去看了一下我发的只抓一堆石子的帖子http://bbs.mf8-china.com/viewthr ... &extra=page%3D1,发现参与讨论的也是上面两位老师yq_118和lulijie。
  我把两位老师发的几段帖子转过来,也许有助于此题的讨论:
  yq_118:
“假设每次可以取1~4个,
6*n+1个硬币,先手不能保证取偶数个。其它情况先手一定能取到偶数个。
6*n+3,6*n+5个硬币,先手不能保证取奇数个。其它情况先手一定能取到奇数个。”
  lulijie:
“对于每次拿1-m个:
那么若m为偶数,那么被m+2除,余1为必奇局面(即先行方必拿到奇数个硬币),余0和余m-1为必偶局面,余其它为必胜局面(即奇偶由先行方决定)。
若m为奇数,那么被2m+2除,余1和余m+1为必奇局面,余m+2和余0为必偶局面,余其它为必胜局面。”
  lulijie:
“必奇局面:先行方必拿到奇数个硬币。
必偶局面:先行方必拿到偶数个硬币。
必胜局面:先行方可自行决定拿奇数个硬币还是拿偶数个硬币。
------------------------------------------------------------------------
硬币总数为N,每次取1-m个硬币。
m为偶数:       必奇局面 N mod (m+2)=1
                         必偶局面 N mod (m+2)=0 或 m+1
                         必胜局面 剩下的都是
m为奇数:        必奇局面 N mod (2m+2)=1或m+1
                         必偶局面 N mod (2m+2)=0或m+2
                                                 必胜局面 剩下的都是”
作者: lulijie    时间: 2011-9-12 11:38:00

楼主把题目改成了没有确切数值的一般情况,电脑编程最怕这个情况。
解决这种题目的方法:
1. 先让电脑计算出一大堆结果来,然后人脑去分析出规律。
2. 直接用人脑,寻找规律。(电脑在这方面不如人脑)
作者: 钟七珍    时间: 2011-9-12 11:43:32     标题: 回复 13# 的帖子

谢谢lulijie老师的指教!大概基本明白了。
作者: 钟七珍    时间: 2011-9-12 11:57:46

看了13楼之后,再返回去看11楼,这才看懂。
惊叹lulijie老师是怎样分析出:
f(a,b,c)=0    表示先行方必得到偶数结果
f(a,b,c)=1    表示先行方必得到奇数结果
f(a,b,c)=2    表示奇偶结果由先行方决定
f(a,b,c)=3    表示奇偶结果由后行方决定  这四种状态判断的!
作者: 钟七珍    时间: 2011-9-12 12:03:44

用递推的方法,虽然麻烦,且没有一个简捷的公式计算,但毕竟是找到了一条至胜的道路!
作者: yq_118    时间: 2011-9-12 13:02:20

稍加修改就能推广到一般的情况。
根据题目的要求调用solver,返回一个求解器。
调用求解器,第一个参数为一个向量,表示各堆的石子数,第二个参数表示奇偶性,0为偶,1为奇。
返回'lose'表示输了,否则返回一个向量表示决策。
  1. #!/usr/bin/python3 -i


  2. def solver(*m):
  3.     n = len(m)
  4.     mem = {((0,) * n, 0): 'win',
  5.            ((0,) * n, 1): 'lose'}

  6.     def adj(x):
  7.         for i in range(n):
  8.             y = list(x)
  9.             for y[i] in range(max(0, x[i] - m[i]), x[i]):
  10.                 yield tuple(y)

  11.     def rec(x, parity):
  12.         if (x, parity) not in mem:
  13.             q = (sum(x) + parity + 1) % 2
  14.             for y in adj(x):
  15.                 if rec(y, q) == 'lose':
  16.                     mem[(x, parity)] = y
  17.                     break
  18.             else:
  19.                 mem[(x, parity)] = 'lose'
  20.         return mem[(x, parity)]

  21.     return rec


  22. s = solver(3, 4, 5)
  23. print(s((27, 27, 27), 0))
复制代码

[ 本帖最后由 yq_118 于 2011-9-12 13:14 编辑 ]




欢迎光临 魔方吧·中文魔方俱乐部 (http://bbs.mf8-china.com/) Powered by Discuz! X2