邏輯之力:程式驗證與形式化方法揭秘

banq發表於2025-03-01


這篇部落格是Micheal Huth和Mark Ryan寫的《電腦科學中的邏輯》這本書的一個簡單介紹[1]。它會講到書裡第一章的前五個部分。為了讓事情簡單點,這個介紹寫得很隨意。所以,書裡很多複雜的定理和證明這裡都沒提。

陳述句
我們檢查程式對不對,主要就是想看看程式程式碼是不是按照我們想的那麼執行。有很多方法可以做到這一點:

  • 你可以寫一堆測試,看看程式在不同情況下是不是正常工作。
  • 你也可以寫個模糊測試器,用隨機輸入不停地跑程式,確保它不會出錯。等等。

但這些方法的問題是,總有點不確定。你怎麼能確定只用幾個測試就能保證程式完全沒問題呢?可能有些情況你沒測到,結果就出錯了。

這就是形式化方法的好處:它們不會給錯誤留任何機會,可以用來檢查程式是不是總是按我們想的那麼執行。

不過,要用這些方法,我們首先得有個非常詳細的程式說明。這些方法之所以叫“形式化”,是因為它們用數學技術來驗證程式對不對。所以,我們需要一個程式說明,這樣我們才能嚴格地提出和證明我們的觀點。

我們可能會本能地用自然語言來寫這個說明。畢竟,用自然語言描述東西對我們來說很自然:這就是你和朋友聊天或和同事爭論的方式;我們習慣用這種方式表達和思考。但是,自然語言不適合寫程式說明——這樣做就像用畫筆的毛來擰螺絲一樣。問題是自然語言太複雜,太模糊了。想想這句話:

男人用望遠鏡看見了女人

這句話可以有兩種理解:

  • 男人透過望遠鏡看到了女人。
  • 男人看到了一個拿著望遠鏡的女人。

在現實生活中,我們通常會根據上下文猜出正確的意思,或者讓對方解釋清楚。但

是,在做數學論證時,絕對不能有歧義,所以我們需要別的東西:一種更簡單的語言,可以清楚地表達我們的意思,一種有明確規則並且可以推理的語言。

如果我們想讓計算機自動幫我們做這種正確性檢查,那麼它得足夠嚴格,這樣我們才能寫程式來驗證和處理這種語言裡的句子。

雖然自然語言不適合做這個,但它很熟悉。所以,我們先從思考開始,看看能不能想出更簡單的東西。

我們通常用陳述句來論證;這些句子描述了世界的一些事情。比如,看看下面兩個英文句子:

如果下雨而我沒有帶傘,那我就會被淋溼。下雨了,但我沒被淋溼。

如果一個矩陣是方陣,且其行列式不為零,則該矩陣是可逆的。該矩陣是方陣,但不可逆。

你能從這些句子中推斷出什麼?你會怎麼證明?雖然這兩個句子說的東西完全不同(一個是常識,另一個是關於線性代數),但它們的結構是一樣的,論證的結構也一樣。

比如,從第一句話我們可以提出這個論點:

我沒有被淋溼。所以,下雨而我沒有帶傘這不可能是真的。既然我知道下雨了,那就意味著我帶了傘。

同樣,從第二句話開始,我們可以幾乎一樣地重複這個論點,只需要換幾個詞!

該矩陣不可逆。所以,它是方陣且行列式不為零的說法不成立。因為我知道它是方陣,所以行列式一定為零。

另外,如果我們忽略句子的實際意思,只關注它們的結構(並對我們剛剛提出的論點做同樣的事情),你會發現它們是一樣的:

  • 如果 x 且不是 y,則z 
  • 不是 z,且 x, 所以y 


所以現在我們知道了,我們感興趣的不是句子的內容,而是它們的邏輯結構,因為這是我們可以推理的。我們可以用這些陳述句描述我們喜歡的任何東西,然後用它們的結構提出合理的論證。

主要思想總結:

  • 為了驗證程式的正確性,我們需要對其行為進行可論證和推理的描述。
  • 自然語言不適合寫這些說明,因為它複雜且含糊不清。
  • 在自然語言中,我們用陳述句來描述世界,並用它們的結構來進行論證。

命題邏輯
正如剛才展示的,我們可以用陳述句來描述世界,並且為了推理這些描述,我們需要處理這些句子的結構。但是,我們還在用自然語言,這意味著還是有一些問題。比如,看看下面三個句子:

  • 我今天沒有做飯,但是冰箱裡還有剩菜。
  • 冰箱裡有剩菜,今天我又沒有做飯。
  • 因為冰箱裡還有剩菜,所以今天我沒有做飯。

它們都傳達相同的資訊。這三個句子之間的唯一區別在於向讀者呈現資訊的方式。在對話環境中,這些細微的差異非常重要,但就我們的目的而言,我們並不關心。對我們來說,所有這些變化只會使語言更難使用。

想象一下,編寫一個程式或解析器來提取這些資訊會有多難,即使是對於簡單的英語句子!所以,我們的語言應該有更簡單的語法,並且只關注資訊的結構。記住上一節中的內容,我們實際上並不需要知道這些“陳述句”究竟在講述什麼。

所以我們將用單個字母來表示無法進一步分解的陳述:命題原子。

它們可以是真,也可以是假。然後,我們將用一組連線詞將這些命題原子連線成更有趣的公式。以下是規則,並附有一些例子:

假設第一個命題原子 p表示“現在是冬天”。假設第二個命題原子 qq表示“天氣很冷”。那麼:

連線詞 ∧ 類似於英語中的“and”。它表示兩邊的陳述或公式都是真的。

  • 比如,p∧q表示“現在是冬天,天氣很冷”。
  • 連線詞本身稱為“連詞”,兩邊的東西稱為“連詞”。
  • 當且僅當兩個連詞都為真時,整個連詞才為真。

類似地,∨類似於英語中的“或”。它表示至少有一個陳述為真。

  • p∨q 表示“現在是冬天或天氣很冷”。
  • 該操作稱為析取,兩側的公式稱為“析取”。
  • 如果至少有一個析取為真,則整個析取為真。

¬ 是所有運算子中唯一一個不真正將兩件事聯絡在一起的運算子 - 它只是反轉一個公式,就像英語中的“not”一樣。

  • 比如,¬p的意思是“現在不是冬天”。
  • 這個操作稱為否定,如果你要否定的東西是假的,那麼它就是真的。

最後一個聯結詞是 →→,稱為“蘊涵”。它就像英語中的“如果……那麼……”,所以順序很重要!比如,p→qp→q 表示“如果是冬天,那麼天氣很冷”,而 q→pq→p 表示“如果天氣很冷,那麼天氣就是冬天”。

  • 箭頭左側的公式稱為“前件antecedent”,箭頭右側的公式稱為“後件consequent”。
  • 只有當前件為真且後件為假時,蘊涵才為假。

關於這些連線詞,還有一些值得一提的事情。
第一個與析取有關:它被引入為類似於英語中的“或”一詞,但有一點不同。

在英語中,當我們說“a 或 b”時,我們通常是指其中一個,而不是兩個。這就是所謂的排他或,因為我們排除了兩個選項為真。然而,析取是包容性的——如果兩個析取項都為真,它仍然為真。

下一個例子是為了強調在處理邏輯蘊涵時順序確實很重要。考慮以下語句

  • 如果我正在做麵包,那麼我就正在烘烤。

這是正確的。但是如果你把前件和後件互換一下,就會得到這樣的結果:

  • 如果我在烘焙,那麼我就是做麵包。

現在這顯然是錯誤的;也許你可以去烤蛋糕!

最後,在邏輯中處理蘊涵時,我們並不關心因果關係。也就是說,當我們說一件事暗示另一件事時,前因和後果之間不必有任何關係。比如,考慮這個句子

  • 如果埃及人建造了金字塔,那麼胡蘿蔔就是橙色的。

在英語中,這毫無意義,因為埃及金字塔與胡蘿蔔的顏色毫無關係。但從邏輯上講,我們並不關心這些關係。

現在,我們有了一種方法,可以將簡單的陳述(命題原子)組合成公式,用邏輯連線詞來描述這個世界更有趣的事情。

主要思想總結:

  • 因為我們只關心邏輯結構而不是具體含義,所以我們用單個字母來表示無法進一步分解的簡單句子(命題原子)。
  • 有一組四個聯結詞,我們可以用來將命題原子組合成邏輯公式。
  • 連線詞有一個應用順序。
  • 這使我們能夠明確地將有關世界的陳述編碼為可以嚴格論證的形式。

更多點選標題見原文。

 

相關文章