c#和javascript轻松实现计算24点改进版
在上一篇“c#和javascript实现计算24点”后,有网友给我发邮件,指出c#源码中的一处讹误,现在已经改正,在此鸣谢一下,然后还提出改进的建议,并给出思路,说用逆波兰表达式可以更简单的实现。
其实,上一篇的核心实现分为两部分,第二部分属于“投机取巧”地利用语言工具的函数或方法,这点我有言在先。不过这也是我们认为的实现的“缺陷”部分(函数或方法毕竟是人家已经封装好的,还是自己实现一遍更踏实)。下面我就改进一下代码,用构造逆波兰表达式的方式回炉再造一遍,注释写的很清楚,上代码吧:
c#代码实现:
分别运行上面的代码,我们发现输入相同顺序的数字,c#和js产生的表达式可能不同,这是因为输入相同的数字,可以有不同的加减乘除组合方式使产生的结果相同。有人发邮件问如何分辨出产生的结果有无重复,如何一次取出所有的加减乘除组合方式使之等于预期的值(比如24)。我想说这两个问题之前我真的没想过(平时玩游戏,先算出来就算赢,一种表达式足矣),解决起来不是很难,我用c#实现了一种无重复表达式的实现,只要改进一个方法就可以了:
最后,我喜欢这样的拍砖,欢迎大家继续。
其实,上一篇的核心实现分为两部分,第二部分属于“投机取巧”地利用语言工具的函数或方法,这点我有言在先。不过这也是我们认为的实现的“缺陷”部分(函数或方法毕竟是人家已经封装好的,还是自己实现一遍更踏实)。下面我就改进一下代码,用构造逆波兰表达式的方式回炉再造一遍,注释写的很清楚,上代码吧:
c#代码实现:
Code
using System;
using System.Windows.Forms;
using System.Collections;
using System.Collections.Generic;
namespace MyCsStudy
{
/// <summary>
/// 24点算法
/// </summary>
class TwentyFour
{
public static List<string> listOps = new List<string>(); //存加减乘除运算符
static TwentyFour()
{
listOps.Add("+");
listOps.Add("-");
listOps.Add("*");
listOps.Add("/");
}
public static bool Computing(string[] numArr, int numLen, int targetNum)
{
bool flag = false;
for (int i = 0; i < numLen; i++)
{
for (int j = i + 1; j < numLen; j++)
{
string[] strij = new string[] { numArr[i], numArr[j] };
numArr[j] = numArr[numLen - 1];
for (int k = 0; k < listOps.Count; k++)
{
numArr[i] = "(" + strij[0] + listOps[k] + strij[1] + ")";
if (Computing(numArr, numLen - 1, targetNum))
{
flag = true;
return flag;
}
}
numArr[i] = strij[0];
numArr[j] = strij[1];
}
}
string expression = numArr[0];
/*
* 这里是改进的部分:
* 不在利用datatable的compute方法计算字符串表达式
* **************利用构造逆波兰表达式计算结果******************
*/
int result = (int)Math.Ceiling(RpnExpression.GetResult(expression)); //double类型 转换为整数
//如果运算结果是预期的数字,而且数组中的数字全部在运算范畴内
if (result == targetNum && numLen == 1)
flag = true;
return flag;
}
public static string GetExpression(string[] numArr, int targetNum)
{
string result = "没有匹配的表达式使之等于" + targetNum.ToString();
if (Computing(numArr, numArr.Length, targetNum))
{
result = numArr[0].Substring(1, numArr[0].Length - 2)+"="+targetNum.ToString();
}
return result;
}
}
class Program
{
static void Main(string[] args)
{
string[] numArr = { "2", "4", "8", "16" }; //正常都是4个数字,本程序不限制个数
string result = TwentyFour.GetExpression(numArr, 24); // 预期的目标计算结果可以不是24
Console.WriteLine(result);
}
}
}
利用逆波兰表达式计算字符串表达式结果的类:using System;
using System.Windows.Forms;
using System.Collections;
using System.Collections.Generic;
namespace MyCsStudy
{
/// <summary>
/// 24点算法
/// </summary>
class TwentyFour
{
public static List<string> listOps = new List<string>(); //存加减乘除运算符
static TwentyFour()
{
listOps.Add("+");
listOps.Add("-");
listOps.Add("*");
listOps.Add("/");
}
public static bool Computing(string[] numArr, int numLen, int targetNum)
{
bool flag = false;
for (int i = 0; i < numLen; i++)
{
for (int j = i + 1; j < numLen; j++)
{
string[] strij = new string[] { numArr[i], numArr[j] };
numArr[j] = numArr[numLen - 1];
for (int k = 0; k < listOps.Count; k++)
{
numArr[i] = "(" + strij[0] + listOps[k] + strij[1] + ")";
if (Computing(numArr, numLen - 1, targetNum))
{
flag = true;
return flag;
}
}
numArr[i] = strij[0];
numArr[j] = strij[1];
}
}
string expression = numArr[0];
/*
* 这里是改进的部分:
* 不在利用datatable的compute方法计算字符串表达式
* **************利用构造逆波兰表达式计算结果******************
*/
int result = (int)Math.Ceiling(RpnExpression.GetResult(expression)); //double类型 转换为整数
//如果运算结果是预期的数字,而且数组中的数字全部在运算范畴内
if (result == targetNum && numLen == 1)
flag = true;
return flag;
}
public static string GetExpression(string[] numArr, int targetNum)
{
string result = "没有匹配的表达式使之等于" + targetNum.ToString();
if (Computing(numArr, numArr.Length, targetNum))
{
result = numArr[0].Substring(1, numArr[0].Length - 2)+"="+targetNum.ToString();
}
return result;
}
}
class Program
{
static void Main(string[] args)
{
string[] numArr = { "2", "4", "8", "16" }; //正常都是4个数字,本程序不限制个数
string result = TwentyFour.GetExpression(numArr, 24); // 预期的目标计算结果可以不是24
Console.WriteLine(result);
}
}
}
Code
using System;
using System.Text;
using System.Collections;
namespace MyCsStudy
{
/// <summary>
/// 逆波兰表达式
/// </summary>
public class RpnExpression
{
/// <summary>
/// 加减乘除 四则运算
/// </summary>
/// <param name="prevNum"></param>
/// <param name="nextNum"></param>
/// <param name="opration"></param>
/// <returns></returns>
private static double DoOperation(double prevNum, double nextNum, char opration)
{
double result = 0;
switch (opration)
{
case '+':
result = prevNum + nextNum;
break;
case '-':
result = prevNum - nextNum;
break;
case '*':
result = prevNum * nextNum;
break;
case '/':
if (nextNum != 0)
{
result = prevNum / nextNum;
}
else
{
throw new ArgumentException("0不能作为除数"); //输入数字中不能为0,
}
break;
default:
result = 0;
break;
}
return result;
}
/// <summary>
/// 获取操作符的优先级,加减优先级低于乘除
/// </summary>
/// <param name="opration"></param>
/// <returns></returns>
private static int GetOperationPriority(char opration)
{
switch (opration)
{
default:
return 0;
case '+':
case '-':
return 1;
case '*':
case '/':
return 2;
}
}
/// <summary>
/// 生成逆波兰表达式
/// </summary>
/// <param name="exp"></param>
/// <returns></returns>
public static string CreateRpnExpression(string exp)
{
Stack skOp = new Stack();//定义操作符堆栈
string operations = "+-*/"; //加减乘除运算符4个
StringBuilder sbExpression = new StringBuilder();//逆波兰表达式
char[] charExp = exp.ToCharArray();//将中缀表达式转换为char数组
string digit = string.Empty;//数字字符串
for (int i = 0; i < charExp.Length; i++)
{
char chr = charExp[i];
if (char.IsDigit(chr))//如果是数字,添加到数字字符串中
{
digit += chr;
}
else if (operations.IndexOf(chr) >= 0)//如果是运算符
{
if (digit.Length > 0)
{
sbExpression.Append("<" + digit + ">");//首先将数字添加到逆波兰表达式
digit = string.Empty;
}
//弹出操作符并添加到逆波兰表达式,直至遇到左括号或优先级较低的操作符
while (skOp.Count > 0)
{
char opInStack = (char)skOp.Pop();
if (opInStack == '(' || GetOperationPriority(opInStack) < GetOperationPriority(chr))
{
skOp.Push(opInStack);
break;
}
else
{
sbExpression.Append(opInStack);
}
}
skOp.Push(chr);//将当前操作符压入堆栈中
}
else if (chr == '(')//遇到左括号,直接压入堆栈中
{
skOp.Push(chr);
}
else if (chr == ')')//遇到右括号
{
if (digit.Length > 0)//先将数字添加到逆波兰表达式
{
sbExpression.Append("<" + digit + ">");
digit = string.Empty;
}
while (skOp.Count > 0)//弹出运算符并添加到逆波兰表达式,直至遇到左括号
{
char opInStack = (char)skOp.Pop();
if (opInStack == '(')
{
break;
}
else
{
sbExpression.Append(opInStack);
}
}
}
}
//到达字符串末尾后,首先将数字添加到逆波兰表达式
if (digit.Length > 0)
{
sbExpression.Append("<" + digit + ">");
}
//弹出所有操作符并添加到逆波兰表达式
while (skOp.Count > 0)
{
char opInStack = (char)skOp.Pop();
sbExpression.Append(opInStack);
}
return sbExpression.ToString();
}
/// <summary>
/// 获取逆波兰表达式的值。
/// </summary>
/// <param name="rpnExp"></param>
/// <returns></returns>
public static double GetResultByRpnExp(string rpnExp)
{
Stack stack = new Stack();
char[] expChar = rpnExp.ToCharArray();
string digit = string.Empty;
double result = 0;
for (int i = 0; i < expChar.Length; i++)
{
char c = expChar[i];
if (c == '<')
{
digit = string.Empty;
}
else if (c == '>')
{
stack.Push(digit);
}
else if (char.IsDigit(c))
{
digit += c.ToString();
}
else if (c == '+' || c == '-' || c == '*' || c == '/')
{
double nextNum = Convert.ToDouble(stack.Pop()); //第一个数字出栈
double prevNum = Convert.ToDouble(stack.Pop());//第二个数字出栈
result = DoOperation(prevNum, nextNum, c); //运算
stack.Push(result); //运算结果入栈
}
}
return result;
}
/// <summary>
/// 最后,计算逆波兰表达式的值 (将中缀表达式转换为后缀表达式(逆波兰表达式),然后根据逆波兰表达式计算出结果)
/// </summary>
/// <param name="exp">加减乘除和括号构成的表达式</param>
/// <returns></returns>
public static double GetResult(string exp)
{
string rpn = CreateRpnExpression(exp);//构造逆波兰表达式
return GetResultByRpnExp(rpn); //根据逆波兰表达式计算结果
}
}
}
javascript代码:using System;
using System.Text;
using System.Collections;
namespace MyCsStudy
{
/// <summary>
/// 逆波兰表达式
/// </summary>
public class RpnExpression
{
/// <summary>
/// 加减乘除 四则运算
/// </summary>
/// <param name="prevNum"></param>
/// <param name="nextNum"></param>
/// <param name="opration"></param>
/// <returns></returns>
private static double DoOperation(double prevNum, double nextNum, char opration)
{
double result = 0;
switch (opration)
{
case '+':
result = prevNum + nextNum;
break;
case '-':
result = prevNum - nextNum;
break;
case '*':
result = prevNum * nextNum;
break;
case '/':
if (nextNum != 0)
{
result = prevNum / nextNum;
}
else
{
throw new ArgumentException("0不能作为除数"); //输入数字中不能为0,
}
break;
default:
result = 0;
break;
}
return result;
}
/// <summary>
/// 获取操作符的优先级,加减优先级低于乘除
/// </summary>
/// <param name="opration"></param>
/// <returns></returns>
private static int GetOperationPriority(char opration)
{
switch (opration)
{
default:
return 0;
case '+':
case '-':
return 1;
case '*':
case '/':
return 2;
}
}
/// <summary>
/// 生成逆波兰表达式
/// </summary>
/// <param name="exp"></param>
/// <returns></returns>
public static string CreateRpnExpression(string exp)
{
Stack skOp = new Stack();//定义操作符堆栈
string operations = "+-*/"; //加减乘除运算符4个
StringBuilder sbExpression = new StringBuilder();//逆波兰表达式
char[] charExp = exp.ToCharArray();//将中缀表达式转换为char数组
string digit = string.Empty;//数字字符串
for (int i = 0; i < charExp.Length; i++)
{
char chr = charExp[i];
if (char.IsDigit(chr))//如果是数字,添加到数字字符串中
{
digit += chr;
}
else if (operations.IndexOf(chr) >= 0)//如果是运算符
{
if (digit.Length > 0)
{
sbExpression.Append("<" + digit + ">");//首先将数字添加到逆波兰表达式
digit = string.Empty;
}
//弹出操作符并添加到逆波兰表达式,直至遇到左括号或优先级较低的操作符
while (skOp.Count > 0)
{
char opInStack = (char)skOp.Pop();
if (opInStack == '(' || GetOperationPriority(opInStack) < GetOperationPriority(chr))
{
skOp.Push(opInStack);
break;
}
else
{
sbExpression.Append(opInStack);
}
}
skOp.Push(chr);//将当前操作符压入堆栈中
}
else if (chr == '(')//遇到左括号,直接压入堆栈中
{
skOp.Push(chr);
}
else if (chr == ')')//遇到右括号
{
if (digit.Length > 0)//先将数字添加到逆波兰表达式
{
sbExpression.Append("<" + digit + ">");
digit = string.Empty;
}
while (skOp.Count > 0)//弹出运算符并添加到逆波兰表达式,直至遇到左括号
{
char opInStack = (char)skOp.Pop();
if (opInStack == '(')
{
break;
}
else
{
sbExpression.Append(opInStack);
}
}
}
}
//到达字符串末尾后,首先将数字添加到逆波兰表达式
if (digit.Length > 0)
{
sbExpression.Append("<" + digit + ">");
}
//弹出所有操作符并添加到逆波兰表达式
while (skOp.Count > 0)
{
char opInStack = (char)skOp.Pop();
sbExpression.Append(opInStack);
}
return sbExpression.ToString();
}
/// <summary>
/// 获取逆波兰表达式的值。
/// </summary>
/// <param name="rpnExp"></param>
/// <returns></returns>
public static double GetResultByRpnExp(string rpnExp)
{
Stack stack = new Stack();
char[] expChar = rpnExp.ToCharArray();
string digit = string.Empty;
double result = 0;
for (int i = 0; i < expChar.Length; i++)
{
char c = expChar[i];
if (c == '<')
{
digit = string.Empty;
}
else if (c == '>')
{
stack.Push(digit);
}
else if (char.IsDigit(c))
{
digit += c.ToString();
}
else if (c == '+' || c == '-' || c == '*' || c == '/')
{
double nextNum = Convert.ToDouble(stack.Pop()); //第一个数字出栈
double prevNum = Convert.ToDouble(stack.Pop());//第二个数字出栈
result = DoOperation(prevNum, nextNum, c); //运算
stack.Push(result); //运算结果入栈
}
}
return result;
}
/// <summary>
/// 最后,计算逆波兰表达式的值 (将中缀表达式转换为后缀表达式(逆波兰表达式),然后根据逆波兰表达式计算出结果)
/// </summary>
/// <param name="exp">加减乘除和括号构成的表达式</param>
/// <returns></returns>
public static double GetResult(string exp)
{
string rpn = CreateRpnExpression(exp);//构造逆波兰表达式
return GetResultByRpnExp(rpn); //根据逆波兰表达式计算结果
}
}
}
Code
var opArr =new Array("+", "-", "*","/"); //运算符数组
/*
参数说明:
1、numArr:输入的四个数字
2、numLen:输入的数字个数
3、targetNum:输入四个数字后要计算出的结果(24,etc)
*/
function computeNum(numArr, numLen, targetNum) {
for (var i = 0; i < numLen; i++) {
for (var j = i + 1; j < numLen; j++) {
var numij = [numArr[i], numArr[j]];
numArr[j] = numArr[numLen - 1];
for (var k = 0; k < opArr.length; k++) {
numArr[i] = '(' + numij[k % 2] + opArr[k] + numij[(!(k % 2) * 1)] + ')'; //构建算术表达式,然后利用自定义函数根据逆波兰表达式计算结果
if (computeNum(numArr, numLen - 1, targetNum)) {
return true;
}
}
numArr[i] = numij[0];
numArr[j] = numij[1];
}
}
var tmpStr=numArr[0];
//不通过js的eval函数直接算出预期结果targetNum,我们利用逆波兰表达式计算结果
var result=Math.ceil( funcGetResult(numArr[0]));
return (numLen == 1) && (result == targetNum);
}
//获取最终的计算表达式
function getExpression(numArr,targetNum) {
if (computeNum(numArr, numArr.length, targetNum)) {
var str = numArr[0].substring(1, numArr[0].length - 1);
return numArr[0].substring(1, numArr[0].length - 1)+"="+String(targetNum);
} else {
return "没有匹配的表达式使之等于" + targetNum;
}
}
//单击按钮,显示结果
function showExpression() {
var txtNumberArr=document.getElementsByName("txtNumber");
var txtTargetNum=document.getElementById("txtTargetNumber").value;
var txtResult=document.getElementById("txtExpression");
var numsArr=new Array();
for(var i=0;i<txtNumberArr.length;i++){
numsArr.push(txtNumberArr[i].value);
}
txtResult.value=getExpression(numsArr,txtTargetNum);
}
对应的javascript逆波兰表达式部分:var opArr =new Array("+", "-", "*","/"); //运算符数组
/*
参数说明:
1、numArr:输入的四个数字
2、numLen:输入的数字个数
3、targetNum:输入四个数字后要计算出的结果(24,etc)
*/
function computeNum(numArr, numLen, targetNum) {
for (var i = 0; i < numLen; i++) {
for (var j = i + 1; j < numLen; j++) {
var numij = [numArr[i], numArr[j]];
numArr[j] = numArr[numLen - 1];
for (var k = 0; k < opArr.length; k++) {
numArr[i] = '(' + numij[k % 2] + opArr[k] + numij[(!(k % 2) * 1)] + ')'; //构建算术表达式,然后利用自定义函数根据逆波兰表达式计算结果
if (computeNum(numArr, numLen - 1, targetNum)) {
return true;
}
}
numArr[i] = numij[0];
numArr[j] = numij[1];
}
}
var tmpStr=numArr[0];
//不通过js的eval函数直接算出预期结果targetNum,我们利用逆波兰表达式计算结果
var result=Math.ceil( funcGetResult(numArr[0]));
return (numLen == 1) && (result == targetNum);
}
//获取最终的计算表达式
function getExpression(numArr,targetNum) {
if (computeNum(numArr, numArr.length, targetNum)) {
var str = numArr[0].substring(1, numArr[0].length - 1);
return numArr[0].substring(1, numArr[0].length - 1)+"="+String(targetNum);
} else {
return "没有匹配的表达式使之等于" + targetNum;
}
}
//单击按钮,显示结果
function showExpression() {
var txtNumberArr=document.getElementsByName("txtNumber");
var txtTargetNum=document.getElementById("txtTargetNumber").value;
var txtResult=document.getElementById("txtExpression");
var numsArr=new Array();
for(var i=0;i<txtNumberArr.length;i++){
numsArr.push(txtNumberArr[i].value);
}
txtResult.value=getExpression(numsArr,txtTargetNum);
}
Code
// 构造类似StringBuilder的函数
function StringBuilder(str)
{
this.tempArr = new Array();
}
StringBuilder.prototype.Append = function(value)
{
this.tempArr.push(value);
return this;
}
StringBuilder.prototype.Clear = function()
{
this.tempArr.length = 0;
}
StringBuilder.prototype.toString = function()
{
return this.tempArr.join('');
}
String.prototype.Trim = function()
{
return this.replace(/(^\s+)|(\s+$)/g, "");
}
String.prototype.IsDigit = function()
{
var str = this.Trim();
return (str.replace(/\d/g, "").length == 0);
}
function funcDoOperation(prevNum, nextNum, opration)
{
var result = 0;
switch (opration)
{
case '+':
result = prevNum + nextNum;
break;
case '-':
result = prevNum - nextNum;
break;
case '*':
result = prevNum * nextNum;
break;
case '/':
try
{
result =parseFloat( prevNum / nextNum);
}
catch(ex)
{
}
break;
default:
result = 0;
break;
}
return result;
}
function funcGetOperationPriority(opration)
{
var opResult=0;
switch (opration)
{
case '+':
opResult=1;
case '-':
opResult=1;
case '*':
opResult=2;
case '/':
opResult=2;
}
return opResult;
}
function funcCreateRpnExpression( exp)
{
var skOp = new Array();
var operations = "+-*/";
var sbExpression = new StringBuilder();
var digit = "";
for (var i = 0; i < exp.length; i++)
{
var chr = exp.charAt(i);
if (chr.IsDigit())//如果是数字,添加到数字字符串中
{
digit += chr;
}
else if (operations.indexOf(chr) >= 0)//如果是运算符
{
if (digit.length > 0)
{
sbExpression.Append("<" + digit + ">");
digit = "";
}
while (skOp.length > 0)
{
var opInStack = skOp.pop();
if (opInStack == '(' || funcGetOperationPriority(opInStack) < GetOperationPriority(chr))
{
skOp.push(opInStack);
break;
}
else
{
sbExpression.Append(opInStack);
}
}
skOp.push(chr);
}
else if (chr == '(')//遇到左括号,直接压入堆栈中
{
skOp.push(chr);
}
else if (chr == ')')//遇到右括号
{
if (digit.length > 0)//先将数字添加到逆波兰表达式
{
sbExpression.Append("<" + digit + ">");
digit = "";
}
while (skOp.length > 0)//弹出运算符并添加到逆波兰表达式,直至遇到左括号
{
var opInStack = skOp.pop();
if (opInStack == '(')
{
break;
}
else
{
sbExpression.Append(opInStack);
}
}
}
}
if (digit.length > 0)
{
sbExpression.Append("<" + digit + ">");
}
while (skOp.Count > 0)
{
var opInStack =skOp.pop();
sbExpression.Append(opInStack);
}
return sbExpression.toString();
}
function funcGetResultByRpnExp(rpnExp)
{
var stack = new Array();
var digit ="";
var result = 0;
for (var i = 0; i < rpnExp.length; i++)
{
var c = rpnExp.charAt(i);
if (c == '<')
{
digit ="";
}
else if (c == '>')
{
stack.push(digit);
}
else if (c.IsDigit())
{
digit += c.toString();
}
else if (c == '+' || c == '-' || c == '*' || c == '/')
{
var nextNum = parseFloat(stack.pop());
var prevNum = parseFloat(stack.pop());
result = funcDoOperation(prevNum, nextNum, c);
stack.push(result);
}
}
return result;
}
function funcGetResult(exp)
{
var rpn = funcCreateRpnExpression(exp);
return funcGetResultByRpnExp(rpn);
}
html文档和上一篇一样,不贴了。// 构造类似StringBuilder的函数
function StringBuilder(str)
{
this.tempArr = new Array();
}
StringBuilder.prototype.Append = function(value)
{
this.tempArr.push(value);
return this;
}
StringBuilder.prototype.Clear = function()
{
this.tempArr.length = 0;
}
StringBuilder.prototype.toString = function()
{
return this.tempArr.join('');
}
String.prototype.Trim = function()
{
return this.replace(/(^\s+)|(\s+$)/g, "");
}
String.prototype.IsDigit = function()
{
var str = this.Trim();
return (str.replace(/\d/g, "").length == 0);
}
function funcDoOperation(prevNum, nextNum, opration)
{
var result = 0;
switch (opration)
{
case '+':
result = prevNum + nextNum;
break;
case '-':
result = prevNum - nextNum;
break;
case '*':
result = prevNum * nextNum;
break;
case '/':
try
{
result =parseFloat( prevNum / nextNum);
}
catch(ex)
{
}
break;
default:
result = 0;
break;
}
return result;
}
function funcGetOperationPriority(opration)
{
var opResult=0;
switch (opration)
{
case '+':
opResult=1;
case '-':
opResult=1;
case '*':
opResult=2;
case '/':
opResult=2;
}
return opResult;
}
function funcCreateRpnExpression( exp)
{
var skOp = new Array();
var operations = "+-*/";
var sbExpression = new StringBuilder();
var digit = "";
for (var i = 0; i < exp.length; i++)
{
var chr = exp.charAt(i);
if (chr.IsDigit())//如果是数字,添加到数字字符串中
{
digit += chr;
}
else if (operations.indexOf(chr) >= 0)//如果是运算符
{
if (digit.length > 0)
{
sbExpression.Append("<" + digit + ">");
digit = "";
}
while (skOp.length > 0)
{
var opInStack = skOp.pop();
if (opInStack == '(' || funcGetOperationPriority(opInStack) < GetOperationPriority(chr))
{
skOp.push(opInStack);
break;
}
else
{
sbExpression.Append(opInStack);
}
}
skOp.push(chr);
}
else if (chr == '(')//遇到左括号,直接压入堆栈中
{
skOp.push(chr);
}
else if (chr == ')')//遇到右括号
{
if (digit.length > 0)//先将数字添加到逆波兰表达式
{
sbExpression.Append("<" + digit + ">");
digit = "";
}
while (skOp.length > 0)//弹出运算符并添加到逆波兰表达式,直至遇到左括号
{
var opInStack = skOp.pop();
if (opInStack == '(')
{
break;
}
else
{
sbExpression.Append(opInStack);
}
}
}
}
if (digit.length > 0)
{
sbExpression.Append("<" + digit + ">");
}
while (skOp.Count > 0)
{
var opInStack =skOp.pop();
sbExpression.Append(opInStack);
}
return sbExpression.toString();
}
function funcGetResultByRpnExp(rpnExp)
{
var stack = new Array();
var digit ="";
var result = 0;
for (var i = 0; i < rpnExp.length; i++)
{
var c = rpnExp.charAt(i);
if (c == '<')
{
digit ="";
}
else if (c == '>')
{
stack.push(digit);
}
else if (c.IsDigit())
{
digit += c.toString();
}
else if (c == '+' || c == '-' || c == '*' || c == '/')
{
var nextNum = parseFloat(stack.pop());
var prevNum = parseFloat(stack.pop());
result = funcDoOperation(prevNum, nextNum, c);
stack.push(result);
}
}
return result;
}
function funcGetResult(exp)
{
var rpn = funcCreateRpnExpression(exp);
return funcGetResultByRpnExp(rpn);
}
分别运行上面的代码,我们发现输入相同顺序的数字,c#和js产生的表达式可能不同,这是因为输入相同的数字,可以有不同的加减乘除组合方式使产生的结果相同。有人发邮件问如何分辨出产生的结果有无重复,如何一次取出所有的加减乘除组合方式使之等于预期的值(比如24)。我想说这两个问题之前我真的没想过(平时玩游戏,先算出来就算赢,一种表达式足矣),解决起来不是很难,我用c#实现了一种无重复表达式的实现,只要改进一个方法就可以了:
Code
using System;
using System.Windows.Forms;
using System.Collections;
using System.Collections.Generic;
namespace MyCsStudy
{
/// <summary>
/// 24点算法
/// </summary>
class TwentyFour
{
public static List<string> listOps = new List<string>(); //存加减乘除运算符
public static List<string> listExpression = new List<string>(); //存所有运算结果等于预期目标数字的加减乘除表达式
static TwentyFour()
{
listOps.Add("+");
listOps.Add("-");
listOps.Add("*");
listOps.Add("/");
}
public static bool Computing(string[] numArr, int numLen, int targetNum)
{
bool flag = false;
for (int i = 0; i < numLen; i++)
{
for (int j = i + 1; j < numLen; j++)
{
string[] strij = new string[] { numArr[i], numArr[j] };
numArr[j] = numArr[numLen - 1];
for (int k = 0; k < listOps.Count; k++)
{
numArr[i] = "(" + strij[0] + listOps[k] + strij[1] + ")";
if (Computing(numArr, numLen - 1, targetNum) && !listExpression.Contains(numArr[0])) //这里过滤重复表达式
{
flag = true;
return flag;
}
}
numArr[i] = strij[0];
numArr[j] = strij[1];
}
}
string expression = numArr[0];
/*
* 这里是改进的部分:
* 不在利用datatable的compute方法计算字符串表达式
* **************利用构造逆波兰表达式计算结果******************
*/
int result = (int)Math.Ceiling(RpnExpression.GetResult(expression)); //double类型 转换为整数
//如果运算结果是预期的数字,而且数组中的数字全部在运算范畴内
if (result == targetNum && numLen == 1 && !listExpression.Contains(expression)) //这里过滤重复表达式
{
flag = true;
listExpression.Add(expression);//将表达式加入list
}
return flag;
}
public static void GetExpression(string[] numArr, int targetNum)
{
Computing(numArr, numArr.Length, targetNum);
if (listExpression.Count > 0)
{
foreach (string item in listExpression)
{
string result = item.Substring(1, item.Length - 2) + "=" + targetNum.ToString();
Console.WriteLine(result);
}
}
else
{
Console.WriteLine("没有表达式使之等于" + targetNum.ToString());
}
}
}
class Program
{
static void Main(string[] args)
{
string[] numArr = { "2", "4", "8", "16" }; //正常都是4个数字,本程序不限制个数
TwentyFour.GetExpression(numArr, 24); // 预期的目标计算结果可以不是24
Console.ReadLine();
}
}
}
另外一个类文件逆波兰表达式保持不变,大家可以运行看看结果。using System;
using System.Windows.Forms;
using System.Collections;
using System.Collections.Generic;
namespace MyCsStudy
{
/// <summary>
/// 24点算法
/// </summary>
class TwentyFour
{
public static List<string> listOps = new List<string>(); //存加减乘除运算符
public static List<string> listExpression = new List<string>(); //存所有运算结果等于预期目标数字的加减乘除表达式
static TwentyFour()
{
listOps.Add("+");
listOps.Add("-");
listOps.Add("*");
listOps.Add("/");
}
public static bool Computing(string[] numArr, int numLen, int targetNum)
{
bool flag = false;
for (int i = 0; i < numLen; i++)
{
for (int j = i + 1; j < numLen; j++)
{
string[] strij = new string[] { numArr[i], numArr[j] };
numArr[j] = numArr[numLen - 1];
for (int k = 0; k < listOps.Count; k++)
{
numArr[i] = "(" + strij[0] + listOps[k] + strij[1] + ")";
if (Computing(numArr, numLen - 1, targetNum) && !listExpression.Contains(numArr[0])) //这里过滤重复表达式
{
flag = true;
return flag;
}
}
numArr[i] = strij[0];
numArr[j] = strij[1];
}
}
string expression = numArr[0];
/*
* 这里是改进的部分:
* 不在利用datatable的compute方法计算字符串表达式
* **************利用构造逆波兰表达式计算结果******************
*/
int result = (int)Math.Ceiling(RpnExpression.GetResult(expression)); //double类型 转换为整数
//如果运算结果是预期的数字,而且数组中的数字全部在运算范畴内
if (result == targetNum && numLen == 1 && !listExpression.Contains(expression)) //这里过滤重复表达式
{
flag = true;
listExpression.Add(expression);//将表达式加入list
}
return flag;
}
public static void GetExpression(string[] numArr, int targetNum)
{
Computing(numArr, numArr.Length, targetNum);
if (listExpression.Count > 0)
{
foreach (string item in listExpression)
{
string result = item.Substring(1, item.Length - 2) + "=" + targetNum.ToString();
Console.WriteLine(result);
}
}
else
{
Console.WriteLine("没有表达式使之等于" + targetNum.ToString());
}
}
}
class Program
{
static void Main(string[] args)
{
string[] numArr = { "2", "4", "8", "16" }; //正常都是4个数字,本程序不限制个数
TwentyFour.GetExpression(numArr, 24); // 预期的目标计算结果可以不是24
Console.ReadLine();
}
}
}
最后,我喜欢这样的拍砖,欢迎大家继续。
作者:Jeff Wong
出处:http://jeffwongishandsome.cnblogs.com/
本文版权归作者和博客园共有,欢迎围观转载。转载时请您务必在文章明显位置给出原文链接,谢谢您的合作。