python算24点穷举法_24点游戏7节课–第1节-游戏介绍与基本算法 | 学步园

这仅仅是一个控制台(DOS窗口下)的小游戏——有人欢喜有人烦了。欢喜的是因为可以专心于游戏逻辑自身过程,就算你只学过C++简单的屏幕输入输出(cin、cout ),乃至换用java,C#也可以写这个小程序。甚至用的是C的printf/scanf也问题不大,这个程序基本不用去考虑“面向对象”的问题。烦的人是正好他想学习高级的游戏编程,比如2D甚至3D(Direct X / OpenGL)啊,或者至少有图形用户界面,比如MFC或wxWidgets啊……这个就另外学习了。

这个游戏会用到boost::thread,也就是需要有个后台线程——又有人欢喜又有忧愁了。欢喜的是这游戏才算有点意思嘛,后台线程……估计是会搞个人机对战吧?忧愁的是,很多人说我不会编译boost啊……呵呵,自行解决吧。如果一时不想编译,也没关系,线程会相对后才使用。

最后一句废话,这系列文章虽然在本处归为“《白话C++》”下,但为了适合在网上(这样没有上下文的情况下)发表,做了大量的修改,包括分章,还包括代码(被粗暴地)简化,因此和最后书里的内容相差很大。

(一)游戏规则介绍

言归正传,先谈下我们要实现24点游戏规则(有很多注意点,别跳过):

A基本玩法:出4张牌(不含大小鬼),然后用“加减乘除”组合计算这四个数,能算出24就算得解。

B虽然我们的5、5、5、1的某种算法:(5 - 1/5)/5,不被我们接受,因为会让它显得不像是给人玩的游戏(我家小朋友不喜欢)。

C我们不会让计算过程中,出现负数。原因之一类同B,读小学的小孩不会这么算。原因之二是它其实没有必要。对于24点游戏,带负数的计算过程,可以转换为不带负数的,比如:(4 - 5 + 9) * 3,可以转换为:(9 - 5 + 4)  *  3。

(二)算法考虑

我只用过两种算法计算24点,其一是“知识库”法。是我当学生时的作品。大致是将可能计算出24点的算式,先全部列出来,比如:a+b+c+d, a+b+c-d, (a+b)*(c-d)等等……事实上也不多,20几个就够了。

然后针对每一个算法,再进行穷举法,像初中的代数,把四个数不断代入到算式,形成一个表达式,再计算出来结果是24。

这种方法当然比完全穷举要高效得多,但以当前的计算机运算能力,但穷举法也不过在毫秒之间,所以不是很有所谓。

当初用这个方法……倒是因为人机竞争中,我为给“机器人”设置了几个角色,比如“哭鼻子大王”,“马虎王”,“机器猫”等等,不同的角色拥有不同的计算能力,比如“哭鼻子大王”代表一位二年级学生,所以他就只拥有那些不带乘除运算的算式……

如果要用这个方法,就得需要一个表达式求值的功能,比如给一个(9 - 2) * 2 + 9,能够算出它是23——当然在C++中,这样的库多乎其多……实在要自己写一个也可以,但为了简化,我们不想碰表达式求值的东东,也不想再引入其它库(除了boost::thread)。

另一个方法是纯粹的穷举法,也称为“暴力法”。

首先一个事,要先悟到:对于四个数计算,括号"(和")"在穷举过程其实可以有效规避,从而仅仅变成两种计算方式。

先说第1种:从左向右,两个数两个数地计算,比如:1+2+3+4,可以分解为这样三步:1+2=>33+3=>66+4=>10。

注意!这个过程是不需要考虑优先级的,比如:1 + 2 * 3 - 4,计算过程还是仅仅从左到右:1 +2 =>3

,3 * 3 =>9 , 9 -4 =>5,即运算过程是:(1+2) * 3 - 4。那存在优先级计算的1 + 2 * 3 – 4何时计算?不要紧,因为我们是穷举,所以必然会有机会计算这个:2 * 3 + 1 – 4,耤以我们强大的小学算术知识,可以知道它就是1 + 2 * 3 – 4——这样一想,我们这个算法很英明了,因为它可以避免出现大量的重复答案。

不过,有没有漏洞,有噢,因为是4个数,所以,类似(1+2)*(3+5),这样先计算两头,再用中间的操作符处理左右两个(临时)结果,最终得出结果的,没办法换成那种不需要考虑优先级的计算式来的……不过幸好,对于四个数组合,也就这一种特例。(得出一个小提示:如果题目变成5个数或6个数,或更多数,我们的算法就不能直接改几个值就想使用的,不过我们现在对写一个超级通用算法不感冒)。

结论是:拿到一个表达式,我们分别用两种方法进行计算。

例如:存在算式:1 + 2 * 3 +5,则两种方式计算:

第一种,我们称为“连贯式计算”,(代码中用“consecutive”表示,别问我为什么),得到14。看本文到现在,你必须搞懂这个14是如何得来的。真不懂?答:((1+2) * 3) + 5

第二种,我们称为“分隔式计算”,(代码中用“separate”表示,很想问我为什么?因为我就懂这个词),得到24。如何得来?答:(1+2) * (3+5)。

推而广之:我们用n1..4表示四个操作数,用_o1..3_表示中间三个算符,则有:

算式:n1_o1_n2_o2_n3_o3_ n4,要计算其值,我们仅通过以下a,b两算式计算:

a: ((n1 _o1_ n2)_o2_ n3) _ o3_ n4

b:(n1 _o1_ n2) _o3_( n3 _o3_ n4)

n1..4是拿到手四个数,_o1..3_则是从四个操作符(+、-、*、/)挑出三个操作符,并且可以重复,比如可以挑出3个加号,或者2个减号1个除号等等。有学过高中数学的,可以回忆一下排列组合的知识。

就算回忆不出高中数学知识,也不要紧,反正我们是不讲理的,是要用暴力解决问题的……所以剩下的问题,就是如何把真实的数字及操作符不断地放入到那7个位置上,方法当然就是循环,就是最强的暴力穷举工具:for,来一段示例代码:

Code:

intcalc_24(intnum[4])  //[4]中的那个‘4’,你知道,我也知道是无用的...仅为了好看...

{

intn[4];

/*开始丑陋的多层循环...如果用OO,是可以换成一个可遍历的对象来解决...但...算了吧 */

/* 只会让实际代码更多并且更不直观*/

for(inti1=0; i1<4; ++i1)

{

n[0] = num[i1];

for(inti2=0; i2<4; ++i2)

{

if(i2==i1)//第二张牌不能和第一张一样

continue;

n[1] = num[i2];

for(inti3=0; i3<4; ++i3)

{

if(i3 == i1 || i3 == i2)//第3张...同样要绕过前面两张

continue;

n[2] = num[i3];

inti4 = 6-i1-i2-i3;//最后一张当然不用挑...

//因为..可以计算出来

n[3] = num[i4];

//... 现在,我们准备好(一次)四个数了...

//下面应该开始摆放3个操作符了...以后说...

}

}

}

}

(黑色注释部分是我用来自我解释的……还有像‘数组’入参没有检查边界等等...)

看得懂这段代码,就可以准备看第2节……看不懂,那得复习C或C++去,还可以google搜索下,网上谈24点算法很多,估计我这个不算什么另类的。

布置一个作业: 在下节开始之前,或许你应该把这段代码写完:再准备一个数组(o[3]),用来从4个操作符里,挑出3个来存着。

(后续:第2节……)

-------------------------------------------------------------------------------

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值