[英]SWI-Prolog reporting wrong answer with bitshifts CLPFD
我在一個更大的代碼庫中遇到了這個問題,但將其縮減為一個最小的可重現示例。 這是匯編程序的一些代碼:
:- use_module(library(clpfd)).
bigconst(X) :- X #=< 0x3FF, X #>= 0.
asm(instruction('ADD', [A]), R) :-
bigconst(A),
R #= 0b0000 + (A << 4).
asm(instruction('SUB', [A]), R) :-
bigconst(A),
R #= 0b0001 + (A << 4).
組裝時似乎可以工作:
?- asm(instruction('SUB', [5]), R).
R = 81.
但是在拆卸時似乎失敗了:
?- asm(I, 81).
I = instruction('ADD', [_42146]),
_42146 in 0..1023,
81#=_42146<<4 .
這是我的程序中的錯誤還是 Prolog 中的錯誤? 我將如何解決這個問題?
當我找到答案時,它是LOL。 我使用了許多奇怪的模式來解決問題,但這是我以前從未使用過的模式。 一旦我看到它工作,我就知道我有一個閃亮的工具箱新工具。
對於 CLP(FD) 問題,它們通常可以雙向工作,這正是您想要的。 您遇到的第一個問題是您有bigconst(A)
,它的作用類似於保護語句。 所以把它扔掉。
然后下一件事是R #= 0b0000 + (A << 4)
按預期工作,但遇到了問題,兩種方式都不能按預期工作,
?- X #= 4 << 4.
X = 64.
?- 64 #= X << 4.
64#=X<<4.
同樣反過來
B #= A >> 4.
也按預期工作,也遇到同樣的問題。
?- X #= 64 >> 4.
X = 4.
?- 4 #= X >> 4.
4#=X>>4.
所以我嘗試使用in/2添加一些約束,但沒有奏效,然后意識到我已經擁有了所有需要的約束並且它們起作用了。
asm(instruction('ADD', [A]), R) :-
R #= 0b0000 + (A << 4),
A #= (R - 0b0000) >> 4.
示例用法
?- asm(instruction('ADD', [5]), R).
R = 80.
?- asm(I,80).
I = instruction('ADD', [5]).
表明這不是一個一擊的奇跡。
asm(instruction('SUB', [A]), R) :-
R #= 0b0001 + (A << 4),
A #= (R - 0b0001) >> 4.
示例用法
?- asm(instruction('SUB', [5]), R).
R = 81.
?- asm(I,81).
I = instruction('SUB', [5]).
這是我的程序中的錯誤還是 Prolog 中的錯誤? 我將如何解決這個問題?
這是您正在使用的頂層的可用性錯誤。 如果你看一下非常密切的,你可能會發現一個小提示:
81#=_42146<<4 .
^ SPACE
這個微小的空間意味着:請注意,答案不止於此。 事實上,有兩個答案。 第一個答案(來自'ADD'
)是假的,它不包含任何解決方案。 只有第二個答案包含一個解決方案。
請注意,Prolog 主要生成答案而不是解決方案。 這就是我們談論答案替換的原因。 答案和解決方案之間的關系是微妙的。 一個答案可能包含零到無窮多個解。
那么為什么 Prolog 不直接產生解決方案呢?
首先,對於無限的解決方案,這將是非常無效的。 想想length(L,3)
,它有一個單一的答案,其中包含所有長度為 3 的列表,並且只包含可以想象的任何元素,因此有無限多個解決方案。 比如L = [1,2,3]
或L = [bof, toto, machin-
maschin
]
或L = [louis_XVI, bernie, bernie]
等等。 在邏輯變量的幫助下,我們可以將無限的三元素列表折疊成以下答案: L = [_A,_B,_C]
。 這就是邏輯變量的威力!
這種權力也用於約束。 但是有了這種權力,就會有很多責任和負擔。 畢竟,許多問題在一個方向上很容易計算,而反過來卻很難。 你已經發現了這樣的問題。 在這種情況下,可以考慮改進clpfd
以輕松處理這種情況,但在一般情況下是不可判定的。 因此,有時根本沒有算法。 在這種情況下,給出一個虛假的答案(不一致、Scheinlösung、解決方案布蘭奇)是系統可以做的最好的事情。 或者,它可能會產生錯誤並中止整個計算。 但這太糟糕了。 通常,我們可以忍受這種不一致。
以您的情況為例,如果您真的想確保存在解決方案,請通過標記它們來消除答案中的約束!
?- asm(I,81), I = instruction(_,[R]).
I = instruction('ADD', [R]),
R in 0..1023,
81#=R<<4
; I = instruction('SUB', [R]),
R in 0..1023,
80#=R<<4.
?- asm(I,81), I = instruction(_,[R]), labeling([],[R]).
I = instruction('SUB', [5]),
R = 5
; false.
另一種方法是使約束更強大,正如@Guy-Coder 所示。 這可能有效(然后很好),但理解起來更復雜。 在某些情況下也可能效率較低。 這是一個真正的工程問題。 在某些情況下,我們什么時候接受不一致作為更快解決方案的代價?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.