循环不变量

  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++;
            }
        }
    }
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章