趣味集算:算二十四
算 24 点,是一种常见的智力游戏。它可以使用抽去大小王的扑克牌来玩,随便抽出 4 张牌,要求通过加、减、乘、除等四则运算,并使用所有的这 4 个数,将结果算为 24。在玩这个游戏时,扑克牌中的 JQK 记为 11,12 和 13。
用集算器,可以比较方便地编写程序,根据随便给定的 4 个数,计算出游戏的解答,如下面的 24points.dfx:
A | B | C | D | |
---|---|---|---|---|
1 | =arg1 | [+,-,*,/] | [] | [] |
2 | =to(0,255).([~\64+1, ~%64\16+1,~%16\4+1,~%4+1]) | =A2.select(~.id().count()==4) | ||
3 | =to(0,63).([~\16+1, ~%16\4+1,~%4+1]) | =A3.select(~.eq([1,2,3])) | =A3.(~.(B1(~))) | |
4 | =B2.(~.(A1(~))) | =A4.id() | ||
5 | for B4 | for B3 | for C3 | >func(A8,A5,B5,C5) |
6 | =C1.id(~) | for A6 | if round(eval(B6),3) ==24 | >D1=D1|B6 |
7 | if D1.len()==0 | >D1=”No answer.” | ||
8 | func | =A8.(~) | ||
9 | =B8.(~) | for B9 | >B9.(~=~-if(#>#C9 && ~>C9,1,0)) | |
10 | for B9 | =D8(B10)/C8(#B10)/ D8(B10+1) | >D8.delete(B10) | |
11 | if #B10==3 | >C1=C1&C10 | ||
12 | else | >D8(B10)=”(“/C10/”)” | ||
13 | return D1 |
其中,arg1 用参数输入使用的 4 张牌上的数,参数也可在 A1 中查看:
下面,详细分析一下这段代码。
计算使用的 4 个数在 A1 中给出,这里用的四张牌是 8,3,8,3。首先,在算 24 点时,应该尝试 4 张牌的所有顺序。所以,先在 A2 中,利用一个 4 位的 4 进制数,来列出 4 张牌的可重复排列情况。在 B2 中,仅选出不重复排列的情况如下:
在计算时,4 个数中间共需要插入 3 个四则运算符号,每个符号都可以从加减乘除中 4 选 1。在 A3 中循环一个 3 位的 4 进制数,列出所有情况如下:
在计算时,不同的计算顺序会影响结果,可以通过添加括号来调整计算顺序。3 个运算符号,说明计算可以分为 3 个位置执行,添加括号即是决定这 3 个位置的执行顺序。在 B3 中,从 A3 的结果中选择了同时包含 1,2,3 的组合,即为所有可能的执行顺序如下:
由于选出的 4 张牌中,有可能出现重复的数,为了避免因此造成的多余循环,在 A4 中把所有的牌面排列顺序列出,并在 B4 中去掉了可能出现的重复的排列情况。对于 A1 中给出的 [8,3,8,3],B4 中列出排列结果如下:
在第 5 行的程序中,对于每种数的排列,循环每一种符号选择,并循环所有的计算顺序,调用 A8 中的子程序计算。
调用 A8 中的子程序时,数值序列、符号序列和计算顺序序列,分别填到 A8,B8,C8 中,并在 D8 中准备计算使用的表达式。单步计算完成后,原有的总计算步数就少了 1 步,还需要在第 9 行中相应调整计算顺序序列。在 B10 中执行循环,每计算一步,都将两个数或表达式用计算符号连接起来,同时在 D8 中删除 1 个占位。在第 11 行执行判断,对于最后一步计算,将把获得的表达式添加到序列 C1 中。如果是中间步骤,则会在新生成的表达式中添加括号,以保证计算顺序,并将这部分表达式更新到 D8 中。
这样,当第 5 行代码中循环调用子程序完毕,就在 C1 中得到了所有可能出现的表达式。其中会有一些重复的情况,因此在 A6 中用 id() 函数去掉重复的表达式。C1 和 A6 中的表达式序列如下:
在 B6 中循环 A6 中的各个表达式,判断结果是否等于 24,判断时只需用 eval 函数计算表达式的结果即可,考虑到双精度数的计算误差,结果保留 3 位小数。如果结果等于 24,说明当前个表达式可以满足条件,在 D6 中,将其存储到 D1 中。
在所有的情况都循环完毕后,在 A7 中,如果发现 D1 中未填入任何表达式,则说明无解。
计算完成后,就可以在 D1 中查看结果如下:
如果需要修改使用的 4 张牌,则可在计算前修改网格参数 arg1,如修改为 [7,3,3,7]:
计算后,D1 中查看到的结果如下:
可以在另一段程序中,通过调用 24points.dfx,计算出所有 4 张扑克牌组合的结果:
A | B | C | |
---|---|---|---|
1 | =file(“d:/file/24points.dfx”) | =create(Num1,Num2,Num3,Num4,Answer) | |
2 | 13 | =A2*A2 | =B2*A2 |
3 | =to(0,A2*C2-1).([~\C2+1, ~%C2\B2 +1,~%B2\A2+1,~%A2+1]) | =A3.(~.sort()).id() | >B3.run((a=~,B1.insert(0,a(1),a(2),a(3),a(4),call(A1,a)))) |
4 | =file(“d:/file/24points.txt”) | >A4.export@t(B1) |
在这段代码中,A3 中列出 4 张扑克牌的所有组合:
由于同样的 4 张牌只是打乱顺序,它们的解答是相同的,所以在 B3 中将 A3 中的组合排序整理,并去掉重复的情况,以便减少运算次数:
在 C3 中,针对 B3 中的每一种情况,调用 24points.dfx 来计算结果,并将结果记录在 B1 的表中,计算结束后,B1 中得到的结果如下:
在第 4 行,把计算得到的结果记录在文件中,集算器支持多种格式的文件,为了便于查看,这里选择存储到文本文件 24points.txt 中,存储结果如下:
👍