德·梅齊里亞克的法碼問題The Weight Problem of Bachet de Meziriac
一位商人有一個40磅的砝碼,由於跌落在地而碎成4塊.後來,稱得每塊碎片的重量都是整磅數,而且可以用這4塊來稱從1至40磅之間的任意整數磅的重物.
問這4塊砝碼碎片各重多少?
這是一個著名的初等數學問題,解決這樣的問題我們第一想到的辦法就是窮舉法。
假設: 4個碎片的重量分別爲整數a,b,c,d,那麼從1到40中的任意整數n都必然能被a,b,c,d的加減組合所表示。我們直觀的想到將a,b,c,d的所有加減組合全部列出來:
一個操作數:a?n b?n c?n d?n
兩個操作數:a+b?n a+c?n a+d?n b+c?n b+d?n c+d?n b-a?n c-a?n c-b?n …..
三個操作數:…..
很快我們就發現太複雜了,能不能有一種比較簡單的方式將可能的計算組合全部表示出來呢?
我們想到,對於a,b,c,d四個數來說,參加計算的數是從其中任選出來的,我們可以用4bit的0、1字符串來記錄對應的數是否參與計算,1表示參與,0表示不參與。
如0001表示只有a參與計算
這樣對應的計算組合就有
dcba
0001
0010
…
1111
二進制轉化爲10進制,一個循環就可以搞定。
同理,由於計算只能爲加減運算,且參與計算的數最多隻有4個,則參與計算的操作符最多爲3個,用和上述類似的方法將操作符的組合窮舉出來。
代碼:
public class T002 {
/**
* @author magic_wk
*/
//獲取參與操作數的參與數組
public static int[] getOp(int n){
int [] a=new int[4];
int i=0;
while(n!=0){
a[i]=n%2;
n/=2;
i++;
}
return a;
}
//獲取參與計算的操作符數組
public static char[] getOpreater(int n){
char [] res=new char[3];
int i=0;
while(i<3){
res[i]=(n%2==0)?’+’:’-‘;
n/=2;
i++;
}
return res;
}
//計算表達式
public static int cal(int a,int b,int c,int d,int[] op,char[] opreater){
int res = 0;
switch (opreater[2]) {
case ‘+’:
res = d * op[3] + c * op[2];
break;
case ‘-‘:
res = d * op[3] – c * op[2];
break;
}
switch (opreater[1]) {
case ‘+’:
res = res + b * op[1];
break;
case ‘-‘:
res = res – b * op[1];
break;
}
switch (opreater[0]) {
case ‘+’:
res = res + a * op[0];
break;
case ‘-‘:
res = res – a * op[0];
break;
}
return res;
}
//檢查a,b,c,d的組合計算能否表示從1到40的任意數字
public static boolean check(int a,int b,int c,int d){
int[] op;
char[] opreater;
for(int i=1;i<=40;i++){
int f1=0;
for(int k=1;k<16;k++){
int f2=0;
op=getOp(k);
for(int j=0;j<8;j++){
opreater=getOpreater(j);
if(cal(a,b,c,d,op,opreater)==i){f2=1;break;}
}
if(f2==1){f1=1;break;}
}
if(f1==0)return false;
}
return true;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
for(int i=0;i<10;i++)
for(int j=i+1;j<15;j++)
for(int k=j+1;k<20;k++)
if(check(i,j,k,40-i-j-k)){
System.out.println(i+”, “+j+”, “+k+”, “+(40-i-j-k));
return;
}
System.out.println(“no answer!”);
}
}
最後結果爲:1, 3, 9, 27