List DCG与Prolog中的列表求逆
我想数一数列表中的倒数。谓词List DCG与Prolog中的列表求逆,list,prolog,clpfd,List,Prolog,Clpfd,我想数一数列表中的倒数。谓词倒装(+L,-N)将N与该列表中的倒装数统一起来。反转定义为X>Y,并且X出现在列表中Y之前(除非X或Y为0)。例如: ?- inversions([1,2,3,4,0,5,6,7,8],N). N = 0. ?- inversions([1,2,3,0,4,6,8,5,7],N). N = 3. 对于我使用它的目的,列表总是有9个元素,并且总是唯一地包含数字0-8 我对Prolog还很陌生,我正努力做到尽可能简洁和优雅;看来这可能会有很大帮助。我阅读了官方定义和
倒装(+L,-N)
将N
与该列表中的倒装数统一起来。反转定义为X>Y
,并且X
出现在列表中Y
之前(除非X
或Y
为0
)。例如:
?- inversions([1,2,3,4,0,5,6,7,8],N).
N = 0.
?- inversions([1,2,3,0,4,6,8,5,7],N).
N = 3.
对于我使用它的目的,列表总是有9个元素,并且总是唯一地包含数字0-8
我对Prolog还很陌生,我正努力做到尽可能简洁和优雅;看来这可能会有很大帮助。我阅读了官方定义和一些教程网站,但仍然不明白它是什么。如有任何帮助,我们将不胜感激。在SWI Prolog中,使用库和:
我不确定DCG在这里是否有用。虽然我们正在处理一个序列,但在查看每个元素时,在每个点上都会对整个列表进行大量检查 这里有一个CLPFD方法,它实现了反演的“朴素”算法,因此它是透明和简单的,但没有它可能的那么有效(它是O(n^2))。有一个更有效的算法(O(n logn))涉及一种方法,我将在下面进一步展示
:- use_module(library(clpfd)).
inversions(L, C) :-
L ins 0..9,
all_distinct(L),
count_inv(L, C).
% Count inversions
count_inv([], 0).
count_inv([X|T], C) :-
count_inv(X, T, C1), % Count inversions for current element
C #= C1 + C2, % Add inversion count for the rest of the list
count_inv(T, C2). % Count inversions for the rest of the list
count_inv(_, [], 0).
count_inv(X, [Y|T], C) :-
( X #> Y, X #> 0, Y #> 0
-> C #= C1 + 1, % Valid inversion, count it
count_inv(X, T, C1)
; count_inv(X, T, C)
).
?- inversions([1,2,3,4,0,5,6,7,8],N).
N = 0 ;
false.
?- inversions([1,2,3,0,4,6,8,5,7],N).
N = 3 ;
false.
?- inversions([0,2,X],1).
X = 1 ;
false.
正如你所见,它确实留下了一个选择点,我还没有理清。
下面是O(n logn)解决方案,它使用排序/合并算法
inversion([], [], 0).
inversion([X], [X], 0).
inversion([HU1, HU2|U], [HS1, HS2|S], C) :- % Ensure list args have at least 2 elements
split([HU1, HU2|U], L, R),
inversion(L, SL, C1),
inversion(R, SR, C2),
merge(SL, SR, [HS1, HS2|S], C3),
C #= C1 + C2 + C3.
% Split list into left and right halves
split(List, Left, Right) :-
split(List, List, Left, Right).
split(Es, [], [], Es).
split(Es, [_], [], Es).
split([E|Es], [_,_|T], [E|Ls], Right) :-
split(Es, T, Ls, Right).
% merge( LS, RS, M )
merge([], RS, RS, 0).
merge(LS, [], LS, 0).
merge([L|LS], [R|RS], [L|T], C) :-
L #=< R,
merge(LS, [R|RS], T, C).
merge([L|LS], [R|RS], [R|T], C) :-
L #> R, R #> 0 #<==> D, C #= C1+D,
merge([L|LS], RS, T, C1).
inversion([]、[]、0)。
反转([X],[X],0)。
反转([HU1,HU2 | U],[HS1,HS2 | S],C):-%确保列表参数至少有2个元素
分裂([HU1,HU2 | U],L,R),
反转(L,SL,C1),
反转(R,SR,C2),
合并(SL、SR、[HS1、HS2|S]、C3),
C#=C1+C2+C3。
%将列表分成左右两半
拆分(列表,左,右):-
拆分(列表、列表、左、右)。
拆分(Es、[]、[]、Es)。
拆分(Es,[[u],],Es)。
拆分([E|Es]、[E|T]、[E|Ls],右):-
拆分(Es、T、Ls、右侧)。
%合并(LS、RS、M)
合并([],RS,RS,0)。
合并(LS,[],LS,0)。
合并([L|LS]、[R|RS]、[L|T]、C):-
L#=R,R#>0#D,C#=C1+D,
合并([L | LS],RS,T,C1)。
您可以忽略第二个参数,它是排序列表(如果您只想得到反转计数,那么这只是一个副作用)。使用clpfd et automaton/8我们可以编写
:- use_module(library(clpfd)).
inversions(Vs, N) :-
Vs ins 0..sup,
variables_signature(Vs, Sigs),
automaton(Sigs, _, Sigs,
[source(s),sink(i),sink(s)],
[arc(s,0,s), arc(s,1,s,[C+1]), arc(s,1,i,[C+1]),
arc(i,0,i)],
[C], [0], [N]),
labeling([ff],Vs).
variables_signature([], []).
variables_signature([V|Vs], Sigs) :-
variables_signature_(Vs, V, Sigs1),
variables_signature(Vs, Sigs2),
append(Sigs1, Sigs2, Sigs).
variables_signature_([], _, []).
variables_signature_([0|Vs], Prev, Sigs) :-
variables_signature_(Vs,Prev,Sigs).
variables_signature_([V|Vs], Prev, [S|Sigs]) :-
V #\= 0,
% Prev #=< V #<==> S #= 0,
% modified after **false** remark
Prev #> V #<==> S,
variables_signature_(Vs,Prev,Sigs).
此类特定于应用程序的约束通常可以使用具体化的约束(其真值反映为0/1变量的约束)构建。这导致了一个相对自然的公式,其中B是1,前提是要计算的条件满足:
:- lib(ic).
inversions(Xs, N) :-
( fromto(Xs, [X|Ys], Ys, [_]), foreach(NX,NXs) do
( foreach(Y,Ys), param(X), foreach(B,Bs) do
B #= (X#\=0 and Y#\=0 and X#>Y)
),
NX #= sum(Bs) % number of Ys that are smaller than X
),
N #= sum(NXs).
此代码适用于。这里有另一个解决方案,它不会使用
来保留选择点,如果\u3
:
inversions([],0).
inversions([H|T], N):-
if_( H = 0,
inversions(T,N),
( find_inv(T,H,N1),inversions(T, N2), N #= N1+N2 )
).
find_inv([],_,0).
find_inv([H1|T],H,N1):-
if_( H1=0,
find_inv(T,H,N1),
if_( H#>H1,
(find_inv(T,H,N2),N1 #= N2+1),
find_inv(T,H,N1)
)
).
#>(X, Y, T) :-
( integer(X),
integer(Y)
-> ( X > Y
-> T = true
; T = false
)
; X #> Y,
T = true
; X #=< Y,
T = false
).
反转([],0)。
反转([H | T],N):-
如果_uh=0,
反转(T,N),
(求逆(T,H,N1),逆(T,N2),N#=N1+N2)
).
查找\u inv([],\u0)。
查找_inv([H1 | T],H,N1):-
如果_u1(H1=0,
查找_inv(T,H,N1),
如果uh#>H1,
(求逆(T,H,N2),N1#=N2+1),
查找库存(T、H、N1)
)
).
#>(X,Y,T):-
(整数(X),
整数(Y)
->(X>Y)
->T=真
;T=假
)
;X#>Y,
T=真
;X#=
这里是定义关系的另一种可能性。首先,#…和X
出现在列表中的Y
之前。。。这是紧接着,还是在之后的任何时候?这确实是一个很好的clpfd示例。我会悬赏一个clpfd-solution@lurker,在反演([0,2,1],1]之前的任何点。
成功推广反演([0,2,X],1)。
失败。为什么不修复程序?用=\=
替换\=
或将X>Y
放在第一位是如此简单@谢谢,我会的。。。我曾短暂尝试过使用clpfd:element/3,但它不起作用。聚合和clpfd不会聚集在一起:反转([0,1,2],N)。
现在失败了??另外,请使用单独的解决方案使用单独的答案。反转(Xs,1)。
失败,但Xs=[2,1,0],反转(Xs,1)。
使用6.2开发成功21。有更好的版本吗?看起来像许多开放式CP。还有((L#>0,R#>0)->
…看起来非常可疑!@false确实,它显示了CPs。这是一种幼稚的方法。我确实更新了->
表达式,因为它有点过度。@false-good建议。我不太熟悉/2
操作符。@false:tu es français?Rendons gr–ceáAlain Colmerauer!C'est seulment main que j'ai注意!哎哟!我真是太棒了,我的朋友们:我的朋友们,我的朋友们,我的朋友们,我的朋友们,我的朋友们,我的朋友们,我的朋友们,我的朋友们,我的朋友们,我的朋友们。
:- use_module(library(clpfd)).
inversions(Vs, N) :-
Vs ins 0..sup,
variables_signature(Vs, Sigs),
automaton(Sigs, _, Sigs,
[source(s),sink(i),sink(s)],
[arc(s,0,s), arc(s,1,s,[C+1]), arc(s,1,i,[C+1]),
arc(i,0,i)],
[C], [0], [N]),
labeling([ff],Vs).
variables_signature([], []).
variables_signature([V|Vs], Sigs) :-
variables_signature_(Vs, V, Sigs1),
variables_signature(Vs, Sigs2),
append(Sigs1, Sigs2, Sigs).
variables_signature_([], _, []).
variables_signature_([0|Vs], Prev, Sigs) :-
variables_signature_(Vs,Prev,Sigs).
variables_signature_([V|Vs], Prev, [S|Sigs]) :-
V #\= 0,
% Prev #=< V #<==> S #= 0,
% modified after **false** remark
Prev #> V #<==> S,
variables_signature_(Vs,Prev,Sigs).
?- inversions([1,2,3,0,4,6,8,5,7],N).
N = 3 ;
false.
?- inversions([1,2,3,0,4,5,6,7,8],N).
N = 0 ;
false.
?- inversions([0,2,X],1).
X = 1.
:- lib(ic).
inversions(Xs, N) :-
( fromto(Xs, [X|Ys], Ys, [_]), foreach(NX,NXs) do
( foreach(Y,Ys), param(X), foreach(B,Bs) do
B #= (X#\=0 and Y#\=0 and X#>Y)
),
NX #= sum(Bs) % number of Ys that are smaller than X
),
N #= sum(NXs).
inversions([],0).
inversions([H|T], N):-
if_( H = 0,
inversions(T,N),
( find_inv(T,H,N1),inversions(T, N2), N #= N1+N2 )
).
find_inv([],_,0).
find_inv([H1|T],H,N1):-
if_( H1=0,
find_inv(T,H,N1),
if_( H#>H1,
(find_inv(T,H,N2),N1 #= N2+1),
find_inv(T,H,N1)
)
).
#>(X, Y, T) :-
( integer(X),
integer(Y)
-> ( X > Y
-> T = true
; T = false
)
; X #> Y,
T = true
; X #=< Y,
T = false
).
:- use_module(library(clpfd)).
bool_t(1,true).
bool_t(0,false).
#<(X,Y,Truth) :- X #< Y #<==> B, bool_t(B,Truth).
#\=(X,Y,Truth) :- X #\= Y #<==> B, bool_t(B,Truth).
inv_t(X,Y,T) :-
if_(((Y#<X,Y#\=0),X#\=0),T=true,T=false).
list_inversions(L,I) :-
list_inversions_(L,I,0).
list_inversions_([],I,I).
list_inversions_([X|Xs],I,Acc0) :-
list_x_invs_(Xs,X,I0,0),
Acc1 #= Acc0+I0,
list_inversions_(Xs,I,Acc1).
list_x_invs_([],_X,I,I).
list_x_invs_([Y|Ys],X,I,Acc0) :-
if_(inv_t(X,Y),Acc1#=Acc0+1,Acc1#=Acc0),
list_x_invs_(Ys,X,I,Acc1).
?- list_inversions([1,2,3,4,0,5,6,7,8],N).
N = 0.
?- list_inversions([1,2,3,0,4,6,8,5,7],N).
N = 3.