24点计算编程思路
二、寻求全部解的递归算法
分治法是一种循环迭代的算法,也可以看成一种递归思路。
Python 递归主程序 recurs24.py
递归的思路逐步降维思路,在代码实现中,程序不断调用自身,每次调用都使大问题规模变小,最后达到退出条件(可以计算的条件)一层层返回直至解决原先的问题。
递归算法达到降维作用
采用降低维度的算法,即把多维问题降低到二维甚至一维来解决。
比如,给定四个数字 [a, b, c, d]
,这是一个四维问题,我们首先要将其转换为二维问题。
(1) 任取四个数中的两个,有
C
(
4
,
2
)
=
6
C(4, 2)=6
C(4,2)=6 种取法,每种取法中得到包含两个数的全部表达式。
假定首先取出[a, b]
,考虑到加法和乘法的交换率,共有6种可能的表达式:
即 a+b, a-b, a*b, a/b, b-a, b/a
,则四维问题转化为了多组三维问题,
即 ['a+b', c, d],['a-b', c, d],['a*b', c, d], ['a/b', c, d], ['b-a', c, d], ['b/a', c, d]
。
def merge2(a, b):
'''两个数配一个运算符号,共有 6 种可能,去掉了 加法和乘法(满足交换律)中的重复项。a+b,a-b,a*b, b-a,a/b,b/a,注意排除掉除数为0的情形。'''
res = set()
ops = ['+','-','*','/']
for i in range(3):
res.add('{0} {1} {2}'.format(a, ops[i], b))
res.add('{0} {1} {2}'.format(b, ops[1], a))
if eval(str(b)) != 0:
res.add('{0} {1} {2}'.format(a, ops[3], b))
if eval(str(a)) !=0 :
res.add('{0} {1} {2}'.format(b, ops[3], a))
return res
所以将四维问题转化为三维问题共有 C ( 4 , 2 ) × 6 = 36 C(4, 2) \times 6 = 36 C(4,2)×6=36 种组合。
(2) 重复上述过程,将三维问题继续转化为二维问题,同理,每一个三维问题都可转化为等价的二维问题,共有 C ( 3 , 2 ) × 6 = 18 C(3, 2) \times 6 = 18 C(3,2)×6=18 种组合。
(3) 四维问题转化为 36 × 18 = 648 36 \times 18 = 648 36×18=648 种二维问题,每个二维问题又有 6 6 6 种组合方式,所以,全部的表达式个数为 648 × 6 = 3888 648 \times 6 = 3888 648×6=3888 个。
加括号算法
在每一次二维组合成新表达式的时候,我们根据原有的两个表达式的各自的运算符号和两个表达式之间的运算符号的关系来判断是否需要添加括号。
比如,
a
、
b
a、b
a、b 两个表达式要组成新的表达式,总共会有如下几种情况:
(1)如果是
a
+
b
a + b
a+b,则完全不需要加括号;
(2)如果是
a
∗
b
a * b
a∗b 或者
a
/
b
a / b
a/b,若
a
、
b
a、b
a、b 自身的运算符号是加号或减号,则应加括号,如
a
=
a
1
+
a
2
,
b
a = a_1 + a_2,b
a=a1+a2,b 为数字,则
a
×
b
=
(
a
1
+
a
2
)
×
b
a \times b = (a_1 + a_2) \times b
a×b=(a1+a2)×b;
(3)如果是
a
−
b
a - b
a−b,若
b
b
b 为加号或减号,则
b
b
b 应加括号,如
b
=
b
1
−
b
2
,
a
=
a
1
+
a
2
b = b_1 - b_2,a = a_1 + a_2
b=b1−b2,a=a1+a2,则
a
−
b
=
a
1
+
a
2
−
(
b
1
−
b
2
)
a - b = a_1 + a_2 - (b_1 - b_2)
a−b=a1+a2−(b1−b2),但值得注意的是,
a
1
+
a
2
−
(
b
1
−
b
2
)
a_1 + a_2 - (b_1 - b_2)
a1+a2−(b1−b2) 其实等价于
a
1
+
a
2
−
b
1
+
b
2
a_1 + a_2 - b_1 + b_2
a1+a2−b1+b2,这种情况在其他的组合中其实已经存在。因此,可以无需再考虑括号问题;
(4)如果是
a
/
b
a / b
a/b,若
b
b
b 的符号是乘号或除号,原本也要加括号,但这种情况与上一种情况类似,我们出于计算简便考虑,可以不再考虑括号问题。
还有一种偷懒的方式,干脆全部加上括号。
去除等价表达式
对于一个表达式, a + b − c + d a + b - c + d a+b−c+d 与如下表达式均是等价的:
- a + d + b − c a + d + b - c a+d+b−c
- b + a + d − c b + a + d - c b+a+d−c
- b − c + a + d b - c + a + d b−c+a+d
可以在任何一个表达式前再加一个加号,然后使用正则表达式对表达式进行切割成如下状态:['+a', '+b', '-c', '+d']
。
然后对其进行排序后再组合成字符串得到:
a
+
b
+
d
−
c
a + b + d - c
a+b+d−c
这样的表达式称为标准表达式,凡是通过这样的处理方法得到的标准表达式是相同的,我们均认为是等价表达式,只保留一个标准表达式即可。
乘法交换率也是同样的转换方法。