暴力法、窮舉搜尋與回溯

生成並測試(generate-and-test)

想像你在拼拼圖:一次次拿起一片、卡進去、然後問「這片合不合?」合就繼續,不合就拿回來換一片。你在「猜一下」和「檢查一下」之間來回。這個兩步驟的迴圈——生成一個候選,再測試它——正是幾乎所有暴力法內部的引擎。

正式地說,生成並測試把兩件職責分開。生成器是一個程序,負責產出候選解,理想上每個恰好產出一次、最終全部產出:下一組鎖碼、下一個子集合、下一種座位安排。測試器是一個述詞,接收一個候選並回答它是否滿足問題的要求。整體演算法就是一個迴圈:只要生成器還有候選,就取下一個來測試;回報通過的候選(或在第一個通過時就停)。把兩半分開是實在的設計好處——你可以換上更聰明的生成器(產出較少的垃圾候選)而不必動到測試,反之亦然。

成本基本上是(生成的候選數)乘上(一次測試的成本)。純粹的生成並測試讓生成器是盲目的:它產出候選時完全不顧測試,所以可能吐出大量顯然注定失敗的候選。最重要的單一改進,就是把測試器的知識反饋回生成器——拒絕生成那些已知會失敗的候選。當這個反饋發生在「候選還只建到一半」的時候,生成並測試就變成了回溯法,省下的功夫可以非常驚人。

要找出 a, b, c <= 20 的畢氏三元組:生成這個範圍內每一個三元組 (a, b, c),測試是否 a^2 + b^2 = c^2。生成器盲目地產出 20^3 = 8000 個三元組;每個測試是一次比較。一個只讓 a <= b、並只用 a^2 + b^2 去算 c 的更聰明生成器,做的功就少得多——這正是測試的知識進入了生成器。

生成並測試是兩個乾淨半邊的迴圈;速度來自於教會生成器「測試器知道的事」。

正確的生成器必須是完備的(最終能產出每一個合法候選)——若它會跳過某個真正的解,即使每次測試本身都沒問題,整個方法也會變得不可靠。

又称
guess and checkproduce-and-verify生成測試法