Der Abstand zwischen einer langen Sequenz und einer kurzen Sequenz ist der minimale Abstand zwischen der kurzen Sequenz und einer beliebigen Untersequenz der langen Sequenz, die dieselbe Länge wie die kurze Sequenz hat.Dcg-Status Implementierung des Algorithmus
Die Entfernung, die ich benutze, ist die Manhattan Entfernung. (Aber das sollte unwichtig sein, da ich die Entfernungsfunktionen gerne ändern würde).
Diese erste Version zeigt eine naive Implementierung ohne frühen Verzicht. Ich erzeuge alle Untersequenzen derselben Länge, mappe diese, um den Abstand zwischen ihnen und der kurzen Sequenz zu finden, und verwende dann aggregate/3, um das min zu finden.
abs(X,Y,Z):-
Z is abs(X-Y).
seq_seq_absdis(Seq1,Seq2,Dis):-
same_length(Seq1,Seq2),
maplist(abs,Seq1,Seq2,Dislist),
sumlist(Dislist,Dis).
seq_subseq(List1,List2):-
append(List2,_,List1),
dif(List2,[]).
seq_subseq([_|T],Subseq):-
seq_subseq(T,Subseq).
smallseq_largeseq_dis(Sseq,Lseq,Dis):-
findall(Subseq, (same_length(Subseq,Sseq),seq_subseq(Lseq,Subseq)),Subseqs),
maplist(seq_seq_absdis(Sseq),Subseqs,Distances),
aggregate(min(D),member(D,Distances),Dis).
Beispiel query:
?-smallseq_largeseq_dis([1,2,4],[1,2,3,1,2,5],Dis).
Dis = 1
Diese nächste Version effizienter sein sollte, da sie im Stich lassen werden, den Abstand zwischen einer Subsequenz der langen Sequenz und der kurzen Sequenz Berechnung, wenn der Abstand über das Minimum ist schon gefunden.
ea_smallseq_largeseq_dis(Sseq,Lseq,Subseq,Dis):-
retractall(best(_,_)),
assert(best(initial,10000)),
findall(Subseq-Dis, ea_smallseq_largeseq_dis_h(Sseq,Lseq,10000,Subseq,Dis),Pairs),
append(_,[Subseq-Dis|[]],Pairs).
ea_smallseq_largeseq_dis_h(Sseq,Lseq,BestSofar1,Subseq,Dis):-
same_length(Sseq,Subseq),
seq_subseq(Lseq,Subseq),
best(_,BestSofar2),
( ( BestSofar2 < BestSofar1) ->
accumulate_dis(Sseq,Subseq,BestSofar2,Dis),
retractall(best(_,_)),
assert(best(Subseq,Dis))
;(
accumulate_dis(Sseq,Subseq,BestSofar1,Dis),
retractall(best(_,_)),
assert(best(Subseq,Dis))
)
).
accumulate_dis(Seq1,Seq2,Best,Dis):-
accumulate_dis(Seq1,Seq2,Best,Dis,0).
accumulate_dis([],[],_Best,Dis,Dis).
accumulate_dis(Seq1,Seq2,Best,Dis,Ac):-
Seq1=[H1|T1],
Seq2=[H2|T2],
abs(H1,H2,Dis1),
Ac1 is Dis1 + Ac,
Ac1 <Best,
accumulate_dis(T1,T2,Best,Dis,Ac1).
Abfrage:
?-ea_smallseq_largeseq_dis([1,2,3],[1,2,4,5,6,7,8,1,2,3],Subseq,Dis).
Dis = 0,
Subseq = [1, 2, 3]
Aber in diesem habe ich verwendet assert und einfahren, so möchte ich eine Version haben, die den gleichen Algorithmus tut, aber mit aus diesen. Ich denke, dass ich das mit einem dcg mit Semicontext-Notation machen könnte, aber es ist schwer zu verstehen ... wie behalte ich die Subsequenzen im Auge, die ich durch Backtracking und gleichzeitig den 'Zustand' der Mindestdistanz erzeuge bis jetzt gefunden?
Das Problem habe ich .. seq_subseq/2 erzeugt die Untersequenzen durch Rückverfolgung. Der erste getestete sublive muss auf den Mindestabstand eingestellt werden. Ich möchte dann Schleife, also zurück verfolgen, um eine andere Sequenz zu generieren. Aber um zurückzuverfolgen, muss ich scheitern. Aber dann kann ich die minimale Entfernung nicht zurück bringen, um die nächste Sequenz zu überprüfen.
Wenn ich nicht Backtracking verwenden möchte, muss ich ein Zustandsübergangsprädikat für die Generierung der Untersequenzen in Reihenfolge definieren.
Im Moment
? seq_subseq([1,2,3,4],X).
X = [1]
X = [1, 2]
X = [1, 2, 3]
X = [1, 2, 3, 4]
X = [2]
X = [2, 3]
X = [2, 3, 4]
X = [3]
X = [3, 4]
X = [4]
Also ich denke, ich brauche eine Beziehung zu definieren:
subseq0_seq_subseq1(Subseq0,Seq,Subseq1)
, die wie funktionieren würde:
?-subseq0_seq_subseq1([1,2,3,4],[1,2,3,4],Subseq1).
Subseq1 = [2].
und
?-subseq0_seq_subseq1([1,2,3],[1,2,3,4],Subseq1).
Subseq1 = [1,2,3,4].
Aber ich muss dies auf eine effiziente Weise tun.
Update - Dank der Antwort von Mat habe ich jetzt das, was eine große Verbesserung ist, denke ich. Kann jemand weitere Verbesserungen sehen? Ich habe eine doppelte verschachtelte -> Struktur und ein! in der Definition accumulate_dis/4, die beide hässlich erscheinen. Ich habe es auch dazu gebracht, die Subsequenz der langen Sequenz zurückzugeben, die die kürzeste Entfernung von der kurzen Sequenz ist.
Es muss mit nicht ganzen Zahlen arbeiten, so clpfd ist nicht geeignet, denke ich.
abs(X,Y,Z):-
Z is abs(X-Y).
list_subseq_min(Ls, Subs, Min,BestSeq1) :-
prefix_dist(Ls, Subs, 1000, Front, D0),
BestSeq0=Front,
min_sublist(Ls, Subs,BestSeq0,BestSeq1, D0, Min).
prefix_dist(Ls, Ps, Best,Front,D) :-
same_length(Front, Ps),
append(Front, _, Ls),
accumulate_dis(Front, Ps, Best, D).
min_sublist(Ls0, Subs, BestSeq0,BestSeq2, D0, D) :-
( prefix_dist(Ls0, Subs, D0, Front,D1) ->
min_list([D0,D1],D2),
Ls0 = [_|Ls],
( D0 < D1 ->
BestSeq1 =BestSeq0
;
BestSeq1 =Front
),
min_sublist(Ls, Subs, BestSeq1,BestSeq2, D2, D)
; D = D0,BestSeq0 =BestSeq2
).
accumulate_dis(Seq1,Seq2,Best,Dis):-
accumulate_dis(Seq1,Seq2,Best,Dis,0),!.
accumulate_dis([],[],_Best,Dis,Dis).
accumulate_dis(Seq1,Seq2,Best,Dis,Ac):-
Seq1=[H1|T1],
Seq2=[H2|T2],
abs(H1,H2,Dis1),
Ac1 is Dis1 + Ac,
Ac1 <Best,
accumulate_dis(T1,T2,Best,Dis,Ac1).
accumulate_dis(Seq1,Seq2,Best,Dis):-Dis is Best+1.
query:
?- list_subseq_min([2.1,3.4,4,1.1,2,4,10,12,15],[1,2,3],D,B).
D = 1.1,
B = [1.1, 2, 4].
Danke für eine weitere nützliche Antwort. Ich habe aktualisiert mit dem, was ich jetzt habe, was besser ist, aber immer noch nicht sicher, es ist großartig! Übrigens glaube ich, ich benutze Manhattan Distanz nicht Hamming Entfernung - Entschuldigung das war nicht klar. Manhattan Abstand von [1,2,3,4] zu [5,2,3,4] ist 4, wo wie Hamming Abstand 1 ist, denke ich? – user27815
Ja natürlich, Manhatten Entfernung! – mat
Danke wirklich hilfreich. – user27815