赞
踩
还记得小时候拿一副牌玩得24点游戏吗?
拿一副牌,抽去大小王后(J,Q,K记为11,12,13;用1代替A),剩下1~13这52张牌。任意抽取4张牌(称为牌组),用加、减、乘、除(可加括号,高级玩家也可用其他运算符号)把牌面上的数算成24。每张牌必须用且只能用一次。如抽出的牌是3、8、8、9,那么算式为(9-8)×8×3=24。
下面就介绍一种求解24点问题的递归思路以及给出其Python求解代码。
递归的思路便是把大的问题变小,每一步将原先要求解的问题分解成与原问题相似的规模较小的问题更小的问题,直至最后达到一个满足退出条件的简单问题,这样再一层层向上返回,便解决了原先的大规模的问题。在代码实现中,程序不断调用自身,每次调用都使问题规模变小,最后达到退出条件一层层返回直至解决原先的问题。
构成递归需具备的条件:
1.子问题须与原始问题为同样的事,且更为简单;
2.不能无限制地调用本身,须有个出口,化简为非递归状况处理。
理解它的一个非常好的例子便是阶乘。n的阶乘(记为fact(n))定义为:
n ! = ∏ i = 1 n i = n ∗ ( n − 1 ) ∗ ( n − 2 ) . . . ∗ 1 n!=\prod_{i=1}^{n}i=n*(n-1)*(n-2)...*1 n!=∏i=1ni=n∗(n−1)∗(n−2)...∗1
它可以通过递归方式定义为:
若n=1 , 则fact(n)=1;
若n为大于1的整数,则fact(n)=n*fact(n-1)
这相当于给了一个退出条件(n=1的情况),和一个把问题分解为更小的同类型问题的方法(求n的阶乘变为求n乘上n-1的阶乘)。很容易验证,这样的递归定义和其本身的定义是一致的,而递归定义很容易用通过函数调用自身的程序代码实现。
24点问题也是同理
用四张牌求出24点也许没那么容易,但如果只有两个数,那么我们可以很容易地通过不同的运算符(例:加减乘除)得到两个数组合之后的结果。因此两个数的24点问题是一目了然的。
那么三个数呢?对于三个数a,b,c,我们可以从其中任意挑选两个数(三种挑选方式),例如a,b,之后通过加减乘除计算得到a与b组合后的数d(如果只有四则运算,a与b一定时d有a*b,a+b,a-b,b-a,a/b,b/a几种可能,当然除法首先要求分母不为0),那么d确定了之后问题便变成c与d算24点的两数问题,它们是否能得出24是一目了然的。于是对一个个分支(及所有可能的c和d)进行尝试,便能判断三个数能否以及如何得出24点。
四个数当然也可以以同样的方法变为多个三数问题,进而变成两数问题输出结果。
当然如果不止四个数也可以以同样的方法一步步使问题变小,直至缩小成两数问题达到递归的返回条件。
于是我们的算法求解如下:
1.一个将两个数a,b通过运算组合成新的数的函数f(a,b)
此函数输入两个数a,b,输出它们四则运算的各种对应解及其运算符。
例如输入(a,b),输出a+b的结果及加号,a-b的结果及减号…
2.递归主程序
输入牌组,判断牌组长度,若为两数问题则通过f(a,b)判断能否算出24点并返回;若牌组长度大于2则任选两个数a,b;调用函数f(a,b),将其输出的数存储在一个新的牌组中(此数所对应的运算符即为当前的解决方案分支),新的牌组长度比原先的长度减一,解这个新的牌组的24点问题。
函数的Python实现
1.一个将两个数a,b通过运算组合成新的数的函数f(a,b)
在Python中可以以一个列表的形式很容易地将输出存储起来,即输入(a,b)两个数,f(a,b)返回[(a+b,’+’),(a-b,’-’),…],这里方括号代表列表,括号代表元祖的数据结构,它将运算结果和其对应的运算符放在一起。运算结果对应的运算符用字符(即’+’,’-’,…)进行输出。
2.递归主程序
牌组可用列表来保存,并记录当前列表长度用来判断,用一个全局的数组记录求解过程,即在每一个递归分支中,保存使用过的数,组合成新数的运算符等。
将牌组记为列表A,全局的数组(当前求解过程)记为temp,初始问题大小记为N,当前问题大小记为n,则主程序可以用一张图加以说明:
cal24.py中代码如下:
#交换a与b
def swap(a,b):
temp=a
a=b
b=temp
return a,b
# +:0, -:1, *:2, /:3
#in:two number
#out:a tuple with result and its operator
def f(a,b):
#让a>=b
if(a<b):
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。