算 24 点

24 点游戏是一种经典的用扑克牌来进行的益智游戏。游戏内容是:从一副扑克牌中抽去大小王剩下 52 张,任意抽取 4 张牌,把牌面上的数字(JQKA 分别代表 1112131)运用加、减、乘、除和括号进行运算得出 24。每张牌都必须使用一次,但不能重复使用。

请编写代码对任意给出的四张牌算 24 点,用文本输出解答。


A

B

C

D

1

[3,3,8,8]

[1234,1243,1324,1342,1423,1432,2134,2143,2314,2341,2413,2431,3124,3142,3214,3241,3412,3421,4123,4132,4213,4231,4312,4321]

[+,-,*,/]

=C1.conj@r(C1.(C1.(~/get(1)/get(2))))

2

for B1

=A1(string(A2).split@p())

>a=B2(1),b=B2(2),c=B2(3),d=B2(4)

3


for D1

=B3.split()

>x=C3(1),y=C3(2),z=C3(3)

4



=a/x/"("/b/y/"("/c/z/d/"))"


5



if round(eval(C4),4)==24

=@|C4

6



=a/x/"(("/b/y/c/")"/z/d/")"


7



if round(eval(C6),4)==24

=@|C6

8



="("/a/x/b/")"/y/"("/c/z/d/")"


9



if round(eval(C8),4)==24

=@|C8

10



="(("/a/x/b/")"/y/c/")"/z/d


11



if round(eval(C10),4)==24

=@|C10

12



=a/x/"(("/b/y/c/")"/z/d/")"


13



if round(eval(C12),4)==24

=@|C12

14

=[D5,D7,D9,D11,D13].conj().id()



http://try.scudata.com.cn/try.jsp?splx=ExA009essd.splx

A1给出计算所用的4张牌。B1枚举出4张牌所有可能的排列顺序。C1列出可选的运算符号。

由于4张牌之间需要放置3个运算符号,每个都可能任意选择,D1中用多层循环列出所有可能的符号选择。SPL中,在最外层中用A.conj@r()可以将各层的结果合并成一个序列。D1中结果如下:

..

B1中的枚举结果,如果不想手动去写,也可以类似写为=4.conj@r(4.(4.(4.([~,get(1),get(2),get(3)]).select(~.icount()==4).(~.concat())))),与C1不同的是,需要选出用到了所有4张牌,即每张牌都只用1次的排列方案。

A2循环每一种牌的排列顺序,B2根据顺序,得到算24所需的4个数,为了便于使用,C2中将这四个数赋值给a,b,c,d四个变量。

B3对于每一组数,循环可使用的运算符。C3将运算符拆分为序列,D3将其分别用变量x,y,x表示。

对于给定了顺序和符号的一种组合:a#b#c#d,根据不同的运算顺序,有5种情况,分别用括号表示如下:a#(b#(c#d))a#((b#c)#d)(a#b)#(c#d)((a#b)#c#d(a#(b#c))#d

在循环体中依次尝试。如C4按照a#(b#(c#d))的运算顺序,拼出表达式,C5eval计算出表达式的计算结果。为了避免计算误差影响,需要用round函数处理结果,这里保留4位小数。如果表达式计算的结果正好是24,说明C4中的表达式满足要求,把结果记录在D5中。类似的,C6C8C10C12按照另外4种运算顺序拼出表达式,并把计算结果为24的表达式分别记录在D7D9D11D13中。

循环完成后,A14中取出所有满足要求的表达式,并用conj拼为一个序列。因为给出的牌有可能重复,因此结果可能存在相同的表达式,还需要用id去重。A14得到的结果如下:

..

上面的解法中,在对给定顺序的数和符号的组合,分析5种运算顺序时,实际上执行了类似的处理和判断,还可以用循环处理,进一步简化代码,如下:


A

B

C

D

1

[3,3,8,8]

[1234,1243,1324,1342,1423, 1432,2134,2143,2314,2341, 2413,2431,3124,3142,3214, 3241,3412,3421,4123,4132, 4213,4231,4312,4321]

[+,-,*,/]

=C1.conj@r(C1.(C1.(~ /get(1)/get(2))))

2

[]

[?/?/"("/?/?/"("/?/?/?/"))", ?/?/"(("/?/?/?/")"/?/?/")", "("/?/?/?/")"/?/"("/?/?/?/")", "(("/?/?/?/")"/?/?/")"/?/?, ?/?/"(("/?/?/?/")"/?/?/")"]

3

for B1

=A1(string(A3).split@p())



4


for D1

=B4.split()


5



>B2.(exp=eval(~,B3(1),C4(1),B3(2), C4(2),B3(3), C4(3), B3(4)), if(round(eval(exp),4)==24, A2|=exp))

6

=A2.id()




http://try.scudata.com.cn/try.jsp?splx=ExA009essd2.splx

A2准备存储结果,B2中将5种运算顺序对应的表达式列了出来,其中各个变量都用?表示。在循环中,C5对给定顺序的数与符号组合执行判断时,循环对B2中的每种情况判断,其中eval(s, x1,x2,…)可以用参数x1,x2,…的值依次替换字符串s中的?,这里按照所需顺序把数和符号间隔输入参数列表,就能得到对应的表达式exp,再次使用eval即可计算表达式的结果,如果得到24则将表达式记录在A2中。