24点计算器的Javascript实现

前段时间小舅子(小学生)过来玩,没事一起玩3*8=24,遇到难算的半天都想不出来,所以就想有没有app或者小工具啥的,搜了一下,有工具,但是不好用,所以就想自己写个简单易用的。

开始着手做的时候,发现运算逻辑无法总结成简单的计算公式,百度也没找到有人完整的实现,最后只能用最笨的方法了,且听我娓娓道来。

首先,按照常规,共有四个正整数参与运算,取值区间均为 [1,10](包含1和10),仅有加减乘除以及括号运算,不存在其他高级运算(如平方、开方等等),这四个数的计算顺序不确定,每个数字参与且仅参与一次运算。

按数学的思路来对这个常规描述进行解析,如下:

  • 四个数 a、b、c、d ;
  • 取值范围 1<=a<=10,1<=b<=10,1<=c<=10,1<=d<=10;
  • 运算方式 :+、 -、 *、 / 以及 () ;

运算顺序不确定,所以我们暂时认为 a+b 、 b+a 、(a+b) 与 (b+a)a*b 、 b*a 、(a*b) 与(b*a) 等等均互为不相同的计算(至于把他们认为是相同的计算,还需要更深入的研究了,这里的愚笨算法暂时无法区分)。

我们先确定四个空位(美其名曰 A、B、C、D),在计算的时候往这四个空位里填上这四个数字,如果按照我下面描述的计算方式最终得到 24 ,那么我们就认为这种数字与计算的组合就是一种"3*8=24"的计算方法,否则,我们继续遍历。

我仔细思考了一下,这四个空位的结合顺序一共有 5 种

1、{[(A,B)C]D}

2、{[A(B,C)]D}

3、{A[(B,C)D]}

4、{A[B(C,D)]}

5、[(A,B),(C,D)]

有了这些结合顺序之后,我们只需要把我们的四个数字分别放到这四个空位中,并且在这四个空位之间分别放上四种计算方式(+、 -、 *、 /)中的一种,然后根据这个组合计算出结果,并判断是否等于24,如果是,那么就找到了一种,否则继续往下执行。

所以,看到此处,大家可以思考一下,这种愚笨的算法所需要的循环层次有几级。

绝对不会有 8 级。

也不会少于 4 级。

通过巧妙的数据结构设计,我将循环的层次控制在了4级,还好运算量不大,速度是可以接受的,不然写一个4级循环来实现功能真是要命(被嫌弃死)。

首先,由于每个数字必须参与运算且只能参与一次运算,我必须先把这些数字的排列顺序找出来。比如 1,2,3,4四个数字,他们的排列为:1234、1243、1324、1342.等等。

var minNum = 1, //最小值
    maxNum = 10,//最大值
    opt = [‘+‘, ‘-‘, ‘*‘, ‘/‘],//运算
    dataStruct = function (a, b, c, d) {
        //构建特殊数据结构
        this[1] = a;
        this[2] = b;
        this[4] = c;
        this[8] = d;
    },
    getGroup = function (data) {
        //对dataStruct结构的数字进行排列组合
        var group = [];
        try {
            for (var i1 = 1; i1 <= 8; i1 *= 2) {
                for (var i2 = 1; i2 <= 8; i2 *= 2) {
                    for (var i3 = 1; i3 <= 8; i3 *= 2) {
                        for (var i4 = 1; i4 <= 8; i4 *= 2) {
                            if ((i1 | i2 | i3 | i4) != 0xf) continue;
                            group.push([data[i1], data[i2], data[i3], data[i4]]);
                        }
                    }
                }
            }
        } catch (e) {
            throw e.message;
        }
        return group;
    };

接下来我们输入一组数字进行测试:

//测试数据
var test = getGroup(new dataStruct(1, 2, 3, 4));
console.log(test);

测试结果:

在此,可以做一点小优化,去掉重复组合。如果四个数字存在两个以上的重复,那么就会出现重复的组合,这些重复的组合没必要多次参与计算,故可以在此去掉。修改后的getGroup方法如下:

getGroup = function (data) {
    //对dataStruct结构的数字进行排列组合
    var group = [],
        repeat = ‘‘;
    try {
        for (var i1 = 1; i1 <= 8; i1 *= 2) {
            for (var i2 = 1; i2 <= 8; i2 *= 2) {
                for (var i3 = 1; i3 <= 8; i3 *= 2) {
                    for (var i4 = 1; i4 <= 8; i4 *= 2) {
                        if ((i1 | i2 | i3 | i4) != 0xf) continue;
                        var str = "" + data[i1] + data[i2] + data[i3] + data[i4];
                        //过滤重复组合
                        if (repeat.indexOf(str) > -1) continue;
                        repeat += str + ",";
                        group.push([data[i1], data[i2], data[i3], data[i4]]);
                    }
                }
            }
        }
    } catch (e) {
        throw e.message;
    }
    return group;
};

传入测试数据 1,2,3,3进行测试:(左边是过滤前的结果,右边是过滤后的结果)

(过滤前)                                 (过滤后)

封装了一个简单的运算函数(主要应对类似除数为0这种情况):

function operate(f, m, n) {
    //简单的计算函数,正常情况返回计算结果,异常情况返回 NaN
    if (isNaN(m) || isNaN(n)) return NaN;
    if (f == ‘*‘) return (m * n);
    else if (f == ‘/‘) return n ? (m / n) : NaN;//如果除数为0,则返回 NaN
    else if (f == ‘-‘) return (m - n);
    else return (parseFloat(m) + parseFloat(n));
}

接着写具体计算(也就是前面说的四个空位):

function compute(a, b, c, d, opt1, opt2, opt3) {
    ///获取一组数字的计算结果
    ///abcd为4个计算数
    ///opt1,opt2,opt3为这四个数依次的运算符号
    var result = []; //定义数组保存计算结果
    try {
        //开始根据5种结合方式进行计算

        //第一种:{[(A,B)C]D}
        var r1 = operate(opt1, a, b);
        var r2 = operate(opt2, r1, c);
        var r3 = operate(opt3, r2, d);
        if (!isNaN(r3) && Math.abs((r3 - 24)) < 1e-5) { //由于计算结果可能出现浮点数,这里的比较必须使用浮点数比较方式
            result.push(‘[(‘ + a + opt1 + b + ‘)‘ + opt2 + c + ‘]‘ + opt3 + d + ‘………………1‘);
        }

        //第二种 {[A(B,C)]D}
        r1 = operate(opt1, b, c);
        r2 = operate(opt2, a, r1);
        r3 = operate(opt3, r2, d);
        if (!isNaN(r3) && Math.abs((r3 - 24)) < 1e-5) {
            result.push(‘[‘ + a + opt2 + ‘(‘ + b + opt1 + c + ‘)]‘ + opt3 + d + ‘………………2‘);
        }

        //第三种 {A[(B,C)D]}
        r1 = operate(opt1, b, c);
        r2 = operate(opt2, r1, d);
        r3 = operate(opt3, a, r2);
        if (!isNaN(r3) && Math.abs((r3 - 24)) < 1e-5) {
            result.push(a + opt3 + ‘[(‘ + b + opt1 + c + ‘)‘ + opt2 + d + ‘]………………3‘);
        }

        //第四种 {A[B(C,D)]}
        r1 = operate(opt1, c, d);
        r2 = operate(opt2, b, r1);
        r3 = operate(opt3, a, r2);
        if (!isNaN(r3) && Math.abs((r3 - 24)) < 1e-5) {
            result.push(a + opt3 + ‘[‘ + b + opt2 + ‘(‘ + c + opt1 + d + ‘)]………………4‘);
        }

        //第五种 [(A,B),(C,D)]
        r1 = operate(opt1, a, b);
        r2 = operate(opt2, c, d);
        r3 = operate(opt3, r1, r2);
        if (!isNaN(r3) && Math.abs((r3 - 24)) < 1e-5) {
            result.push(‘(‘ + a + opt1 + b + ‘)‘ + opt3 + ‘(‘ + c + opt2 + d + ‘)………………5‘);
        }

    } catch (e) { }
    return result;
}

接下来就是向空位中填数字(有简单的去重操作,但对于复杂的去重,比如 (1+3)*(3+3)与(3+3)*(3+1)还需继续研究):

function getResult(group) {
    var result = [],
        repeat = ‘‘;
    for (var g = 0; g < group.length; g++) {
        for (var i = 0; i < 4; i++) {
            for (var j = 0; j < 4; j++) {
                for (var k = 0; k < 4; k++) {
                    var a1 = group[g][0],
                        a2 = group[g][1],
                        a3 = group[g][2],
                        a4 = group[g][3];

                    var tmp = compute(a1, a2, a3, a4, opt[i], opt[j], opt[k]);
                    for (var t = 0; t < tmp.length; t++) {
                        //简单去重
                        if (repeat.indexOf(tmp[t]) > -1) {
                            continue;
                        }
                        result.push(tmp[t]);
                        repeat += tmp[t] + ‘,‘;
                    }
                }
            }
        }
    }
    return result;
}

好了,贴出测试效果:

这个是 1、3、3、3

这是 1、2、3、4:

这是5、8、6、5:

最后送上一个比较难计算的:1、4、5、6

以上就是整个实现过程,我没有按照先开发原型后迭代优化的流程来描述,而是直接贴出了最后的版本,当然这个版本也是相当愚笨的,特别是在去重方面,还需要大大的改进,至于计算速度的话没啥可担心的,后续有空还会继续深入研究。

按照这个思路,其他开发语言要写出来也不是难事,自己也写了C# 版本的,此文不再赘述。

再来说说后续可以做的事情吧:

结果去重,这个是首要,也是难点;

UI设计,输入以及结果展示UI;

可对抗性设计,可以设计成休闲游戏。

作者:乔二哥

如需 apk,请评论区留下邮箱。

本文禁止转载,特此说明。

原文地址:https://www.cnblogs.com/qiaoge0923/p/9794386.html

时间: 2024-11-01 00:37:29

24点计算器的Javascript实现的相关文章

[24点计算器][C++版本]无聊拿去玩

程序用法: 输入n,tar,lim:分别表示n个数,目标答案,最多计算lim个解(0表示输出所有解) 然后输入n个数字 例: 4 24 3 1 2 3 4 表示4个数字1,2,3,4要算出24的前三个解 一般6个数找所有答案开O2需要一分半吧.. 思路(当然是爆枚咯...): 由于中缀表达式枚举括号比较麻烦,所以用后缀表达式枚举运算符,最后再把后缀转成中缀输出即可. 但这样还枚举不全,需要枚举数字的全排列再把运算符填进去. 这样虽然可以枚举所有情况,但是因为交换律的存在大量重复出现(比如2*3*

JavaScript运算符

JavaScript中五种常见运算符 一. in运算符 in运算符希望它的左操作数是一个字符串或可以转换为字符串,希望它的右操作数是一个对象.如果右侧的对象拥有一个名为左操作数值的属性名,那么表达式返回true.例如: 1 2 3 4 5 6 7 8 9 var point = {x:1, y:1}; 'x' in point    //=>true:对象有一个名为'x'的属性 'z' in point    //=>false:对象中不存在名为'z'的属性 'toString' in poi

javascript数组去重算法-----4(另一种写法)

1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>javascript数组去重算法-----3</title> 6 </head> 7 <body> 8 <script> 9 var arr = [1,1,2,2,3,2,2,3,3,1,1,4,4,5

第一讲:初识JavaScript

1.0. 学习目标 –了解javascript组成部分 –认识javascript解析机制 –如何使用javascript 1.1.初识JavaScript •javascript是一种专为与网页交互儿设计的脚本语言.由三部分组成: –ECMAScript  (ECMA-262定义)  提供核心语言功能 –文档对象模型(DOM)提供访问和操作网页内容的方法和接口 –浏览器对象模型(BOM)提供与浏览器交互的方法和接口 •Javascript的这三个组成部分在当前五大主流浏览器中都得到了不同程度的

JavaScript的基础应用

1 <!DOCTYPE html> 2 <!--JavaScript基础1--> 3 <html lang="en"> 4 <head> 5 <meta charset="UTF-8"> 6 <title>Title</title> 7 </head> 8 <body> 9 10 <idv> script> alert("JavaS

javascript开源大全

javascript开源大全 Ajax框架-jQuery 可视化HTML编辑器-CKEditor 国产jQuery-UI框架-(jUI)-DWZ 网页开发FireFox插件-Firebug 服务器端的JavaScript脚本-Node.js jQuery图表插件-jQchart HTML5-开发框架-jQuery-Mobile 跨浏览器的RIA框架-ExtJS Flash视频播放器-JW-PLAYER jQuery表单插件-jQuery.form jQuery-File-Upload 可视化HT

Javascript基本代码

简单的了解了javascript 的基本代码,感觉和c#中的语句差不多. 1 <!DOCTYPE html> 2 <html xmlns="http://www.w3.org/1999/xhtml"> 3 <head> 4 <!--利用var俩声明变量,仅仅声明变量,而不指定具体类型--> 5 <title>javascript study</title> 6 <!-- <script src=&quo

javascript变量和对象要注意的点

js对象和变量,对象本身是没有名称的,之所以使用变量是为了通过某个名称来称呼这样一种不具名称的对象.变量分基本变量和引用类型变量.将基本类型的的值付给变量的话,变量将这个值本身保存起来. 1 <script type="text/javascript"> 2 function p(){ 3 var len=arguments.length; 4 for(var i=0;i<len;i++){ 5 document.write(arguments[i]+"&l

javascript没那么简单(转)

原文出处:http://www.cnblogs.com/bestfc/archive/2010/08/02/1790173.html 写在前面:似乎园子里最近少了些人,各个文章的阅读量少了许多废话不说,写此文目的是为了让更多的程序员理解javascript的一些概念,对是理解不是了解我们已经了解得够多了,该是向深入理解的方向靠拢的时候了为什么这么说,前些日子收到面试邀请,那就去试试呗,有几年没有面试过了吧和面试官坐在沙发上,聊天式的他问我答,以下就是几个javascript方面的问题>请创建一个