分治法之芯片測試

最近在看算法導論時,看到了這道芯片測試的題,想了很久,總結一下我的思路

一、問題描述

Diogenes教授有n個被認爲是完全相同的VLSI芯片,原則上它們是可以互相測試的。教授的測試裝置一次可測二片,當該裝置中放有兩片芯片時,每一片就對另一片作測試並報告其好壞。一個好的芯片總是能夠報告另一片的好壞,但一個壞的芯片的結果是不可靠的。這樣,每次測試的四種可能結果如下:

A芯片報告 B芯片報告 結論
B是好的 A是好的 都是好的,或都是壞的
B是好的 A是壞的 至少一片是壞的
B是壞的 A是好的 至少一片是壞的
B是壞的 A是壞的 至少一片是壞的

a)證明若多於n/2的芯片是壞的,在這種成對測試方法下,使用任何策略都不能確定哪個芯片是好的。假設壞的芯片可以聯合起來欺騙教授。

b)假設有多於n/2的芯片是好的,考慮從n片中找出一片好芯片的問題。證明n/2對測試就足以使問題的規模降至近原來的一半。

c)假設多於n/2片芯片是好的,證明好的芯片可用Θ(n)對測試找出。給出並解答表達式測試次數的遞歸式。

二、題幹解讀

  1. 當兩個芯片都報告是好的,如果其中一個A是壞的,因爲另一個B說A是好的,所以B也是壞的
  2. 當兩個芯片的報告一好一壞時:
    1. 說對方好的那個芯片一定是壞的:假設A說B是好的,B說A是壞的,若A是好的,則B是好的,那麼B說A是壞的就自相矛盾了,所以A一定是壞的。
    2. 說對方壞的那個芯片可能是好的,也可能是壞的:經過上面的分析,A一定是壞的,那麼A說的就不可信(可能是對的可能是錯的),那麼B說A是壞的,B好的壞的都有可能
  3. 當兩個芯片的報告都是壞的時:
    1. 首先不可能兩個都是好的
    2. 若一個是好的:假設A是好的,則A說B是壞的,成立;B是壞的,所以還不管B的說辭
    3. 兩個都是壞的,就無所謂他們說啥了

三、算法分析

對於問題a,採用窮舉法,將任何一片芯片與其它所有芯片進行比較,因爲有多於n/2的芯片是壞的,且壞的芯片可以聯合起來欺騙教授,則測試結果是不可靠的,無法判斷出該芯片是好是壞。

對於問題b和c,先假設:設有a個好的芯片數,b個壞的芯片數,z測試結果爲全好的測試對,其中芯片(注意這裏是芯片全爲好的,不是測試結果爲好的)全是好的測試對爲x芯片全爲壞的測試對爲yx+y=z

分治算法設計思想

這裏先說明一下前提:好芯片比壞芯片至少多1片!(以下例子,想不明白可以假設總共有4片或者6片芯片)

一、隨機的兩兩配對,則共有⌊n/2⌋對,分別測試。

二、根據問題描述:

(1)如果測試結果爲一好一壞,或者兩壞,那麼把這對丟棄

  • 很好理解,本來好的比壞的多,丟一個好的一個壞的,那麼好的還是比壞的多,丟兩個壞的就更不用說了

(2)如果測試結果爲兩好,那麼隨意丟棄其中一個,留下一個:

  • 解釋:這樣做的主要目的是減小芯片的規模,經過第一步後,現在就剩兩個都報告好的情況了(因爲好的芯片比壞的芯片多,所以不存在所有的芯片對均測試爲一好一壞),這種情況一對的兩個芯片要麼都是好的,要麼都是壞的,那麼從每對芯片中隨意扔一個,最後好的芯片還是比壞的芯片多,但芯片數減少了一半

(3)這裏要考慮芯片爲奇數的情況:

  • n爲奇數,則剩餘一個芯片沒有配對。那麼這個時候,就要根據第(2)步中測試結果爲兩好的芯片對數z進行判斷:若z爲奇數,丟棄這個芯片;若z爲偶數(0也是偶數),留下這個芯片。
    • 解釋:(a)當z爲奇數時,假設最壞的情況,即最後剩的那一個芯片是好的,假設前面(z-1)對的好芯片與壞芯片數相同,那麼現在就剩一對芯片了,這對芯片要麼都是好的,要麼都是壞的,如果是壞的,則好的芯片數比壞的芯片數少一,不成立,則這對芯片都是好的。即當z爲奇數時,好芯片對數-壞芯片對數>= 1對,即好芯片數-壞芯片數>= 2,丟掉一半後,好芯片數-壞芯片數>=1(見下圖);那麼把最後多出來的一個芯片必須丟掉,最後好的芯片還是比壞的芯片多
    • (b)若z爲偶數(0也是偶數),還是假設最壞的情況,即這z對芯片好的與壞的一樣多,那麼這多出來的一片必是好的;若多出來的這一片是壞的,那麼z對芯片中,好的芯片對數-壞的芯片對數>=2對,即好芯片數-壞芯片數>= 4,減半後,好芯片數-壞芯片數>= 2,即使放進來一個壞芯片,最後好的芯片還是比壞的芯片多

這樣操作後,留下的好芯片數一定還是大於壞的芯片數的,且經過⌊n/2⌋對測試後,原問題的規模降低了近一半(解決了問題(b))。

三、 重複第(二)步,當n<=2時,就只剩下了好的芯片。

因爲始終是好芯片數多於壞芯片數,那麼當n<=2時,就只有好的芯片了

總結:

測試結果爲一好一壞或者兩壞的芯片對中至少一片是壞的,把這對丟棄能保證剩下的芯片中好芯片的數量仍大於壞芯片的數量。

測試結果爲全好的,這對芯片有可能全爲好,有可能全爲壞,隨意丟棄其中一個,留下 一個。

設好芯片的對數爲x,壞芯片的對數爲y。考慮n的情況:

      若n爲偶數,則x>y,所以留下的好芯片數還是大於壞的芯片數;

      若n爲奇數,則剩餘一個沒有配對的芯片。考慮z的情況:

           若z是奇數,則x>y,丟棄這個芯片,這樣留下的好芯片數還是大於壞的芯片數;

           若z是偶數,考慮這個芯片的情況:

                若芯片是好的,則x>=y,留下這個好的芯片,這樣留下的好芯片數還是大於壞的芯片數;            

                若芯片是壞的,則x-y>=2,留下這個壞的芯片,這樣留下的好芯片數還是大於壞的芯片數。

綜上所述,留下的好芯片數一定還是大於壞的芯片數的。

對於問題c:採用b中的操作方法,遞歸式爲:f(n) = f(n/2) + n/2。這個遞歸式符合主定理中的第三種情況,所以好的芯片可用Θ(n)對測試找出。

四、代碼實現

class Test {
    public static void main(String[] args) {
    	//用數組來存儲芯片,1代表爲好的芯片,0代表爲壞的芯片,下面的數組總有23個芯片,12個爲好的
        int[] arr = {1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1};
        System.out.println(Arrays.toString(getRight(arr,arr.length)));
    }

    //定義一個測試平臺,來模擬題幹中的測試裝置,好的芯片輸出對的,壞的芯片隨機輸出
    public static boolean[] judge(int x, int y) {
        boolean[] result = new boolean[2];
        if (x == 1 && y == 1) {
            result[0] = true;
            result[1] = true;
        } else if (x == 1 ^ y == 1) {
            result[0] = false;
            result[1] = ((int) (Math.random() * 2)) == 1;
        } else {
            result[0] = ((int) (Math.random() * 2)) == 1;
            result[1] = ((int) (Math.random() * 2)) == 1;
        }
        return result;
    }

    //返回一個正確的芯片
    public static int[] getRight(int[] arr,int length) {
    	//當芯片個數小於等於2時,均爲好的芯片,返回
        if (length<= 2) {
            int[] z = new int[length];
            for (int i = 0; i < length; i++) {
                z[i] = arr[i];
            }
            return z;
        }
        //這裏用數組存儲選出來的芯片,數組定義爲最壞情況下的長度,其它情況數組後邊可能有多餘的位數
        //所以用length來標明數組中的有效長度
        int[] tmp = new int[length / 2 + 1];
        boolean[] result = null;//這個數組用來接收測試結果

        int point = 0;//記錄選出來的芯片個數
        //芯片兩兩爲一組,進行測試
        for (int i = 0; i < length-1; i += 2) {
            result = judge(arr[i], arr[i + 1]);
            if (!result[0] ^ result[1]) {
                //兩個芯片都測試爲好的情況,則隨機選出其中一個芯片
                int x = (int) (Math.random() * 2);
                tmp[point] = arr[i + x];
                point++;
            }
        }
        //考慮總個數爲奇數的情況
        if (length % 2 == 1) {
        	//這時要根據選出來的個數進行判斷,偶數就加進去,奇數就扔掉
            if (point % 2 == 0) {
                tmp[point] = arr[length - 1];
                point++;
            }
        }
		//遞歸,繼續兩兩進行測試,直到芯片個數少於3個
        return getRight(tmp,point);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章