有n片芯片,已知好芯片比坏芯片至少多1片。现在需要通过测试从中找出1片好芯片,测试方法如下:将2片芯片放到测试台上,2片芯片互相测试并报告测试结果(即好或者坏);其中,好芯片的报告是正确的,而坏芯片的报告是不可靠的。请在上述背景下解决以下问题:
问题1:若用枚举法从中找出1片好芯片,在最坏的情况下,需要多少测试次数?请给出详细的分析过程;
问题2:相比于枚举法,为保证使用更少的测试次数,请设计一个合适的算法解决上述问题。
问题一:
若n=1,因为好芯片比坏芯片至少多1,所以该芯片就是好的,此时测试次数为0;
若n=2,同理,只可能是两个芯片都是好的,测试次数为0;
若n>2时:
①n为偶数时至少有n/2+1个好芯片,按照枚举法,每次拿出一个芯片与其他芯片做测试,若该芯片是好芯片,那么至少有n/2个芯片会报告它是好芯片,若该芯片是坏芯片,那么至少有n/2+1个芯片会报告它是坏芯片。
因此,若该芯片是好芯片,那么至少有一半的芯片会报告它是好芯片,否则,它就是坏芯片。
②n为奇数时至少有(n+1)/2个好芯片,按照枚举法,每次拿出一个芯片与其他芯片做测试,若该芯片是好芯片,那么至少有(n-1)/2个芯片会报告它是好芯片,若该芯片是坏芯片,那么至少有(n+1)/2个芯片会报告它是坏芯片。
因此,若该芯片是好芯片,那么至少(n-1)/2的芯片会报告它是好芯片,否则,它就是坏芯片。
问题二:
首先,对于该问题的结果进行分析:
①由问题一可知,当n<=2时,每一个芯片都是好芯片,没必要进行测试,因此测试次数为0
②当n>2时,由题意可知,一开始的好芯片是比坏芯片多的,而每次的相互判断情况有以上四种。
如果n是偶数,将n个芯片两个为一组进行判断,当出现第2、3、4种情况的时候,说明芯片A和B至少有一个是坏芯片,或者都是坏芯片。此时若是将这两个芯片移除出,则剩余的芯片中,好芯片数量还是多于坏芯片的,这样就将问题规模缩小了,若当前状态下,没出现情况2,3,4,即只出现情况1,说明每一对芯片同为好或者同为坏,那么每一对芯片就随意拿掉一个,剩下的好芯片数量还是多于坏芯片数量。
如果n是奇数,那么先对单独一个芯片进行枚举法判断,若判断得出该芯片是好芯片,那么就不用计算下去,若判断得出该芯片是坏芯片,则将它拿掉,再对剩下的芯片进行如上判断。
综上所述,对于n是奇数时,可以对最后一个按照问题一的算法判断,若是好芯片,算法结束,若是坏芯片,则弃掉,剩余芯片个数转化为偶数个;对于n是偶数,将芯片两两一组,有n/2组,若测试结果为情况1,则随意丢弃一块芯片,若测试结果为情况2、3、4,则两块都丢弃,若再次出现奇数块的芯片,则再取出一块做问题一的算法判断,直到剩下两块及以下,则都为好芯片,任取一块即可。
问题一:
函数:seek(all, n)
输入:包含所有芯片的集合all,芯片个数n
输出:确定是好芯片的芯片下标
S1: for i ← 0 to n/2//逐个遍历第0个到第n/2个芯片,直到找到一个好芯片
S2: isTrue ← 0//记录其他芯片对此芯片判断为好的个数
S3: for j ← 0 to n-1
S4: if j!=i//除了第i个的其他芯片
S5: then pd ← judge(all[i], all[j])//返回第j个芯片对第i个芯片的好坏判断(true/false)
S6: if pd == true
S7: then isTrue ← isTrue+1//判断为true,记录个数+1
S8: if isTrue > n/2//判断结果为好的个数超过一半
S9: then return i//返回确定为好芯片的下标i
S10: if isTrue == n/2 and n%2 != 0//奇数个的特殊情况判断
S11: then return i//返回确定为好芯片的下标i
问题二:
函数:does(S, n)
输入:芯片集合S和个数n
输出:找到的好芯片的下标
S1: if n <= 2
S2: then return 0//返回第一块芯片
S3: if n%2 != 0//奇数个
S4: then if test(S, n) == true//判断最后一块芯片是不是好芯片
S5: then return n//n为好芯片,返回
S6: else n ← n-1//个数-1,即舍弃最后一个芯片
S7: nums ← 0//记录一轮判断后的剩余个数
S8: for i from 0 to n-1 step 2//两个两个判断(此时n肯定是偶数)
S9: if pd(S[i], S[i+1]) == true//两芯片结果若是情况1则为true,否则为false
S10: then nums ← nums+1//个数+1
S11: newS[nums-1] ← S[i]//结果为true,任取一个芯片存入新的集合
S12: return does(newS, nums)//将问题规模变为小于等于原来的1/2
问题一:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
//好芯片为true,坏芯片为false
boolean[] all= {false, false, false, false, false, true,true,true, true, true, true};
//芯片个数
int n=all.length;
if(n<=2) {
//该情况下所有芯片为好芯片
System.out.println("第1个是好芯片");
}else {
int result=seek(all,n);
System.out.println("第"+(result+1)+"个是好芯片");
}
}
private static int seek(boolean[] all, int n) {
for (int i = 0; i <= n/2; i++) {//0到n/2中必定有好芯片
int isTrue=0;//记录其他芯片对此芯片判断为好的个数
for (int j = 0; j <= n-1; j++) {
boolean pd=false;
if(i!=j) {
pd=judge(all[i], all[j]);//返回第j个芯片对第i个芯片的好坏判断(true/false)
if(pd==true) {
isTrue++;
}
}
}
if(isTrue>n/2) {//判断结果为好的个数超过一半
return i;//返回确定为好芯片的下标i
}
if(isTrue==n/2&&n%2!=0) {//奇数个的特殊情况判断
return i;//返回确定为好芯片的下标i
}
}
//由题意可知不可能出现这种情况,因此也不可能返回-1
return -1;
}
private static boolean judge(boolean a, boolean b) {
//如果b是好芯片,就返回b对a的判断结果,否则就返回a是坏芯片
if(b==true) {
return a;
}else {
return false;
}
}
}
问题二:
Main方法:
//好芯片为true,坏芯片为false
boolean[] all= {true, true, true, true, true, true, false, false, false, false, false};
//芯片个数
int n=all.length;
int result=does(all, n);
System.out.println("最终剩下的第"+(result+1)+"个是好芯片");
does方法:
private static int does(boolean[] S, int n) {
if(n<=2) {
return 0;//返回第一块芯片
}
if(n%2 != 0) {//奇数个
//以下为判断第n个是不是好芯片
int k=0;
for (int i = 0; i < n-1; i++) {
boolean b=judge(S[i], S[n-1]);
if(b==true) {
k++;
}
}
if(k>=n/2) {
return n-1;//最后一个是好芯片,返回
}else {
n--;//弃掉最后一个
}
}
int nums=0;//记录一轮判断后的剩余个数
boolean[] newS=new boolean[n/2];//记录剩下的芯片,最多为一半
for (int i = 0; i < n-1; i+=2) {//两个两个判断(此时n肯定是偶数)
//相互判断
boolean b1=judge(S[i], S[i+1]);
boolean b2=judge(S[i+1], S[i]);
if(b1==true&&b2==true) {//情况1
nums++;//个数+1
newS[nums-1]=S[i];//结果为true,任取一个芯片存入新的集合
}
}
return does(newS, nums);//将问题规模变为小于等于原来的1/2
}
将judge方法进行改进,使得坏芯片的判断具有随机性:
private static boolean judge(boolean a, boolean b) {
//如果b是好芯片,就返回b对a的判断结果,否则就返回a是坏芯片
if(b==true) {
return a;
}else {
//体现随机性
int i=(int)(Math.random()*2);
if(i==0) {
return true;
}else {
return false;
}
}
}
问题二:
结果具有随机性,取决于剩下的芯片多少,即每次情况1、2、3、4的多少
对于问题一:
因为该算法实际上为枚举法,根据最坏情况,第一次 for循环需要遍历一半,因此时间复杂度为O(n/2),第二次遍历全部,时间复杂度为O(n-1),因此该枚举法的总的时间复杂度为(n2-n)/2,即O(n2)。
对于问题二:
该算法实际上是基于递归的分治算法,每次将问题规模缩小递归调用,根据最坏情况,每两个芯片只保留一个,即每次对芯片集合进行遍历O(n)后,再加上递归规模会变成原来的n/2,T(n)=T(n/2)+O(n),可知时间复杂度为O(n)