分享一段用JS写的,求解24点的算法。思路是简单粗暴的穷举法。
该算法很灵活,不单单只能求解4个数。也可求解其它数量的数。而且目标也不单单是24,你也可以设置为求解其它值。先看下用法:
var numArr = [1,2,3,4];
if(true == permute(numArr)){
console.log(operationProcess);
}
- 你先自己定义一个数组,存放你要计算的数据。不一定是4个元素,也可以是其它的长度。内容也不限于1-9,也可以是更大的数字。例如
numArr = [7,5,9,25,75,66,84]
- 然后调用permute。如果它返回true,那你就可以在变量operationProcess中拿到计算步骤了。
无解的情况下,整个穷举过程大概0.1秒左右,有解时,则10ms就能解出答案。
界面是我自己写的,下面只给出计算部分的代码。
完整代码
const ops = ['+', '-', '*', '/'];
//用以存放计算过程的数组,每一个元素都是文本
var operationProcess = [];
//返回num op num2的结构,op是四则运算中的某一种运算符
function solve24_try_calculate_do(num1, op, num2){
switch(op){
case '+':
return num1 + num2;
case '-':
return num1 - num2;
case '*':
return num1 * num2;
case '/':
if(num2 == 0){return null;}
else{return num1 / num2;}
}
return null;
}
//递归,它可以将remining中的数排列到current中,会遍历出所可能的组合
function backtrack(current, remaining) {
if (remaining.length === 0) {//当remining为空时,意味着current拿到某一种组合
//我们把这个组合复制出来,并将它的头两个元素进行四则运算,得到一个新的数组。例如[a, b, c, d...]得到[a+b, c, d...]
var copyCurrent = JSON.parse(JSON.stringify(current));//深度复制,制造副本
var a = copyCurrent[0];
var b = copyCurrent[1];
copyCurrent.splice(0, 2);
//尝试加减乘除四种运算,得到四种新的数组,并做为新的递归参数
for (var i = 0; i < ops.length; i++) {
var ret = solve24_try_calculate_do(a, ops[i], b); //计算头两个元素
if(ret == null){continue;} //计算失败,例如被除数为0的情况,执行跳过
operationProcess.push(a + ops[i] + b + "=" + ret); //把计算步骤压入栈
copyCurrent.unshift(ret) //计算结果组成新的数组
if(true == permute(copyCurrent)){return true}; //递归,计算结果。如果返回true表示计算24点成功。此处进行了返回。某你想得到更多的解,此处可以不返回。并将当前栈保存起来。
copyCurrent.splice(0, 1); //去掉新元素,准备尝试进行其它运算符
operationProcess.splice(operationProcess.length-1, 1);//步骤出栈
}
}
//这里是backtrack的递归核心,原理是把remaining中某一个元素拿到current中,将进入下一层递归。
for (let i = 0; i < remaining.length; i++) {
current.push(remaining[i]);
const newRemaining = remaining.slice(0, i).concat(remaining.slice(i + 1));
if(backtrack(current, newRemaining) == true){return true};
current.pop();
}
}
function permute(nums) {
//如果nums只有一个值,则表示计算已经完成
if(nums.length == 1){
if(nums[0] == 24){//当值为24,意味着前面的计算有了成功的结果。返回true。此时可以在operationProcess中得到计算过程
// console.log("success");
// for (var i = 0; i < operationProcess.length; i++) {
// console.log(operationProcess[i]);
// }
return true;
}
else{
//若值不为24,则表示计算失败了,当前组合得不到24
return false;
}
}
//如果nums不是只有一个值,则把nums做为参数,进行遍历,以求它的所有排列组合进行四则运算
if(true == backtrack([], nums)){
return true;
}
return false;
}
如果你希望求解其它目标值,可以修改这一句代码:
if(nums[0] == 24)
如果你希望得到更多求解结果,你可以在这里不要返回true,并把当前operationProcess给深度复制,保存到其它地方。
if(true == permute(copyCurrent)){return true};
修改为
if(true == permute(copyCurrent)){
//自己实现一个方法来保存当前结果
save(JSON.parse(JSON.stringify(operationProcess)))
};
但递归求解的缺点是很多结果是重复的。例如10+14跟14+10,对于递归来讲,是两种不同的结果。但在人看来,就是一样的。
注意事项
每次调用前,你需要先清空operationProcess。否则它里面会保留上一次的步骤
operationProcess = [];
permute(numArr);