運用大眾例子:
所謂的逆波蘭表示法(Reverse Polish notation,RPN,或逆波蘭記法),是一種數學表達式方式,在逆波蘭記法中,所有操作符置於操作數的后面,因此也被稱為后綴表示法。逆波蘭記法不需要括號來標識操作符的優先級。(摘自維基)
我們平時長寫的數學公式被稱為中綴表達式,即:(a+b)*c,用逆波蘭式為ab+c*,再如:(a+b)*c-(a+b)/e,逆波蘭式為ab+c*ab+e/-,首先理解下轉化成逆波蘭式的幾個步驟:
算法實現(選自百度):
首先需要分配2個棧,一個作為臨時存儲運算符的棧S1(含一個結束符號),一個作為輸入逆波蘭式的棧S2(空棧),S1棧可先放入優先級最低的運算符#,注意,中綴式應以此最低優先級的運算符結束。可指定其他字符,不一定非#不可。從中綴式的左端開始取字符,逐序進行如下步驟:
static void Main(string[] args) { while (true) { Console.WriteLine("輸入計算公式:"); //輸入公式 string formula = Console.ReadLine(); //定義臨時計算符棧 Stack<string> opStack = new Stack<string>(); opStack.Push("#"); Stack<string> numStack = new Stack<string>(); for (int i = 0; i < formula.Length;) { int opNum = GetOperationLevel(formula[i].ToString()); if (opNum == 0) { int index = GetCompleteValue(formula.Substring(i, formula.Length - i)); numStack.Push(formula.Substring(i, index)); i = (i + index); }//為操作數,獲取完整 else { if (formula[i] == '(') { opStack.Push(formula[i].ToString()); } else if (formula[i] == ')') { MoveOperator(opStack, numStack); } else { if (opStack.Peek() == "(") { opStack.Push(formula[i].ToString()); } else { JudgeOperator(opStack, numStack, formula[i].ToString()); } } i++; } } if (opStack.Count != 0) { while (opStack.Count != 0 && opStack.Peek() != "#") { numStack.Push(opStack.Pop()); } } StringBuilder strBuild = new StringBuilder(); foreach (string s in numStack) { strBuild.Insert(0,s); } Console.WriteLine(strBuild.ToString()); } }
獲取運算符等級:
/// <summary> /// 獲取運算符等級 /// </summary> /// <param name="c">當前字符</param> /// <returns></returns> private static int GetOperationLevel(string c) { switch (c) { case "+": return 1; case "-": return 1; case "*": return 2; case "/": return 2; case "#": return -1; case "(": return -1; case ")": return -1; default: return 0; } }
獲取完整操作數
/// <summary> /// 獲取完整數值 /// </summary> /// <param name="formula">公式</param> /// <returns></returns> private static int GetCompleteValue(string formula) { int index = formula.Length; for (int i = 0; i < formula.Length; i++) { int num = GetOperationLevel(formula[i].ToString()); if (num != 0) { index = i; break; } } return index; }
移動運算符:
/// <summary> /// 移動運算符 /// </summary> /// <param name="opStack"></param> /// <param name="numStack"></param> private static void MoveOperator(Stack<string> opStack, Stack<string> numStack) { string s = opStack.Pop(); if (s == "(") { return; } else { numStack.Push(s); MoveOperator(opStack, numStack); return; } }
判斷運算符:
/// <summary> /// 判斷運算符 /// </summary> /// <param name="opStack"></param> /// <param name="numStack"></param> /// <param name="x"></param> private static void JudgeOperator(Stack<string> opStack, Stack<string> numStack, string x) { int xNum = GetOperationLevel(x); int opNum = GetOperationLevel(opStack.Peek()); if (xNum > opNum || numStack.Peek() == "(") { opStack.Push(x); return; } else { string opStr = opStack.Pop(); numStack.Push(opStr); JudgeOperator(opStack, numStack, x); return; } }
代碼運行效果:
接下來就是計算公式的值,思路是新建一個棧R用來放結果,依次取出表達式的棧F,
1.如果取出的值為操作數,則放入棧R,
2.如果取出的值為運算符,則取出棧R頂的兩個,進行運算,運算結果在存放入棧R
最后R頂的元素,即為計算的值
下面為代碼,目前只做了+,-,*,/
//numStack為逆波蘭表達式 Stack<string> rpnFormula = new Stack<string>(); foreach (string s in numStack) { rpnFormula.Push(s); } Console.WriteLine(CalcRPNFormula(rpnFormula));=
private static string CalcRPNFormula(Stack<string> rpnFormula) { Stack<string> resultStack = new Stack<string>(); foreach (string s in rpnFormula) { int num = GetOperationLevel(s); if (num == 0) { resultStack.Push(s); } else { CalcResult(resultStack,s); } } return resultStack.Pop(); } private static void CalcResult(Stack<string> resultStack, string operatorStr) { if (resultStack.Count >= 2) { double num2 = Convert.ToDouble(resultStack.Pop()); double num1 = Convert.ToDouble(resultStack.Pop()); if (operatorStr == "+") { resultStack.Push(Convert.ToString(num1 + num2)); } else if (operatorStr == "-") { resultStack.Push(Convert.ToString(num1 - num2)); } else if (operatorStr == "*") { resultStack.Push(Convert.ToString(num1 * num2)); } else if (operatorStr == "/") { resultStack.Push(Convert.ToString(num1 / num2)); } } }
因為適應自己的項目,需要做到支持“”^”,"arctan", "sqrt", "sin", "cos", "tan", "arcsin", "arccos"的計算方式,在查看上面生成的逆波蘭式的時候,發現^會后置,其他的都是前置,然后就投機取巧了以下,遇到^取前兩位,遇到其他特殊符號,則取后一位,所以增加了以下內容,測試了下例子:2^3+sqrt(9)+sin(45)+1 通過之前的代碼轉換為后綴表達式為23^sqrt9+sin45+1+(正確應為23^9sqrt+45sin+1+),然后根據上述的進行計算,加了這幾個特許計算后,可能與原來的后綴表達式有點背道而馳,沒有對特殊字符進行后置處理,但目前為了先實現計算,所以投機取巧了下,后期會進行修改
/// <summary> /// 特殊字符 /// </summary> private static List<string> m_SpecialOp = new List<string>() { "arctan", "sqrt", "sin", "cos", "tan", "arcsin", "arccos" }; /// <summary> /// 獲取運算符等級 /// </summary> /// <param name="c">當前字符</param> /// <returns></returns> private static int GetOperationLevel(string c) {//sin(cos(tan(arcsin(arccos(arctan( switch (c) { case "+": return 1; case "-": return 1; case "*": return 2; case "/": return 2; case "^": return 2; case "arctan": return 10; case "sqrt": return 10; case "sin": return 10; case "cos": return 10; case "tan": return 10; case "arcsin": return 10; case "arccos": return 10; case "#": return -1; case "(": return -1; case ")": return -1; default: return 0; } } /// <summary> /// 計算逆波蘭式 /// </summary> /// <param name="rpnFormula"></param> /// <returns></returns> private static string CalcRPNFormula(Stack<string> rpnFormula) { Stack<string> resultStack = new Stack<string>(); while (rpnFormula.Count > 0) { string rpnStr = rpnFormula.Pop(); int num = GetOperationLevel(rpnStr); if (num == 0) { resultStack.Push(rpnStr); } else if (num == 10) { SpecialCalc(resultStack, rpnStr, rpnFormula.Pop()); } else { CalcResult(resultStack, rpnStr); } } return resultStack.Pop(); } /// <summary> /// 特殊計算 /// </summary> /// <param name="resultStack"></param> /// <param name="operatorStr"></param> /// <param name="calcValue"></param> private static void SpecialCalc(Stack<string> resultStack, string operatorStr, string calcValue) { if (m_SpecialOp.Contains(operatorStr)) { double num = Convert.ToDouble(calcValue); if (operatorStr == "sqrt") { resultStack.Push(Math.Sqrt(num).ToString("#0.000")); } else if (operatorStr == "sin") { resultStack.Push(Math.Sin(num).ToString("#0.000")); } else if (operatorStr == "cos") { resultStack.Push(Math.Cos(num).ToString("#0.000")); } else if (operatorStr == "tan") { resultStack.Push(Math.Tan(num).ToString("#0.000")); } else if (operatorStr == "arcsin") { resultStack.Push(Math.Asin(num).ToString("#0.000")); } else if (operatorStr == "arccos") { resultStack.Push(Math.Acos(num).ToString("#0.000")); } else if (operatorStr == "arctan") { resultStack.Push(Math.Atan(num).ToString("#0.000")); } } } /// <summary> /// 計算結果 /// </summary> /// <param name="resultStack"></param> /// <param name="operatorStr"></param> private static void CalcResult(Stack<string> resultStack, string operatorStr) { if (resultStack.Count >= 2) { double num2 = Convert.ToDouble(resultStack.Pop()); double num1 = Convert.ToDouble(resultStack.Pop()); if (operatorStr == "+") { resultStack.Push(Convert.ToString(num1 + num2)); } else if (operatorStr == "-") { resultStack.Push(Convert.ToString(num1 - num2)); } else if (operatorStr == "*") { resultStack.Push(Convert.ToString(num1 * num2)); } else if (operatorStr == "/") { resultStack.Push(Convert.ToString(num1 / num2)); } else if (operatorStr == "^") { resultStack.Push(Math.Pow(num1, num2).ToString()); } } }