芯片测试——枚举与分治

问题描述

有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)

你可能感兴趣的:(算法分析)