问题描述:在八枚外观相同的硬币中,有一枚是假币,并且已知假币与真币的重量不同,但不知道假币与真币相比较轻。可以通过一架天平来任意比较两组硬币,设计一个高效的算法来检测出这枚假币。本文的代码将问题简化,默认假币比真币轻,并且将8硬币扩展为n枚硬币(n任取),利用减治法来求解问题。
主要的思想:如果是硬币的数量为偶数,平分硬币称重,取较轻的一组再称重,如果硬币为奇数,默认取出第一个硬币,然后按照偶数个称重,如果两组重量相等,那么假币为取出的一个,如果不等,那么轻的这组再次称重。递归这个过程,直到找出假币。
import java.util.Random;
import java.util.Scanner;
public class Ncoins {
public int[] coins;
Ncoins(int n){
coins = new int[n];
for (int i = 0; i < coins.length; i++) {
coins[i] = 1;
}
Random r = new Random();
coins[r.nextInt(n)] = 0;
}
public void show() {
for (int i = 0; i < coins.length; i++) {
System.out.print(coins[i] + " ");
}
}
/**
* 判断是否为奇数
* @param n
* @return
*/
public boolean isOdd(int n) {
if(n % 2 == 0) return false;
else return true;
}
/**
* 求和
* @param first
* @param last
* @return
*/
public int getSum(int first, int last) {
int sum = 0;
for(int i = first; i <= last; i++) {
sum += coins[i];
}
return sum;
}
/**
* 核心算法
* @param first
* @param last
* @return
*/
public int findFakeCoin(int first,int last) {
int sum1,sum2,sum3;
float a = (float)(last - first) / 2;
int b = (int) (last - a); //取中间的数组下标
if(isOdd(last - first + 1)) {
sum1 = getSum(first + 1,b);
sum2 = getSum(b + 1, last);
sum3 = coins[first];
if(sum1 == sum2) return first;
else if(sum1 < sum2) return findFakeCoin(first + 1, b);
else return findFakeCoin(b + 1, last);
}
else {
sum1 = getSum(first,b);
sum2 = getSum(b + 1,last);
if(sum1 > sum2) return findFakeCoin(b + 1,last);
if(sum1 < sum2) return findFakeCoin(first,b);
}
return -1;
}
public static void main(String[] args) {
System.out.println("输入硬币个数:");
Scanner s = new Scanner(System.in);
Ncoins n = new Ncoins(s.nextInt());
n.show();
int i = n.findFakeCoin(0, n.coins.length - 1);
System.out.println("位置:" + i +" 为假币");
}
}
运行结果(位置从0开始)