簡體   English   中英

SWI-Prolog 使用位移位 CLPFD 報告錯誤答案

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM