详细解读24点计算器的Javascript实现

简介: 详细解读24点计算器的Javascript实现

前段时间小舅子(小学生)过来玩,没事一起玩38=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),ab 、 ba 、(ab) 与(ba) 等等均互为不相同的计算(至于把他们认为是相同的计算,还需要更深入的研究了,这里的愚笨算法暂时无法区分)。

我们先确定四个空位(美其名曰 A、B、C、D),在计算的时候往这四个空位里填上这四个数字,如果按照我下面描述的计算方式最终得到 24 ,那么我们就认为这种数字与计算的组合就是一种"38=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】】);

//代码效果参考:http://www.zidongmutanji.com/bxxx/51176.html

}

}

}

}

} 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,请评论区留下邮箱。

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

相关文章
|
1月前
|
前端开发 JavaScript
百度搜索:蓝易云【用JavaScript和HTML实现一个精美的计算器网页】
该计算器网页使用HTML定义了页面结构,CSS样式使其具有精美的外观,而JavaScript脚本实现了计算器的逻辑。用户可以通过按钮输入数字和操作符,并通过“=”按钮来进行计算,计算结果会显示在文本框中。
55 6
|
9月前
|
JavaScript 前端开发
|
11天前
|
前端开发 JavaScript 算法
JavaScript制作简版计算器,提供加减乘除功能
JavaScript制作简版计算器,提供加减乘除功能
15 0
|
30天前
|
JavaScript 前端开发
JavaScript编写一个简易计算器
JavaScript编写一个简易计算器
20 0
|
1月前
|
前端开发 JavaScript
使用html+css+javaScript 完成计算器
使用html+css+javaScript 完成计算器
|
1月前
|
移动开发 JavaScript 前端开发
原生JavaScript+CSS实现计算器(简单的介绍一下eval函数)
原生JavaScript+CSS实现计算器(简单的介绍一下eval函数)
39 0
|
1月前
|
前端开发
好看的前端计算器代码分享(html+css+js制作计算器)
好看的前端计算器代码分享(html+css+js制作计算器)
49 0
|
1月前
|
前端开发 JavaScript
使用HTML、CSS和JavaScript实现一个简单的计算器
使用HTML、CSS和JavaScript实现一个简单的计算器
|
8月前
|
存储 搜索推荐 数据可视化
基于html5+javascript技术开发的房贷利率计算器,买房的码农们戳进来
房贷计算器是一款专为购房者设计的实用工具应用,其主要功能是帮助用户详细计算房贷的还款金额、利息以及还款计划等。通过这款软件,用户可以更加便捷地了解到自己的还款情况和计划,从而更好地规划自己的财务。下面将对房贷计算器进行详细的介绍。
72 0
|
9月前
|
JavaScript Android开发
js日期控件 (补助计算器页面)
js日期控件 (补助计算器页面)
46 0