標籤:class java 使用 string art 演算法
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) 輸出時,為每一個運算式都加入了括弧。