分治 芯片測試

1. 芯片測試

在講解具體的芯片測試的分治策略算法之前,先來了芯片測試的意思

1.1 一次測試的過程

在這裏插入圖片描述

如上圖,A、B爲芯片。測試方法爲:將2片芯片(A和B)置於測試臺上,互相進行測試,測試報告是“好”或者“壞”,只取其一。

  • 假設:好芯片的報告一定是正確的,壞芯片的報告是不確定的(可能會出錯)

那麼上述測試的結果有四種可能,如下圖:

在這裏插入圖片描述

上面的結果應該不難理解

那麼現在問題來了:

  • 輸入:n片芯片,其中好芯片,至少比壞芯片多一片
  • 問題:設計一種測試方法,通過測試從n片中挑出1片好芯片
  • 要求:使用最少的測試次數

1.2 如何測試一塊芯片的好壞

針對上述問題,現在先來研究一下,如何在上述n片芯片中,測試出A是好芯片還是壞芯片?

  • 問題:給定芯片A,判定A的好壞
  • 方法:用其他n-1片芯片對A進行測試。

假設:n=7:好芯片數>=4

  1. A好,6個芯片中至少3個報“好”
  2. A壞,6個芯片中至少4個報壞

所以對於n是奇數情況下:好芯片數>=(n+1)/2
A好,至少有(n-1)/2個報“好”
A壞,至少有(n+1)/2個報“壞”

結論:
至少一半報好,A是好芯片
超過一半報壞,A是壞芯片

假設: n=8:好芯片數>=5

  1. A好,7個芯片中至少4個報“好”
  2. A壞,7個芯片中至少5個報“壞”

所以對於n是偶數:好芯片數 >= n/2+1.
A 好, 至少有 n/2個報告“好”
A 壞, 至少有 n/2+1個報告“壞”

結論:n-1份報告中
至少一半報好,A是好芯片
至少一半報壞,A是壞芯片

上面的分析,已經很清晰,我們已經知道如何測試一塊芯片的好壞。那麼人們最拿手的方法就是:暴力算法(蠻力算法)可以直接寫代碼了。。。

1.3 蠻力算法

測試算法:任取 1片測試,如果是好芯片,測試結束;如果是壞芯片,拋棄,再從剩下芯片中任取 1片測試,直到得到 1片好芯片

時間估計:

第一片是壞芯片,最多測試n-2次
第二片是壞芯片,最多測試n-3次

總計:Θ(n^2 )

#include<iostream>
#include<cmath>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <cstring>
#define MAX 100
using namespace std;

int main(){
    int n;
    int a[MAX];
    while(cin>>n){
        srand(time(NULL));
        memset(a,0,sizeof(a));
        for(int i=1;i<=n;i++){   /*芯片編號爲數組下標,從1開始*/
             cin>>a[i];             /*數組值代表芯片好壞,1爲真0爲假*/
        }
        int cnt=0;                  /*查找次數*/
        for(int i=1;i<=n;i++){
            int sum=0;
            for(int j=1;j<=n;j++){
                cnt++;
                if(i!=j&&a[i]==1&&a[j]==1)   /*兩片芯片都是好的*/
                    sum++;
                    if(i!=j&&a[i]==1&&a[j]==0)   /*測試芯片是壞的*/
                        sum+=rand()%2;
                    if(i!=j&&a[i]==0&&a[j]==0)   /*兩片芯片都是壞的*/
                        sum+=rand()%2;
                }
                if(sum>=n/2){
                    cout<<"查找次數:"<<cnt<<endl;
                    break;
                }
            }
        }
        return 0;
}

可見時間複雜度之高,數據量一多,肯定會超時。

1.4 分治算法設計思想

在分析分治算法的正確性之前,我們先給出這個算法的描述:

假設n爲偶數,將n片芯片兩兩一組做測試淘汰,剩下芯片構成子問題,進入下一輪分組淘汰。

淘汰規則爲:

  • “好,好” ==> 任留1片,進入下輪
  • 其他情況 ==> 全部拋棄

遞歸截止條件:n<=3
3片芯片,一次測試可得到好芯片
1或者2片芯片,不需要再測試,他們都爲好芯片。

上述算法過程就是我們給出的分治策略的設計。那麼爲什麼上述的策略是正確的呢?

回憶一下,前面的文章,要保證分治策略的正確性的基本條件是:子問題與原問題性質相同。下面我們就來證明,上述分治策略的子問題與原問題性質相同。

1.41 分治算法的正確性證明

原問題:n片芯片,其中好芯片,至少比壞芯片多一片

那麼子問題,命題1:當 n 是偶數時,在上述淘汰規則下,經過一輪淘汰,剩下的好芯片比壞芯片至少多1片

我們需要證明上述子問題的命題1是正確的。

證明:假設原問題中A,B都好的芯片有i組,A與B一好一壞的有j組,A與B都壞的有k組。那麼經過一輪淘汰後,好芯片還剩i片,壞芯片還剩k片。

因爲

  • 初始芯片總數 2i+2j+2k = n
  • 初始好芯片多於壞芯片:2i+j > 2k+j

得出:i>k

所以,剩餘的芯片好芯片比壞芯片,至少多1片。命題1 是正確的。即證明了上述分治算法的正確性。

當n爲奇數時,特殊處理。當n是奇數時,可能會出現問題,如圖:
在這裏插入圖片描述
可見淘汰後的子問題並不滿足於原問題性質相同,此時無法繼續測試。

  • 處理辦法是:當n爲奇數時,增加一輪對輪空芯片的單獨測試,如果該輪空芯片爲好芯片則算法結束,如果是壞芯片,則淘汰該芯片。

下面給出上述分治算法的僞碼描述:
在這裏插入圖片描述
補充說明:

  1. 第6點:if(1好1壞),則說明測試的兩個至少一個爲壞,所以未參加測試的一定爲好的
  2. 第7點:由於題目初始條件,好的芯片一定比壞的芯片多

1.42 時間複雜度分析

設輸入規模爲n,,每輪淘汰後,芯片數至少減半,測試次數(含輪空處理):O(n)

時間複雜度:

W(n) = W(n/2) + O(n)
W(3)=1,W(2)=W(1)=0

解上述方程的得:W(n) = O(n)

結果很振奮人心,你已經將一個O(n^2)級別的算法優化爲了O(n)O(n)O(n)級別!!!

2. 總結

最大的需要注意的地方就是:如何保證子問題與原問題性質相同:
可以:

  1. 增加額外處理(比如上述n爲奇數時對輪空數據的處理)
  2. 額外處理的工作量,不改變函數的階
  #include<iostream>
  #include <cmath>
  #include <stdio.h>
  #include <time.h>
  #include <stdlib.h>
  #include <cstring>
  #define MAX 100
  using namespace std;

  int main(){
      int n;
      int num1,num2,sum;
      int a[MAX],b[MAX];
      while(cin>>n){            
          srand(time(NULL));
          memset(a,0,sizeof(a));
          memset(b,0,sizeof(b));
          /*a數組標記芯片好壞,b數組記錄被保留芯片編號*/
          for(int i=1;i<=n;i++){                    
              cin>>a[i];
              b[i]=i;
          }
          num1=n;
          num2=1;
          sum=0; 
          while(1){
              /*芯片只剩一片或兩片則可以直接得到結果,結束循環*/
              if(num1<=2)                   
                  break;
              else if(num1%2){     /*芯片個數爲奇數*/
                  for(int i=1;i<num1;i+=2){
                      sum++;            /*記錄查找次數*/
                      if(a[b[i]]==1&&a[b[i+1]]==1)   /*兩片芯片都是好的*/
                          b[num2++]=b[i+rand()%2];
                      /*兩片芯片是壞的,rand模擬結果是好的這一隨機現象*/
                      if(a[b[i]]==0&&a[b[i+1]]==0&&rand()%2)                                               
                        b[num2++]=b[i+rand()%2];
                    }
                    b[num2++]=b[num1];      /*最後一塊芯片保留*/
               }
               else{                      /*芯片個數爲偶數*/
                   for(int i=1;i<=num1;i+=2){
                      sum++;
                      if(a[b[i]]==1&&a[b[i+1]]==1)   /*兩片芯片都是好的*/
                          b[num2++]=b[i+rand()%2];
                      if(a[b[i]]==0&&a[b[i+1]]==0&&rand()%2)  /*兩片芯片是壞的*/
                         b[num2++]=b[i+rand()%2];
                    }
               }
               num1=num2-1;     /*一次比較後保留芯片的個數*/
               num2=1;          /*下一次查找的開始位置*/
          }
          cout<<"查找次數:"<<sum<<endl; 
          cout<<"找到的芯片編號:"<<b[1]<<endl; 
      }
      return 0;
  }

參考鏈接:https://www.jianshu.com/p/1cec17bfb2d5
參考鏈接:https://blog.csdn.net/qq_37375427/article/details/102529005

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章