C語言實現經典24點演算法
本文例項為大家分享了C語言經典24點演算法的具體實現程式碼,供大家參考,具體內容如下
1、概述
給定4個整數,其中每個數字只能使用一次;任意使用 + - * / ( ) ,構造出一個表示式,使得最終結果為24,這就是常見的算24點的遊戲。這方面的程式很多,一般都是窮舉求解。本文介紹一種典型的算24點的程式演算法,並給出兩個具體的算24點的程式:一個是面向過程的C實現,一個是面向物件的java實現。
2、基本原理
基本原理是窮舉4個整數所有可能的表示式,然後對錶達式求值。
表示式的定義: expression = (expression|number) operator (expression|number)
因為能使用的4種運算子 + - * / 都是2元運算子,所以本文中只考慮2元運算子。2元運算子接收兩個引數,輸出計算結果,輸出的結果參與後續的計算。
由上所述,構造所有可能的表示式的演算法如下:
(1) 將4個整數放入陣列中
(2) 在陣列中取兩個數字的排列,共有 P(4,2) 種排列。對每一個排列,
(2.1) 對 + - * / 每一個運算子,
(2.1.1) 根據此排列的兩個數字和運算子,計算結果
(2.1.2) 改表陣列:將此排列的兩個數字從陣列中去除掉,將 2.1.1 計算的結果放入陣列中
(2.1.3) 對新的陣列,重複步驟 2
(2.1.4) 恢復陣列:將此排列的兩個數字加入陣列中,將 2.1.1 計算的結果從陣列中去除掉
可見這是一個遞迴過程。步驟 2 就是遞迴函式。當陣列中只剩下一個數字的時候,這就是表示式的最終結果,此時遞迴結束。
在程式中,一定要注意遞迴的現場保護和恢復,也就是遞迴呼叫之前與之後,現場狀態應該保持一致。在上述演算法中,遞迴現場就是指陣列,2.1.2 改變陣列以進行下一層遞迴呼叫,2.1.3 則恢復陣列,以確保當前遞迴呼叫獲得下一個正確的排列。
括號 () 的作用只是改變運算子的優先順序,也就是運算子的計算順序。所以在以上演算法中,無需考慮括號。括號只是在輸出時需加以考慮。
3、面向過程的C實現
這是 csdn 演算法論壇前版主海星的程式碼,程式非常簡練、精緻:
#include #include #include using namespace std; const double PRECISION = 1E-6; const int COUNT_OF_NUMBER = 4; const int NUMBER_TO_BE_CAL = 24; double number[COUNT_OF_NUMBER]; string expression[COUNT_OF_NUMBER]; bool Search(int n) { if (n == 1) { if ( fabs(number[0] - NUMBER_TO_BE_CAL) < PRECISION ) { cout << expression[0] << endl; return true; } else { return false; } } for (int i = 0; i < n; i++) { for (int j = i + 1; j < n; j++) { double a, b; string expa, expb; a = number[i]; b = number[j]; number[j] = number[n - 1]; expa = expression[i]; expb = expression[j]; expression[j] = expression[n - 1]; expression[i] = '(' + expa + '+' + expb + ')'; number[i] = a + b; if ( Search(n - 1) ) return true; expression[i] = '(' + expa + '-' + expb + ')'; number[i] = a - b; if ( Search(n - 1) ) return true; expression[i] = '(' + expb + '-' + expa + ')'; number[i] = b - a; if ( Search(n - 1) ) return true; expression[i] = '(' + expa + '*' + expb + ')'; number[i] = a * b; if ( Search(n - 1) ) return true; if (b != 0) { expression[i] = '(' + expa + '/' + expb + ')'; number[i] = a / b; if ( Search(n - 1) ) return true; } if (a != 0) { expression[i] = '(' + expb + '/' + expa + ')'; number[i] = b / a; if ( Search(n - 1) ) return true; } number[i] = a; number[j] = b; expression[i] = expa; expression[j] = expb; } } return false; } void main() { for (int i = 0; i < COUNT_OF_NUMBER; i++) { char buffer[20]; int x; cin >> x; number[i] = x; itoa(x, buffer, 10); expression[i] = buffer; } if ( Search(COUNT_OF_NUMBER) ) { cout << "Success." << endl; } else { cout << "Fail." << endl; } }
使用任一個 c++ 編譯器編譯即可。
這個程式的演算法與 2、基本原理所述的演算法基本相同。其中 bool Search(int n) 就是遞迴函式,double number[] 就是陣列。程式中比較重要的地方解釋如下:
(1) string expression[] 存放每一步產生的表示式,最後的輸出中要用到。expression[] 與 number[] 類似,也是遞迴呼叫的現場,必須在下一層遞迴呼叫前改變、在下一層遞迴呼叫後恢復。
(2) number[] 陣列長度只有4。在 search() 中,每次取出兩個數後,使用區域性變數 a,b 儲存這兩個數,同時陣列中加入運算結果,並調整陣列使得有效的數字都排列在陣列前面。在下一層遞迴呼叫後,利用區域性變數 a,b 恢復整個陣列。對 expression[] 的處理與 number[] 類似。
(3) 因為 + * 滿足交換率而 - / 不滿足,所以程式中,從陣列生成兩個數的排列,
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
其內層迴圈 j 是從 i+1 -> n,而非從 0->n ,因為對於交換率來說,兩個數字的順序是無所謂的。當然,迴圈內部對 - / 做了特殊處理,計算了 a-b b-a a/b b/a 四種情況。
(4) 此程式只求出第一個解。當求出第一個解時,通過層層 return true 返回並輸出結果,然後程式結束。
(5) 以 double 來進行求解,定義精度,用以判斷是否為 24 。考慮 (5-1/5)*5 這個表示式就知道這麼做的原因了。
(6) 輸出時,為每個表示式都添加了括號。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。