完整的Java運算式演算法
---擴充容易
本文的運算式求值採用標準的演算法。首先從最簡單的運算式求值開始,到後面的進階運算式求值。大郅演算法如下,首先將運算式轉換為後序運算式,然後對後序運算式求值。表示式求值的關鍵步驟式運算式語義的解析和分割,而對於運算式的求值反而簡單。
在實際應用中,經常會有如下的情境:
1、對一行資料進行運算,例如:總價=單價*數量。
2、對集合資料進行運算,例如:平均銷售價格=sum(單價*數量)/sum(數量)。
3、對集合資料進行運算,例如:全校平均成績=(sum(數學平均分)*sum(數學考試人數)+sum(語文平均分)*sum(語文考試人數))/(sum(數學考試人數+語文考試人數)。
本文中的演算法完全可以解決此類問題。
一、將公式變換為後序運算式
1)檢查輸入的下一元素。
2)假如是個運算元,輸出。
3)假如是個開括弧,將其壓棧。
4)假如是個運算子,則
i) 假如棧為空白,將此運算子壓棧。
ii) 假如棧頂是開括弧,將此運算子壓棧。
iii) 假如此運算子比棧頂運算子優先順序高,將此運算子壓入棧中。
iv) 否則棧頂運算子出棧並輸出,重複步驟4。
5)假如是個閉括弧,棧中運算子逐個出棧並輸出,直到遇到開括弧。開括弧出棧並丟棄。
6)假如輸入還未完畢,跳轉到步驟1。
7)假如輸入完畢,棧中剩餘的所有操作符出棧並輸出它們。
演算法如下:
<br /> public static Stack<String> convertToPostfix (StringSplit x) {<br /> String s = x.getNext () ;<br /> Stack<String> st = new Stack<String> () ;<br /> Stack<String> rSt = new Stack<String> () ;<br /> while (s != null) {<br /> if (isBlank (s)) {<br /> //break;<br /> } else if (isOpenParenthesis (s)) {<br /> st.push (s) ;<br /> } else if (isCloseParenthesis (s)) {<br /> String as = null ;<br /> if (st.isEmpty ()) {<br /> System.out.println ("錯誤:缺少(") ;<br /> } else {<br /> as = st.pop () ;<br /> while (!isOpenParenthesis (as)) {<br /> rSt.push (as) ;<br /> if (st.isEmpty ()) {<br /> System.out.println ("錯誤:缺少(") ;<br /> break ;<br /> } else {<br /> as = st.pop () ;<br /> }<br /> }<br /> }<br /> } else if (isOperator (s)) {<br /> if (!st.isEmpty ()) {<br /> String as = st.pop () ;<br /> while (priority (as) >= priority (s)) {<br /> rSt.push (as) ;<br /> if (st.isEmpty ()) {<br /> as = null ;<br /> } else {<br /> as = st.pop () ;<br /> }<br /> }<br /> if (as != null) {<br /> st.push (as) ;<br /> }<br /> st.push (s) ;<br /> } else {<br /> st.push (s) ;<br /> }<br /> } else {<br /> rSt.push (s) ;<br /> }<br /> s = x.getNext () ;<br /> }</p><p> while (!st.isEmpty ()) {<br /> String as = st.pop () ;<br /> if (isOpenParenthesis (as)) {<br /> System.out.println ("錯誤:缺少)") ;<br /> } else if (isCloseParenthesis (as)) {<br /> System.out.println ("錯誤:缺少(") ;<br /> } else {<br /> rSt.push (as) ;<br /> }<br /> }<br /> Stack<String> nSt = new Stack<String> () ;<br /> while (!rSt.empty ()) {<br /> nSt.push (rSt.pop ()) ;<br /> }<br /> return nSt ;<br /> }<br />
類StringSplit是介面,目的是將表示式進行拆分,拆分為運算元、括弧、運算子。本文中實現了單一字元解析、多字元解析以及函數解析等功能。以滿足不同的應用情境。
<br />package net.csdn.blog.z3h.util ;</p><p>public interface StringSplit {<br /> String getNext () ;<br />}<br />
二、對後序運算式進行求值計算
1)初始化一個空堆棧
2)從左至右讀入尾碼運算式
i)如果字元是一個運算元,把它壓入堆棧。
ii)如果字元是個操作符,彈出兩個運算元,執行恰當操作,然後把結果壓入堆棧。如果您不能夠彈出兩個運算元,尾碼運算式的文法就不正確。
3)到尾碼運算式末尾,從堆棧中彈出結果。若尾碼運算式格式正確,那麼堆棧應該為空白。
<br /> public static double numberCalculate (Stack<String> st) {<br /> Stack<String> tSt = new Stack<String> () ;<br /> while (!st.empty ()) {<br /> String a = st.pop () ;<br /> //System.out.println (a) ;<br /> if (isOperator (a)) {<br /> double d2 = Double.parseDouble (tSt.pop ()) ;<br /> double d1 = Double.parseDouble (tSt.pop ()) ;<br /> double d3 = 0 ;<br /> if ("+".equals (a)) {<br /> d3 = d1 + d2 ;<br /> } else if ("-".equals (a)) {<br /> d3 = d1 - d2 ;<br /> } else if ("*".equals (a)) {<br /> d3 = d1 * d2 ;<br /> } else if ("/".equals (a)) {<br /> d3 = d1 / d2 ;<br /> }<br /> tSt.push (String.valueOf (d3)) ;<br /> } else {<br /> tSt.push (a) ;<br /> }<br /> }<br /> return Double.parseDouble (tSt.pop ()) ;<br /> }<br />
三、完整的求值
單字元運算式分割對象。
<br />package net.csdn.blog.z3h.util ;</p><p>public class CharSplit<br /> implements StringSplit {<br /> private String s ;</p><p> private int pos = 0 ;</p><p> public CharSplit (String s) {<br /> this.s = s ;<br /> }</p><p> public String getNext () {<br /> if (pos < s.length ()) {<br /> return s.charAt (pos++) + "" ;<br /> } else {<br /> return null ;<br /> }<br /> }<br />}<br />
運算式求值樣本:
<br /> //單字元求值。<br /> //運算式分割對象<br /> StringSplit split = new CharSplit("2*3/(2-1)+5*(4-1)");<br /> //將運算式轉化位後序運算式<br /> Stack<String> postFixStack = Expression.convertToPostfix(split);<br /> //對後序運算式進行運算<br /> double result = Expression.numberCalculate(postFixStack);<br /> System.out.println (result) ;<br />
四、進階運算式應用
在實際應用中,經常會有如下的情境:
1、 對一行資料進行運算,例如總價=單價*數量。
2、 對集合資料進行運算,例如:平均銷售價格=sum(單價*數量)/sum(數量)。
<br /> StringSplit split = new FunctionSplit (<br /> "(a12+b23+c34)/($9+c34-12-b23)+9") ; //$9 表示"9",9表示9<br /> Stack<String> postFixStack = Expression.convertToPostfix (split);<br /> double result =Expression.rowCalculate (rowData ,postFixStack);<br /> System.out.println (result) ;<br />
對集合資料進行運算樣本:
<br /> List<Map<String,Double>> dataTable = new ArrayList<Map<String,Double>> () ;<br /> ......</p><p> StringSplit split = new FunctionSplit (<br /> "(sum('a'+'b'+'c'))/rowcount()-sum('a')") ;<br /> Stack<String> postFixStack = Expression.convertToPostfix (split);<br /> double result = Expression.tableCalculate (dataTable,postFixStack);<br /> System.out.println(result);<br />
對一行資料求值的演算法
<br /> /**<br /> * 對Map<String,Double>求值.<br /> * 例如:相應的運算式為<br /> * “(a+b)/2”表示列a和列b的和的平均<br /> * “(a+b)/$4”表示列a與列b求和,然後除 列4<br /> * @param map DataObject<br /> * @param st Stack<br /> * @return double<br /> */<br /> public static double rowCalculate (Map<String,Double> map , Stack<String> sourceStack) {<br /> Stack<String> calStack = new Stack<String> () ;<br /> @SuppressWarnings("unchecked")<br /> Stack<String> cloneStack = (Stack<String>)sourceStack.clone();; //保證之前的棧不變.<br /> while (!cloneStack.empty ()) {<br /> String a = cloneStack.pop () ;<br /> //System.out.println (a) ;<br /> if (isOperator (a)) {<br /> double d2 = doValues (map , calStack.pop ()) ;<br /> double d1 = doValues (map , calStack.pop ()) ;<br /> double d3 = 0 ;<br /> if ("+".equals (a)) {<br /> d3 = d1 + d2 ;<br /> } else if ("-".equals (a)) {<br /> d3 = d1 - d2 ;<br /> } else if ("*".equals (a)) {<br /> d3 = d1 * d2 ;<br /> } else if ("/".equals (a)) {<br /> d3 = d1 / d2 ;<br /> }<br /> calStack.push (String.valueOf (d3)) ;<br /> } else {<br /> calStack.push (a) ;<br /> }<br /> }<br /> return doValues (map , calStack.pop ()) ;<br /> }</p><p> private static double doValues ( Map<String,Double> map , String columnName ){<br /> //System.out.println(columnName);<br /> columnName = columnName.replaceAll ("'" , "") ;<br /> columnName = columnName.replaceAll ("/"" , "") ;<br /> boolean isColumnName = false;<br /> if ( columnName.startsWith("$")){<br /> isColumnName = true;<br /> columnName = columnName.substring(1);<br /> }<br /> if ( isColumnName ){<br /> try{<br /> return map.get (columnName) ;<br /> }catch ( Exception sysEx ){<br /> return 0;<br /> }<br /> }else{<br /> try {<br /> return Double.parseDouble (columnName) ;<br /> } catch (NumberFormatException e) {<br /> try {<br /> return map.get (columnName) ;<br /> } catch (Exception sysEx) {<br /> return 0 ;<br /> }<br /> }<br /> }<br /> }<br />
對二維資料進行求值的演算法
<br /> public static double tableCalculate (List<Map<String,Double>> ds , Stack<String> sourceStack) {<br /> Stack<String> callStack = new Stack<String> () ;<br /> @SuppressWarnings("unchecked")<br /> Stack<String> cloneStack = (Stack<String>)sourceStack.clone(); //保證之前的棧不變.<br /> while (!cloneStack.empty ()) {<br /> String a = cloneStack.pop () ;<br /> //System.out.println (a) ;<br /> if (isOperator (a)) {<br /> double d2 = tableFunValue (ds , callStack.pop ()) ;<br /> double d1 = tableFunValue (ds , callStack.pop ()) ;<br /> double d3 = 0 ;<br /> if ("+".equals (a)) {<br /> d3 = d1 + d2 ;<br /> } else if ("-".equals (a)) {<br /> d3 = d1 - d2 ;<br /> } else if ("*".equals (a)) {<br /> d3 = d1 * d2 ;<br /> } else if ("/".equals (a)) {<br /> d3 = d1 / d2 ;<br /> }<br /> callStack.push (String.valueOf (d3)) ;<br /> } else {<br /> callStack.push (a) ;<br /> }<br /> }<br /> return tableFunValue (ds , callStack.pop ()) ;<br /> }</p><p> private static double tableFunValue (List<Map<String,Double>> ds , String fun) {<br /> try {<br /> fun = fun.trim () ;<br /> fun = fun.replaceAll (" " , "") ;<br /> fun = fun.replaceAll ("/t" , "") ;<br /> fun = fun.toLowerCase () ;<br /> String cn ;<br /> if (fun.startsWith ("sum")) {<br /> cn = fun ;<br /> cn = cn.substring (3) ;<br /> Stack<String> st = Expression.convertToPostfix (new FunctionSplit (cn)) ;<br /> double sum = 0 ;<br /> for (int i = 0 ; i < ds.size() ; i++) {<br /> sum += Expression.rowCalculate (ds.get (i) , st) ;<br /> }<br /> return sum ;<br /> } else if (fun.startsWith ("avg")) {<br /> cn = fun ;<br /> cn = cn.substring (3) ;<br /> Stack<String> st = Expression.convertToPostfix (new FunctionSplit (cn)) ;<br /> double sum = 0 ;<br /> for (int i = 0 ; i < ds.size () ; i++) {<br /> sum += Expression.rowCalculate (ds.get (i) , st) ;<br /> }<br /> return sum / ds.size () ;<br /> } else if (fun.startsWith ("min")) {<br /> cn = fun ;<br /> cn = cn.substring (3) ;<br /> Stack<String> st = Expression.convertToPostfix (new FunctionSplit (cn)) ;<br /> double min = Double.MAX_VALUE ;<br /> for (int i = 0 ; i < ds.size () ; i++) {<br /> double tmin = Expression.rowCalculate (ds.get (i) , st) ;<br /> if (tmin < min) {<br /> min = tmin ;<br /> }<br /> }<br /> return min;<br /> } else if (fun.startsWith ("max")) {<br /> cn = fun ;<br /> cn = cn.substring (3) ;<br /> Stack<String> st = Expression.convertToPostfix (new FunctionSplit (cn)) ;<br /> double max = Double.MIN_VALUE ;<br /> for (int i = 0 ; i < ds.size () ; i++) {<br /> double tmax = Expression.rowCalculate (ds.get (i) , st) ;<br /> if (tmax > max) {<br /> max = tmax ;<br /> }<br /> }<br /> return max ;<br /> } else if (fun.startsWith ("rowcount")) {<br /> return ds.size () ;<br /> } else {<br /> return Double.parseDouble (fun) ;<br /> }<br /> } catch (Exception ex) {<br /> ex.printStackTrace () ;<br /> return 0 ;<br /> }<br /> }<br />
五、其他輔助函數
<br /> /**<br /> * 字串是運算子號.<br /> * @param s String<br /> * @return boolean<br /> */<br /> public static boolean isOperator (String s) {<br /> if ("+".equals (s) || "-".equals (s) || "*".equals (s) || "/".equals (s)) {<br /> return true ;<br /> } else {<br /> return false ;<br /> }<br /> }</p><p> /**<br /> * 字元是運算子號.<br /> */<br /> public static boolean isOperator (char s) {<br /> if ('+' == s || '-' == s || '*' == s || '/' == s) {<br /> return true ;<br /> } else {<br /> return false ;<br /> }<br /> }</p><p> public static boolean isOpenParenthesis (String s) {<br /> return "(".equals (s) ;<br /> }</p><p> public static boolean isOpenParenthesis (char s) {<br /> return '(' == s ;<br /> }</p><p> public static boolean isCloseParenthesis (String s) {<br /> return ")".equals (s) ;<br /> }</p><p> public static boolean isCloseParenthesis (char s) {<br /> return ')' == s ;<br /> }</p><p> public static boolean isBlank (String s) {<br /> if (" ".equals (s) || "/t".equals (s)) {<br /> return true ;<br /> } else {<br /> return false ;<br /> }<br /> }</p><p> public static boolean isBlank (char s) {<br /> if (' ' == s || '/t' == s) {<br /> return true ;<br /> } else {<br /> return false ;<br /> }<br /> }</p><p> public static boolean isDot (char s) {<br /> return s == '.' ;<br /> }</p><p> static int priority (String s) {<br /> if (s == null) {<br /> return -1 ;<br /> }<br /> char c = s.charAt (0) ;<br /> if (c == '^') {<br /> return 3 ; /*Exponential operator*/<br /> }<br /> if (c == '*' || c == '/' || c == '%') {<br /> return 2 ;<br /> } else if (c == '+' || c == '-') {<br /> return 1 ;<br /> } else {<br /> return 0 ;<br /> }<br /> }<br />
六、小結
實際上在實際應用中也會有如下的應用情境。
求每一行資料和平均資料平均值的偏差。次問題,可以在方法tableCalculate的基礎上在進行適當的擴充即可。
<br /> List<Map<String, Double>> dataTable = null;<br /> StringSplit split = new FunctionSplit(<br /> "(avg(price)-price)/avg(price)*100.0");<br /> Stack<String> postFixStack = Expression.convertToPostfix(split);<br /> for (int rowNumber=0;rowNumber<dataTable.size();rowNumber++) {<br /> Map<String,Double> row = dataTable.get(rowNumber);<br /> System.out.println("本月銷售價格:"+row.get("price"));<br /> System.out.println("本月銷售價格偏差"<br /> + Expression.tableRowCalculate(dataTable, rowNumber,<br /> postFixStack));<br /> }
完整的代碼請到這裡 http://z3h.download.csdn.net/ 下載。