迴圈不變式的思想及其應用

2021-05-23 08:46:17 字數 2761 閱讀 4256

迴圈不變式的思想及其應用

迴圈不變式(loop invariants)不只是一種電腦科學的思想,準確地說是一種數學思想。在數學上闡述了通過迴圈(迭代、遞迴)去計算乙個累計的目標值的正確性,屬於基礎數學的範疇,而且在計算機上也應用廣泛。初次見到這個詞是在《演算法導論》,在第二章描述了這個思想和正確性,後來又在《程式設計珠璣》上再次重逢,不得不說是一種緣分。決定把自己的一些認識記錄下來,用於闡述和傳播這種優秀的基礎方法。

迴圈不變式主體是不變式,也就是一種描述規則的表示式。其過程分三個部分:初始,保持,終止。

1、初始:保證在初始的時候不變式為真。

2、保持:保證在每次迴圈開始和結束的時候不變式都為真。

3、終止:如果程式可以在某種條件下終止,那麼在終止的時候,就可以得到自己想要的正確結果。

在這三個部分中,前兩個是條件,最有乙個是結論。看起來很淺顯易懂,小學生就能明白的道理,但是道理明白不代表會使用。下面就舉兩個例子。

例子1、

問題:給出二分查詢的實現,函式原形 int bsearch(int num, int count, int goal); num是儲存已經從小到大排序好的數字,count是陣列元素個數,goal是待查詢的數字;若查詢成功則返回元素的下標,否則返回-1。

分析:二分查詢的原理很簡單:把陣列分成三份,中間乙份只有乙個元素,其他兩份個數基本相同,如果中間的元素等於目標值,就返回它的下標;如果大於,則去前半數組繼續執行二分查詢;如果小於,則去後半數組;直到陣列沒有元素為止。這是迴圈不變式的乙個計算機演算法應用,我們可以按照它的規則來做,我們的不變式就是:當前陣列不為空;迴圈結束條件是:當前陣列為空或找到目標元素。

**:**可以進一步優化:

小結:上述**是直接手寫,沒有經過上機測試,由於遵循了迴圈不變式的要求,我有信心不出現邏輯錯誤,倘若真出現了錯誤,那麼就一定是我沒有遵循迴圈不變式的要求。其實二分法作為一種簡單靈活而又高效的演算法在2023年就被人提出來了,但是第乙個正確的演算法確實在2023年發表的,期間這麼多年都沒有人發布正確的演算法,有人漏掉元素導致結果錯誤,有人重複計算導致死迴圈。其原因在於邏輯不夠嚴密,沒有正確的方法去指導他們寫這個演算法,但是有了迴圈不變式的幫助,一切變得明朗了許多。

例子2、

問題:有數列a[n],n屬於自然數,a[1] = 2,遞推公式1:a[i] = 2/(a[i-1]+1),i>1,i屬於自然數。求通向公式

分析:這個問題求數列的通向公式,是乙個遞迴式,可用數學歸納法:先分析數列規律,再用假設公式帶入k,然後計算a[k]的值並驗證,繼而推廣到a[n]。

解題:第一步,找規律:

a[1] = 2

a[2] = 2/(2+1) = 2/3

a[3] = 2/(2/3+1) = 6/5

a[4] = 2/(6/5+1) = 10/11

則根據規律設遞推公式2:a[i] = 2a[i-1]的分母/(2a[i-1]的分母+(-1)^i),i>1,i屬於自然樹。

第二步,根據規律列出a[k]公式:

i=1,a[i] = 2;

i=2,a[i] = 2/(2+(-1)^2) = 2/(2+1) 

i=3,a[i] = 2(2+(-1)^2)/(2(2+(-1)^2)+(-1)^3)= 2(2+1)/(2(2+1)-1)

i=4,a[i] = 2(2(2+(-1)^2)+(-1)^3)/(2(2(2+(-1)^2)+(-1)^3)+ (-1)^4) = 2(2(2+1)-1) /(2(2(2+1)-1)+1)

i=k,a[i] = 2(...(2(2(2+(-1)^2)+(-1)^3)+...)+(-1)^(k-1))/(2(...(2(2(2+(-1)^2)+(-1)^3)+...)+(-1)^(k-1))+(-1)^(k))

第三步化簡a[k]:

由於a[k]分母中最深入的括號有k層,所以第乙個+前面的2的係數2^(k-2),第乙個+後面的(-1)^2的係數是2^(k-2),這個看作迴圈不變式的初始條件,中間迴圈第二個+後面的(-1)^3係數為2^(k-3),一直到最有乙個+後面的(-1)^k係數為2^(k-k),總共有k-1個帶有(-1)的項。在這些專案中,由於初始化計算正確,中間步驟正確,那麼最後結果一定正確。

若k為奇數:

a[k]分母=2^(k-1)+2^(k-2)-2^(k-3)+....-2(k-k),利用等比數列通向和公式求得=2^(k-1)+ (2^k-2)/3 - (2^(k-1)-1)/3 = (2^(k+1)-1)/3

則a[k]分子= (2^(k+1)-1)/3+1,a[k] = (2^(k+1)+2)/(2^(k+1)-1)

若k為偶數:a[k] = (2(k+1)-2)/(2^(k+1)+1),過程同上,略。

a[k] = (2(k+1)-2(-1)^k)/(2^(k+1)+(-1)^k)

第四步,歸納證明

過程略小結:在這個題目裡面最難的就是根據那第三步,如果使用第二步得到的中間帶省略號的公式。這時候就需要迴圈不變式來幫忙,只要初始值正確(正確計算最內部+兩側式子的係數),迴圈過程無誤(正負號交替,係數依次/2),迴圈可以結束(共迴圈k-1次),那麼結果就是正確的,可以放心大膽地繼續劃簡,最後得到正確結果。

總結:迴圈不變式使用特點

1、在迴圈,迭代,遞迴等用上次結果作為下次初始值的累計過程都可以準確無誤的使用。

2、在迴圈不變式三要素,初始,保持,結束。初始一定要正確,迴圈的時候也要保證不變式為真,迴圈一定要可以結束才能得到正確結果。其中後兩條比較容易疏漏,解題時要慎而又慎嚴謹對待迴圈中不變式和結束條件。

3、可以把三要素再進行一次轉化,使之更適合電腦程式:一次迴圈開始時候不變式為真,一次迴圈完畢時為真,總迴圈可以結束,結果必定正確。

迴圈不變式

先看引用自由cay horstmann寫的 computing concepts with c essentials 3rd 一書的,用以計算a n的例子 double power double a,int n else return r 粗看一下,用此方法計算a n時比原始方法進行迴圈次數要少很多...

迴圈不變式

在演算法中,有乙個重要的概念就是迴圈不變式,迴圈不變式主要用來幫助我們理解演算法的正確性。關於迴圈不變式,我們必須證明三條性質 1.初始化 迴圈的第一次迭代之前,它為真。2.保持 如果迴圈的某次迭代之前它為真,那麼下次迭代之前它仍為真。3.終止 在迴圈終止時,不變式為我們提供了乙個有用的性質,該性質...

利用迴圈不變式寫出正確的二分查詢及其衍生演算法

利用迴圈不變式寫出正確的二分查詢及其衍生演算法 先看看定義 二分查詢的搜尋過程從陣列的中間元素開始,如果中間元素正好是要查詢的元素,則查詢成功 如果某一特定元素大於或者小於中間元素,則在陣列大於或小於中間元素的那一半中查詢,而且跟開始一樣從中間元素開始比較。如果在某一步驟陣列為空,則代表找不到。這種...