作为Prolog的新手,我正在寻找一种计算列表中反转次数的好方法。
我知道如何使用flatten(Matrix, FlatMatrix)
展平矩阵,从而创建一个包含矩阵中单个元素集的变量。但是,我不确定如何查找该列表中的反转次数。
根据我的理解,从0 ... n开始的数字矩阵中的反转次数是小于被比较数字的元素总数(如果我错了,请纠正我)
我对Prolog中的setof/3
如何运作有一点了解,但我很想知道一种更有效的方法来解决平坦矩阵中的反转次数。 Prolog中的变量对我来说很奇怪,所以简单的解释是最好的。
提前谢谢!
答案 0 :(得分:2)
首先,我并没有完全理解你所谓的“反转”,所以我会坚持准规范的解释 @CapelliC使用in his answer来解决这个问题。
我们假设所有列表项都是整数,因此我们可以使用clpfd。
:- use_module(library(clpfd)).
z_z_order(X,Y,Op) :-
zcompare(Op,X,Y).
要计算反转次数(上下方向变化),我们执行以下四个步骤:
比较相邻的项目(使用mapadj/3
,如本答案最后所定义的那样)
?- Zs = [1,2,4,3,2,3,3,4,5,6,7,6,6,6,5,8], mapadj(z_z_order,Zs,Cs0).
Zs = [1,2,4,3,2,3,3,4,5,6,7,6,6,6,5,8],
Cs0 = [ <,<,>,>,<,=,<,<,<,<,>,=,=,>,< ].
消除=
中Cs0
的所有出现(使用
tfilter/3
和dif/3
)
?- Cs0 = [<,<,>,>,<,=,<,<,<,<,>,=,=,>,<,<], tfilter(dif(=),Cs0,Cs1).
Cs0 = [<,<,>,>,<,=,<,<,<,<,>,=,=,>,<,<],
Cs1 = [<,<,>,>,<, <,<,<,<,>, >,<,<].
在Cs1
中运行相同的项目(使用
splitlistIfAdj/3
和dif/3
)
?- Cs1 = [<,<,>,>,<,<,<,<,<,>,>,<,<], splitlistIfAdj(dif,Cs1,Cs).
Cs1 = [ <,< , >,> , <,<,<,<,< , >,> , <,< ],
Cs = [[<,<],[>,>],[<,<,<,<,<],[>,>],[<,<]].
反转次数比运行次数少一次(使用
length/2
和(#=)/2
)
?- Cs = [[<,<],[>,>],[<,<,<,<,<],[>,>],[<,<]], length(Cs,L), N #= max(0,L-1).
Cs = [[<,<],[>,>],[<,<,<,<,<],[>,>],[<,<]], L = 5, N = 4.
就是这样。让我们把它们放在一起!
zs_invcount(Zs,N) :-
mapadj(z_z_order,Zs,Cs0),
tfilter(dif(=),Cs0,Cs1),
splitlistIfAdj(dif,Cs1,Cs),
length(Cs,L),
N #= max(0,L-1).
样本使用:
?- zs_invcount([1,2,3],0),
zs_invcount([1,2,3,2],1),
zs_invcount([1,2,3,3,2],1), % works with duplicate items, too
zs_invcount([1,2,3,3,2,1,1,1],1),
zs_invcount([1,2,3,3,2,1,1,1,4,6],2),
zs_invcount([1,2,3,3,2,1,1,1,4,6,9,1],3),
zs_invcount([1,2,3,3,2,1,1,1,4,6,9,1,1],3).
true.
meta-predicate mapadj/3
:- meta_predicate mapadj(3,?,?), list_prev_mapadj_list(?,?,3,?).
mapadj(P_3,[A|As],Bs) :-
list_prev_mapadj_list(As,A,P_3,Bs).
list_prev_mapadj_list([] ,_ , _ ,[]).
list_prev_mapadj_list([A1|As],A0,P_3,[B|Bs]) :-
call(P_3,A0,A1,B),
list_prev_mapadj_list(As,A1,P_3,Bs).
答案 1 :(得分:1)
这是my previous answer的替代方案。它基于clpfd和meta-predicate mapadj/3
:
:- use_module(library(clpfd)).
使用meta-predicate tfilter/3
,bool01_t/2
和clpfd sum/3
我们定义:
z_z_momsign(Z0,Z1,X) :-
X #= max(-1,min(1,Z1-Z0)).
z_z_absmomsign(Z0,Z1,X) :-
X #= min(1,abs(Z1-Z0)).
#\=(X,Y,Truth) :-
X #\= Y #<==> B,
bool01_t(B,Truth).
最后,我们像这样定义zs_invcount/2
:
zs_invcount(Zs,N) :-
mapadj(z_z_momsign,Zs,Ms0),
tfilter(#\=(0),Ms0,Ms),
mapadj(z_z_absmomsign,Ms,Ds),
sum(Ds,#=,N).
样品使用:
?- zs_invcount([1,2,3],0),
zs_invcount([1,2,3,2],1),
zs_invcount([1,2,3,3,2],1), % works with duplicate items, too
zs_invcount([1,2,3,3,2,1,1,1],1),
zs_invcount([1,2,3,3,2,1,1,1,4,6],2),
zs_invcount([1,2,3,3,2,1,1,1,4,6,9,1],3),
zs_invcount([1,2,3,3,2,1,1,1,4,6,9,1,1],3).
true.
更详细地考虑执行以下示例查询:
?- zs_invcount([1,2,4,3,2,3,3,4,5,6,7,6,6,6,5,8],N).
让我们一步一步地进行!
对于所有相邻的列表项,计算其“动量”的符号:
?- Zs = [1,2,4,3,2,3,3,4,5,6,7,6,6,6,5,8], mapadj(z_z_momsign,Zs,Ms0). Zs = [1,2, 4,3, 2,3,3,4,5,6,7, 6,6,6, 5,8], Ms0 = [ 1,1,-1,-1,1,0,1,1,1,1,-1,0,0,-1,1 ].
消除0
的所有符号值:
?- Ms0 = [1,1,-1,-1,1,0,1,1,1,1,-1,0,0,-1,1], tfilter(#\=(0),Ms0,Ms). Ms0 = [1,1,-1,-1,1,0,1,1,1,1,-1,0,0,-1,1], Ms = [1,1,-1,-1,1, 1,1,1,1,-1, -1,1].
获得“动量反转”,即动量冲动的绝对迹象。
?- Ms = [1,1,-1,-1,1,1,1,1,1,-1,-1,1], mapadj(z_z_absmomsign,Ms,Ds). Ms = [1,1,-1,-1,1,1,1,1,1,-1,-1,1], Ds = [ 0,1, 0, 1,0,0,0,0,1, 0, 1 ].
最后,使用sum/3
总结“动量反转”的数量:
?- Ds = [0,1,0,1,0,0,0,0,1,0,1], sum(Ds,#=,N). N = 4, Ds = [0,1,0,1,0,0,0,0,1,0,1].
或者,或者,所有步骤:
:- Zs = [1,2,4, 3, 2,3,3,4,5,6,7, 6,6,6, 5,8], mapadj(z_z_momsign,Zs,Ms0), Ms0 = [ 1,1,-1,-1,1,0,1,1,1,1,-1,0,0,-1,1 ], tfilter(#\=(0),Ms0,Ms), Ms = [ 1,1,-1,-1,1, 1,1,1,1,-1, -1,1 ], mapadj(z_z_absmomsign,Ms,Ds), Ds = [ 0,1, 0, 1, 0, 0,0,0,1, 0, 1 ], sum(Ds,#=,N), N = 4.
答案 2 :(得分:0)
一个可能的定义,试图让它尽可能简单:
count_inversions(L, N) :-
direction(L, D, L1),
count_inversions(L1, D, 0, N).
direction([A,B|L], D, [B|L]) :-
A > B -> D = down ; D = up.
count_inversions([_], _, N, N).
count_inversions(L, D, M, N) :-
direction(L, D, L1),
!, count_inversions(L1, D, M, N).
count_inversions(L, _, M, N) :-
direction(L, D1, L1),
M1 is M+1, count_inversions(L1, D1, M1, N).
方向/ 3谓词比较一对元素,确定它们是否按升序/降序排列。这些信息传递下来访问列表,如果它不能匹配,则计数器递增(累加器,从0开始)。当访问停止时(列表只有1个元素,则无法确定方向),累积的计数器将被传递出去&#39;在顶级电话会议上退回。
我选择了剪辑,而不是&#39; if / then / else&#39;构造,所以你可以尝试使用它自己重写count_inversions / 4(你可以看到它在方向/ 3中使用)。谨防运营商的优先权!
注意:当A =:= B时,方向/ 3忽略了固有的模糊性,并且指定了&#39; up&#39;对于这种情况。
HTH