勇闖新世界︰ 《 pyDatalog 》【專題】之約束編程‧三

窮舉法』是一種通用方法。它以『問題』為『解』之『對錯』的『判準』,窮盡『所有可能』的『解』作『斷言』之求解法。或可同時參閱《 M♪o 之學習筆記本《寅》井井︰【䷝】試釋是事》文中所說之『蠻力法』以及例子︰

行 ︰雖說是蠻力法,實則乃用『窮舉』,數ㄕㄨˇ數ㄕㄨˋ數不盡,耗時難為功,怎曉 機 機心迅捷後,此法遂真可行耶!?實習所用機,登入採『學號』與『針碼』【※ Pin Code 四位數字碼】。『學號』之制 ── 班碼-位碼 ──,班不過十,位少於百,故而極其數不足千。針碼有四位,總其量,只有萬。試而盡之,『千萬』已『窮舉』。問題當在『咸澤碼訊』有多快?『登錄』之法有 多嚴 ?破解程式幾人會?設使一應具足,那個『駭黑』之事,怕是恐難免!!……

此處特別指出計算機『速度提昇』使得『窮舉法』的實用性大增,對此法的了解也就更顯得重要了。

假使將『所有可能解』看成『解空間』,將『問題』當成『約束』條件,此時『問題』的『求解』,就轉換成『尋找』『解空間』中滿足『約束』條件的『解』。一般將之稱為『蠻力搜尋法』︰

Brute-force search

In computer science, brute-force search or exhaustive search, also known as generate and test, is a very general problem-solving technique that consists of systematically enumerating all possible candidates for the solution and checking whether each candidate satisfies the problem’s statement.

A brute-force algorithm to find the divisors of a natural number n would enumerate all integers from 1 to the square root of n, and check whether each of them divides n without remainder. A brute-force approach for the eight queens puzzle would examine all possible arrangements of 8 pieces on the 64-square chessboard, and, for each arrangement, check whether each (queen) piece can attack any other.

While a brute-force search is simple to implement, and will always find a solution if it exists, its cost is proportional to the number of candidate solutions – which in many practical problems tends to grow very quickly as the size of the problem increases. Therefore, brute-force search is typically used when the problem size is limited, or when there are problem-specific heuristics that can be used to reduce the set of candidate solutions to a manageable size. The method is also used when the simplicity of implementation is more important than speed.

This is the case, for example, in critical applications where any errors in the algorithm would have very serious consequences; or when using a computer to prove a mathematical theorem. Brute-force search is also useful as a baseline method when benchmarking other algorithms or metaheuristics. Indeed, brute-force search can be viewed as the simplest metaheuristic. Brute force search should not be confused with backtracking, where large sets of solutions can be discarded without being explicitly enumerated (as in the textbook computer solution to the eight queens problem above). The brute-force method for finding an item in a table — namely, check all entries of the latter, sequentially — is called linear search.

上一篇因數的例子︰

 

pi@raspberrypi ~ python3 Python 3.2.3 (default, Mar  1 2013, 11:53:50)  [GCC 4.6.3] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> from pyDatalog import pyDatalog  >>> pyDatalog.create_terms('X被除數, Y除數, Q商, R餘數, 被除數空間, 除數空間')  >>> 被除數空間(X被除數) <= (X被除數.in_(range(100))) 被除數空間(X被除數) <= _pyD_in(X被除數,'['0', '1', '2', '3',   >>> 被除數空間(X被除數) .data [(47,), (97,), (28,), (94,), (9,), (75,), (6,), (56,), (53,), (34,), (31,), (81,), (12,), (78,), (59,), (40,), (37,), (18,), (84,), (15,), (65,), (62,), (43,), (24,), (90,), (21,), (87,), (2,), (68,), (49,), (46,), (96,), (27,), (93,), (8,), (74,), (5,), (71,), (52,), (33,), (99,), (30,), (80,), (11,), (77,), (58,), (55,), (36,), (17,), (83,), (14,), (64,), (61,), (42,), (39,), (89,), (20,), (86,), (1,), (67,), (48,), (45,), (26,), (92,), (23,), (73,), (4,), (70,), (51,), (32,), (98,), (29,), (95,), (10,), (76,), (7,), (57,), (54,), (35,), (16,), (82,), (13,), (79,), (60,), (41,), (38,), (88,), (19,), (85,), (0,), (66,), (63,), (44,), (25,), (91,), (22,), (72,), (3,), (69,), (50,)]  >>> 除數空間(X被除數, Y除數) <= 被除數空間(X被除數) & (Y除數.in_(range(1, 100))) & (Y除數 <= X被除數) 除數空間(X被除數,Y除數) <= 被除數空間(X被除數)&_pyD_in(Y除數,'['1', '  >>> 除數空間(20, Y除數) [(13,), (3,), (12,), (2,), (15,), (5,), (14,), (4,), (17,), (7,), (16,), (6,), (19,), (9,), (18,), (8,), (11,), (20,), (1,), (10,)]  >>> len(除數空間(20, Y除數).data) 20  >>> pyDatalog.create_terms('除法等式')  >>> def 餘數(a, b): ...     return a % b ...   >>> pyDatalog.create_terms('餘數')  >>> 除法等式(X被除數,Y除數, Q商, R餘數) <= 除數空間(X被除數, Y除數) & (Q商 == (X被除數 // Y除數)) & (R餘數 == 餘數(X被除數,Y除數)) 除法等式(X被除數,Y除數,Q商,R餘數) <= 除數空間(X被除數,Y除數)&==(Q商,(X被除  >>> 除法等式(20,Y除數, Q商, R餘數) [(18, 1, 2), (14, 1, 6), (7, 2, 6), (8, 2, 4), (16, 1, 4), (15, 1, 5), (2, 10, 0), (9, 2, 2), (1, 20, 0), (12, 1, 8), (19, 1, 1), (4, 5, 0), (13, 1, 7), (17, 1, 3), (5, 4, 0), (3, 6, 2), (6, 3, 2), (20, 1, 0), (11, 1, 9), (10, 2, 0)]  >>> 除法等式(20,Y除數, Q商, 0) [(4, 5), (1, 20), (5, 4), (2, 10), (10, 2), (20, 1)] >>>  >>> pyDatalog.create_terms('因數, F因子')  >>> 因數(X被除數, F因子) <= 除法等式(X被除數, F因子, Q商, 0) 因數(X被除數,F因子) <= 除法等式(X被除數,F因子,Q商,'0')  >>> 因數(20, F因子) [(10,), (5,), (4,), (1,), (20,), (2,)]  >>> 因數(X被除數, F因子) <= 除法等式(X被除數, Y除數, F因子, 0) 因數(X被除數,F因子) <= 除法等式(X被除數,Y除數,F因子,'0')  >>> 因數(20, F因子) [(20,), (1,), (4,), (5,), (10,), (2,)] >>>   >>> pyDatalog.create_terms('因式分解除法')  >>> import math >>> pyDatalog.create_terms('math')  >>> 因式分解除法(X被除數,Y除數, Q商, R餘數) <= 除數空間(X被除數, Y除數) & (Y除數 <= (math.sqrt(X被除數) + 1)) & (Q商 == (X被除數 // Y除數)) & (R餘數 == 餘數(X 被除數,Y除數)) 因式分解除法(X被除數,Y除數,Q商,R餘數) <= 除數空間(X被除數,Y除數)&<=(Y除數,(  >>> 因式分解除法(20,Y除數, Q商, R餘數) [(1, 20, 0), (4, 5, 0), (3, 6, 2), (5, 4, 0), (2, 10, 0)]  >>> 因式分解除法(20,Y除數, Q商, 0) [(5, 4), (4, 5), (1, 20), (2, 10)]  >>> pyDatalog.create_terms('因數一')  >>> 因數一(X被除數, F因子) <= 因式分解除法(X被除數, F因子, Q商, 0) 因數一(X被除數,F因子) <= 因式分解除法(X被除數,F因子,Q商,'0')  >>> 因數一(20, F因子) [(4,), (1,), (2,), (5,)]  >>> 因數一(X被除數, F因子) <= 因式分解除法(X被除數, Y除數, F因子, 0) 因數一(X被除數,F因子) <= 因式分解除法(X被除數,Y除數,F因子,'0')  >>> 因數一(20, F因子) [(4,), (5,), (10,), (2,), (20,), (1,)] >>>  </pre>    其實用的就是此『 generate and test 』生成‧驗證之法。由於此法『邏輯』單純,用『邏輯編程』容易,最要緊的事情,反倒是如何從『問題分析』裡,得到『小的解空間』、形成『適當約束條件』以及『正確』使用 pyDatalog 的『語法』來作表達的了。  就讓我們從一個『語詞方程式』  <span style="color: #ff9900;">I * AM = SAM</span>  <span style="color: #808080;">【※ I、A、S、M 都是十進制數字,不同的字母所代表之數字不同 。I 是一位數、AM 是兩位數、SAM 是三位數,一般領頭數字不為零。『*』是乘法。】</span>  展開討論。如果沒有其它的條件,我們可以用典型『蠻力法』述詞來求解︰<span style="color: #808080;">【※ 注意!變元._in 和 變元._in 意義相同嗎?】</span>  <span style="color: #ff9900;">sol(I,A,M,S) <=</span>  <span style="color: #ff9900;">(I._in(range(10))) & (A._in(range(10))) &</span> <span style="color: #ff9900;"> (M._in(range(10))) &(S._in(range(10))) & </span>  <span style="color: #ff9900;">(I != 0) & (A !=0) & (S !=0) &</span> <span style="color: #ff9900;"> (I != A) & (I != M) & (I != S) &</span> <span style="color: #ff9900;"> (A != M) & (A !=S) &</span> <span style="color: #ff9900;"> (M !=S) & </span>  <span style="color: #ff9900;">(I == ((100*S + 10*A + M) / (10*A +M)))</span>  【程式如下】 <pre class="lang:sh decode:true    ">pi@raspberrypi ~ python3
Python 3.2.3 (default, Mar  1 2013, 11:53:50) 
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from pyDatalog import pyDatalog

>>> pyDatalog.create_terms('I,A,M,S,d,sol')

>>> sol(I,A,M,S) <= (I._in(range(10))) & (A._in(range(10))) & (M._in(range(10))) &(S._in(range(10))) &(I != 0) & (A !=0) & (I != A) & (I != M) & (I != S) & (A != M) & (A !=S) & (M !=S) & (I == ((100*S + 10*A + M) / (10*A +M)))
sol(I,A,M,S) <= _pyD_in(I,'['0', '1', '2', '3', '4

>>> print(sol(I,A,M,S))
I | A | M | S
--|---|---|--
7 | 5 | 0 | 3
3 | 5 | 0 | 1
9 | 5 | 0 | 4
6 | 4 | 0 | 2
9 | 7 | 5 | 6
6 | 2 | 0 | 1
6 | 8 | 0 | 4
>>>

 

那麼我們能不能更深入『了解問題』,然後將程式改寫的更『好』的呢?當然『好』可以有多種標準,對於『計算機科學』來講,『□□最佳化』是最主要的依歸。有興趣的讀者可以閱讀

Approximating Constraint Propagation in Datalog

We present a technique exploiting Datalog with aggregates to improve the performance of programs with arithmetic (in)equalities. Our approach employs a source-to-source program transformation which approximates the propagation technique from Constraint Programming. The experimental evaluation of the approach shows good run time speed-ups on a range of non-recursive as well as recursive programs. Furthermore, our technique improves upon the previously reported in the literature constraint magic set transformation approach.

之論文,得到一些啟發。