-
循環不變量(分治結束標誌量):在循環中
定義
不發生變化的量,當不滿足定義時縮小規模。關於循環不變量定義的條件,我們是有
充分條件
和必要條件
之分的。- 充分條件:這個條件一旦滿足,說明循環不變量滿足定義,過程立即結束;
- 必要條件:假設這個循環不變量滿足其定義,那麼一定滿足必要條件,否則它必然不滿足定義。但是如果加上某個$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(必要條件); // 遞歸思想,實現自我覆蓋 }
-
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}\)的充分條件。它的邊界條件就是:當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]; } } }
-
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++; } } }
循環不變量
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.