最近开心上狂算24点,于是贝壳搞了一个24点计算程序,并且说明原理。

我们将24点问题一般化,变成一个搜索问题。假定从一个初始表开始,里面有一些原子。我们定义一个操作,结合。每次操作任意从中选择出两个(或者以上)原子,使用算符连接,成为一个新的原子。那么,一般来说,24点就是计算所有可能的路径,从初始表开始,持续进行结合,直到只剩下一个原子,并且对这个原子求值得24。

有人可能在算符优先级上想不开,其实不用考虑这个问题,每次求值的时候,按照求值顺序优先就可以。你想到的另外一种优先级可能,会在穷举的时候被列举出来算掉,不用担心遗漏。

同时,算子必须是两目以上算子,因为单目算子可以持续作用于同一个对象,因此原子表中的原子个数并不严格单调减少,造成无法肯定路径收敛于有限步骤上。并且,如果允许单目算子,那么我只需要求导和阶乘就可以对任何数字求24点。

((a')!+(b')!+(c')!+(d')!)!=24

因此,单目算符是没有意义的。

另外,注意算符分可交换和非可交换的。例如:a+b=b+a,但是a-b!=b-a。如果不注意这点,倒是不会漏算,但是会造成搜索空间增大,并且有重复结果。

以下是24点计算程序,python版本的。有兴趣的朋友可以用scheme重写,相信会更简洁有效。回头会用django封装一下,做成网页给大家玩玩。

#!/usr/bin/python
import sys

symbol_list = [
	("%s+%s", True),
	("%s-%s", False),
	("%s*%s", True),
	("%s/%s", False),
	("%s**%s", False),
]


def diff_seq(length):
	for i in range(0, length):
		for j in range(i + 1, length):
			yield (i, j)


def get_less_state(input_state):
	for i, j in diff_seq(len(input_state)):
		temp = input_state[:]
		del temp[j]
		del temp[i]

		for s in symbol_list:
			rslt = s[0] % (input_state[i], input_state[j])
			rslt = "(%s)" % rslt
			temp.append(rslt)
			yield temp
			temp.remove(rslt)
			if s[1]:
				continue
			rslt = s[0] % (input_state[j], input_state[i])
			rslt = "(%s)" % rslt
			temp.append(rslt)
			yield temp
			temp.remove(rslt)


def do_node(input_state, output):
	for i in get_less_state(input_state):
		if len(i) > 1:
			do_node(i, output)
			continue
		try:
			rslt = eval(i[0])
		except:
			continue
		if rslt == 24.0:
			output.add(i[0].replace(".0", ""))


if __name__ == "__main__":
	rslt = []
	for i in sys.argv[1:]:
		rslt.append(float(i))
		output = set([])
		do_node(rslt, output)
		for i in list(output):
			print("%s=24" % i)