2015-09-26 93 views
-1

我試圖實現一個階乘計算(n!)的解決方案,而不使用遞歸,只使用prolog的retroaction。例如:沒有使用遞歸的因子計算

factorial(0, 1). 
factorial(1, 1). 
factorial(2, 2). 

retroaction(X, Y) :- 
factorial(K, Y), 
not(K = X), 
fail. 

retroaction(K, Y) :- factorial(K, Y). 

有了固定的事實階乘,如果我叫謂詞倒行逆施發現的2階乘,例如,我會收到正確的答案:

?- retroaction(2, X). 
X = 2. 

我試圖實現的解決方案是:

factorial_ret(Number, _) :- 
    retractall(factorial(_, _)), 
    assertz(factorial(0,1)), 
    factorial(X, Y), 
    not(X = Number), 
    NewNumber is X + 1, 
    Factorial is Y * NewNumber, 
    retractall(factorial(_, _)), 
    assertz(factorial(NewNumber, Factorial)), 
    fail. 

factorial_ret(Number, Y) :- 
    factorial(Number, Y). 

如果我嘗試獲取數字的階乘大於1,不起作用。有人對此有任何想法嗎?

+0

對,@luker。發佈更新。 – rwehresmann

+2

這有點令人困惑。你想調用哪個謂詞來獲得階乘?它看起來是'factorial/2',但是你的例子使用'retroaction/2',你的主要實現在'factorial_ret/2'中,而不是從其他地方調用。 – lurker

+0

獲得階乘的謂詞是'factorial/2'。 'retroaction/2'只是一個例子來展示階乘非遞歸解決方案的意義。 – rwehresmann

回答

1

正如@lurker所指出的那樣,你將'存儲謂詞'與'定義謂詞'混爲一談。你需要階乘/ 2的定義,所以retractall(factorial(_,_))沒什麼意義。

下面,我用retract(mem(N, F))檢索臨時變量值和最終更新準備DB。應該始終只有一個mem/2的實例。

% replace recursion with a failure driven loop 
:- dynamic mem/2. 
factorial(X, Y) :- 
    assert(mem(0, 1)), 
    repeat, 
    retract(mem(N, F)), 
    ( X == N % done ? 
    -> Y = F, % yes, succeed 
     !  % but be sure to avoid 'external' backtracking... 
    ; N1 is N + 1, 
     F1 is N1 * F, 
     assert(mem(N1, F1)), 
     fail % loop: 'goto repeat' 
    ). 

注意repeat,...,fail之間分支中的所有內建是非backtrackable(當然,除了縮回/ 1)。

請注意也是這樣的代碼就會慢很多遞歸定義的(並且不會在多線程SWI-Prolog的工作)。

我覺得需要處理「知識庫」,而不是作爲全局變量的assert /收回都有了年初早前的程序員。幾個Prolog對全局變量有專門的庫謂詞,因爲它們有合法的用途:例如,見memoization

PS:「追溯」是線性系統穩定性理論中的術語,我從來沒有見過它使用了大約編程

編輯可能是有益的看到如何通過@Repeat已報告的錯誤是可以解決的:只需交換統一和裁員。那麼,我們還需要一個X是一個正整數的測試。真誠的,我認爲這實際上是無關爲手頭的問題。

... 
    ( X == N % done ? 
    -> !,  % yes, be sure to avoid further backtracking 
     Y = F % unify with the computed factorial 
    ; N1 is N + 1, 
... 
+0

我瞭解所提供的解決方案,但對我而言仍然有點困惑。在我將這個問題帶到SO之前,我明白'fail/0'會強制回溯,就像我在帖子中使用'retroaction/2'謂詞所展示的那樣。但是使用assert和retract,這不會發生(在你和@Boris解決方案中,它被用於'repeat/0'來強制)。你明白爲什麼? – rwehresmann

+0

失敗/ 0強制失敗。回溯是失敗的結果,*如果*在測試堆棧上存在選擇點。但是你收回了階乘/ 2,將它們刪除。所有其他謂詞都是確定性的。 – CapelliC

+0

但是我還在'fail/0'前插入了'assertz/1'一個新的'factorial/2'。我認爲這樣一個新的選擇應該出現在證明堆棧上。 – rwehresmann

-1

你已經在你的not條款有一個小寫k

+1

沒錯,@Scott Hunter。我現在改變了。保持功能。 – rwehresmann

+0

你寫了評論,而不是答案。 OP提出的方案在邏輯上有缺陷,'k'或'k'。 – repeat

0

假設的「倒行逆施」你實際上意味着"memoization",一個去了解,這將是這樣:

只要你沒有找到你需要的階乘,找到最大的一個到目前爲止,計算下一個,漂洗和重複。

我在寫這個答案,因爲它比其他正確的answer by @CapelliC少了不必要的代碼。

你只需要一個這樣的出發事實上,0的階乘:

然後,您可以使用repeat環的「只要」做。然後,如果你隨時添加新的階乘到預先計算階乘的堆棧的頂部,你總是期待只有一次,你可以肯定的是你找到的最大的一個,到目前爲止:

:- dynamic factorial/2. 
factorial(0, 1). 

factorial_memoize(N, F) :- 
    repeat, 
     ( factorial(N, F0) 
     -> !, F = F0 
     ; once(factorial(N0, F0)), 
      succ(N0, N1), 
      F1 is N1 * F0, 
      asserta(factorial(N1, F1)), 
      fail 
     ). 

這裏是怎麼程序作品:

?- listing(factorial/2). 
:- dynamic factorial/2. 

factorial(0, 1). 

true. 

?- factorial_memoize(0, F). 
F = 1. 

?- listing(factorial/2). 
:- dynamic factorial/2. 

factorial(0, 1). 

true. 

?- factorial_memoize(5, F). 
F = 120. 

?- listing(factorial/2). 
:- dynamic factorial/2. 

factorial(5, 120). 
factorial(4, 24). 
factorial(3, 6). 
factorial(2, 2). 
factorial(1, 1). 
factorial(0, 1). 

true. 
+0

是一個很好的答案,但並不突出顯示不那麼明顯的難題:收縮和邏輯更新(無論是否爲真)... – CapelliC

+1

@CapelliC我想一點不吭就走了。 – 2015-09-26 19:03:40

+0

@CapelliC另一個觀察:看起來你只是保持一個「運行價值」,而這實現了適當的「memoization」,即它保持了它沿途計算的每個值。當然OP的問題從來沒有說過他們想要記憶。 – 2015-09-26 19:15:51

2

無!我的良心迫使我到un-ask your question

對於你尋求是一個no-no即使在 - 更在Prolog。

在某些情況下肯定會非常有用;我通常認爲它是「最後的武器」,而不是「首選武器」。 IMO使用它的可能後果包括:

例如:請回答by @CapelliCby @Boris。 引用@Boris:

我寫這個答案,因爲它具有比@CapelliC的否則正確答案少一點不必要的代碼。

讓我們運行一些查詢,並將該評估放到測試中!

?- factorialBoris(0,0). 
**LOOPS** 

?- factorialBoris(0,2). 
**LOOPS** 

?- factorialBoris(1,2). 
**LOOPS** 

?- factorialCapelliC(0,0). 
**LOOPS** 

?- factorialCapelliC(0,2). 
**LOOPS** 

нет!

請不要誤會我的意思!我不想放下工作和努力by @CapelliCby @Boris,兩個Prolog topusers on SO,以任何方式,形狀或形式。


底線:

使用風格Prolog的編碼很容易引火燒身的時候!

的使用代碼的嚴格測試比測試logically-pure代碼,甚至是可以更難了不少,相信我!

在不確定的情況下,選擇明智的道路:僅僅做到這一點是正確的:)

保留儘可能鴕鳥政策貿易它的任何回報。

+2

我沒有看到任何努力來實際回答OP的問題.... – CapelliC

+1

記憶不是一個「否」,它有有效的用途。無論哪種方式,這個問題顯然有助於討論一種編程技術,我們不必立即對它進行宗教信仰。如果有人真的認爲這是計算階乘的好方法,他們也可以這樣做。 – 2015-09-26 18:57:22

+0

謝謝您指出我解決方案中的錯誤,它幫助我糾正錯誤。 – 2015-09-26 19:03:04