循環不變量

  1. 循環不變量(分治結束標誌量):在循環中定義不發生變化的量,當不滿足定義時縮小規模。

    關於循環不變量定義的條件,我們是有充分條件必要條件之分的。

    1. 充分條件:這個條件一旦滿足,說明循環不變量滿足定義,過程立即結束;
    2. 必要條件:假設這個循環不變量滿足其定義,那麼一定滿足必要條件,否則它必然不滿足定義。但是如果加上某個$f(必要條件) $,將可以被轉換爲其對應的充分條件;

    所以我們首先需要滿足必要條件(這些必要條件應該是易得到的),並將這些必要條件從大到小依次排序(包含\(\supset\))。

    x[i] = 必要條件[i] from 必要條件[1] to 必要條件[n]: 
    	if f(x[i]) then satisfy 充分條件[i], break;
    	else i++;
    

    或者

    while(i<n){
    	if f(必要條件[i]) then satisfy 充分條件[i], break;
    	else i++;	// 縮小規模(也可以說是“遞歸”)
    }
    

    較大的必要條件滿足\(f\)爲真,則可以轉換爲充分條件;否則繼續檢查較小的必要條件。這一過程直到某一必要條件成功轉換,或者所有必要條件都不能轉換。若出現後者這樣的情況,我們還需要設置邊界條件,通常還被稱爲初始條件(因爲是在循環之前設置的特殊值)。

    initialize(n, 充分條件[n]);	// 當必要條件達到第n個時,設置充分條件[n]
    while(i<n){
        if i == n || f(必要條件[i]) then satisfy 充分條件[i], break;
        else i++;	// 縮小規模(也可以說是“遞歸”)
    }
    

    不過一般的,這樣的必要條件組我們是不清楚具體多長,只能通過“上一個不滿足,然後規模縮小到下一個必要條件”這樣的類似遞歸的方式前進。

    initialize(border);	// 這時具體問題具體分析邊界條件是什麼,假設爲border
    while(true){// 這表示只是解決一個問題,如果是解決多個性質相同的問題,while裏面則是問題規模
        if border(必要條件) || f(必要條件) then satisfy 充分條件, break;
        else 必要條件 = next(必要條件);	// 遞歸思想,實現自我覆蓋
    }
    
  2. KMP算法中的循環不變量分析

    如果在\(j\)位上失配,則假設下次匹配在\(k_j\)處開始。很顯然這個\(k_j\)就是循環不變量,那麼我們給出它的定義:“子串[j-1]中最長重複前後綴子串的長度+1”,記爲\(k_j=\max(j-1)+1\)

    顯然\(k_{j+1}\)(最大)必要條件就是“子串\([k_j-1]=[\max(j-1)]\)”與“子串[j-1]中等長的後綴”相等(就是下\(k_j\)的充分條件),(最小)充分條件是“子串\([k_j]=[\max(j)]\)”與“子串[j-1]中等長的後綴”相等。這兩者之間相差的條件是“\(T[j]=T[k_j]\)”(第\(j\)位與第\(k_j\)位匹配)。

    如果不滿足轉換條件,就需找第二必要條件:“子串\([k′_{j+1}-1]\)”與“子串\([j-1]\)中等長的後綴”相等,而根據\(k_j\)的充分條件可知:“子串\([j-1]\)中等長的後綴”與“子串\([k_j-1]\)中等長的後綴”相等,所以其實也就是\(k_{k_j}\)的充分條件……依次類推,我們可以得出結論:\(k_{j+1}\)的必要條件簇爲\(k_j,k_{k_j},…,k_{…k_j}\)的充分條件。

    KMP算法

    它的邊界條件就是:當1號位都不匹配時,必須規定一個數值,這裏我們定義爲0。

    void GetNext(MString T, int*& next){
        // 定義當前失配指針(cmm)和下次匹配開始指針(nbm)
        int curMissMatch = 1, nextBeginMatch;
        // 設置邊界條件:當第1位不匹配時,規定下一次從0位開始匹配
        nextBeginMatch = next[curMissMatch] = 0;
        while(curMissMatch < StrLen(T)){// 設置問題規模:from 1 to len
            if(nextBeginMatch == 0 || (T[curMissMatch] == T[nextBeginMatch])){
                // 如果達到邊界調教或者滿足轉換條件則:直接+1。移動cmm相當於break,跳出當前問題
                next[++curMissMatch] = ++nextBeginMatch;
            }
            else{
                // 如果沒有達到上述條件:向後迭代。不移動cmm:相當於一直在同一個問題中迭代
                nextBeginMatch = next[nextBeginMatch];
            }
        }
    }
    
  3. ColorSort中的循環不變量分析

    很顯然有兩個分隔指針,一個是zeroCursor,在其左邊全是0;另一個是twoCursor,在其右邊全是2。所以循環不變量就是它倆。給出的定義也就是“左邊全是0”和“右邊全是2”(也是充分條件)。

    所以\(k′_z=k_z+1\)最大必要條件是“\(0\sim k_z\)全是0”(\(k_z\)充分條件),最小充分條件爲“\(0\sim k_z+1\)”全是0,顯然有轉換條件:“在\(k_z+1\)處的值爲0”;\(k′_t=k_t-1\)最大必要條件是“\(k_t\sim n\)全是2”(\(k_t\)充分條件),最小充分條件爲“\(k_t-1\sim n\)”全是2,顯然有轉換條件:“在\(k_t-1\)處的值爲2”。

    這裏有點兒和前面不一樣,之前我們是被動判斷是否滿足轉換條件,這裏我們可以主動讓其滿足

    void sortColors(vector<int>& nums) {
        // 如果數組的長度<2,沒必要排序了
        if(nums.size() < 2){
            return ;
        }
    
        // 定義循環不變量zeroCursor和twoCursor:
        //      1. zeroCursor的左邊永遠只有0;
        //      2. twoCursor的右邊永遠只有2;
        int zeroCursor = 0, twoCursor = nums.size() - 1;
        // 定義cursor作爲循環變量:zeroCursor和cursor之間只有1;
        int cursor = 0;
    
        while(cursor <= twoCursor){// 確定問題規模
            // 假設中間狀態爲:zeroCursor左邊只有0,由於cursor左邊都已經排過序,所以zeroCursor與cursor之間沒有0
            //                           ↓(cursor)
            //      0,0,0,1,1,1,0,0,1,2,1,2,2,2
            //                 ↑(0)                   ↑(2)
            // 我們要保證定義不變,就要將cursor指向的0移至zeroCursor的左邊(交換),然後移動cursor和zeroCursor;
            if(nums[cursor] == 0){
                swap(nums, cursor++, zeroCursor++);// 主動滿足zero的轉換條件
            }
            // 假設一段時間之後的中間狀態爲:twoCursor右邊只有2,現在要將所有的2移至twoCursor右邊
            //                           ↓(cursor)
            //      0,0,1,1,1,1,2,1,2,1,2
            //             ↑(0)            ↑(2)
            // 這是我們要保證定義不變,就要將cursor指向的2移至twoCursor的右邊(交換);
            else if(nums[cursor] == 2){
                swap(nums, cursor, twoCursor--);// 主動滿足two的轉換條件
            }
            else if(nums[cursor] == 1){
                cursor++;
            }
        }
    }
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章