1

長いシーケンスと短いシーケンスの間の距離は、短いシーケンスと短いシーケンスと同じ長さの長いシーケンスのサブシーケンスの間の最小距離です。アルゴリズムのDcg状態の実装

私が使っている距離は、マンハッタン距離だと思います。 (しかし、これは距離関数を変更できるようにするためには重要ではありません)。

この最初のバージョンでは、早期放棄のない単純な実装が示されています。私は同じ長さのすべての部分配列を生成し、それらをマップして短い配列との距離を見つけ出し、次に集計/ 3を使って最小値を求めます。

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). 

例クエリ:

?-smallseq_largeseq_dis([1,2,4],[1,2,3,1,2,5],Dis). 
Dis = 1 

それは距離が最小終わると長いシーケンスのサブシーケンスと短い系列との間の距離を算出放棄れるように、この次のバージョンは、より効率的であるべきですすでに見つかりました。

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). 

問合せ:

?-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] 

しかし、これでは、私はアサートを使用して、私は同じアルゴリズムを行いますが、これらのうちでバージョンを持ちたいので撤回しています。私は半文字の表記法でdcgを使ってこれを行うことができるはずだが、把握するのは難しいと思う...私がバックトラックによって生成しているサブシーケンスと、同時に最小距離の '状態'これまで見つかった?

問題があります。 seq_subseq/2は、バックトラッキングによってサブシーケンスを生成しています。 テストされた最初のsubseqを最小距離に設定する必要があります。 私はループしたいので、バックトラックして別のシーケンスを生成してください。しかし、私は失敗する必要があります。しかし、それから次のシーケンスをチェックするために、これまでのmin距離を戻すことはできません。

バックトラックを使用したくない場合は、サブシーケンスを順番に生成するための状態遷移述語を定義する必要があると思います。現時点で

? 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] 

だから私は、私は関係を定義する必要があると思う:

のように働くだろう
subseq0_seq_subseq1(Subseq0,Seq,Subseq1) 

?-subseq0_seq_subseq1([1,2,3,4],[1,2,3,4],Subseq1). 
Subseq1 = [2]. 

?-subseq0_seq_subseq1([1,2,3],[1,2,3,4],Subseq1). 
Subseq1 = [1,2,3,4]. 

しかし、これを効率的に行う必要があります。

更新 - マットの回答のおかげで、これは私が考える大きな改善です。誰もがこれ以上の改善を見ることができますか?私はダブルネスト - >構造と!両方ともaccumulate_dis/4の定義では醜いようです。また、短いシーケンスから最短距離の長いシーケンスのサブシーケンスを返すようにしました。

これは非整数で動作する必要があるので、clpfdは適切ではないと思います。

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. 

クエリ:

?- 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]. 

答えて

2

一つの重要な注意:あなたがリスト間Manhatten  距離について話していることをclearifiedている必要があります。これはあなたのコードからはっきりしていましたが、あなたの言葉で簡単にあなたがの編集  の距離について話していると仮定することができます。

ここでは、リストを歩いて最小値を追跡し、最終的に最小値を求めるソリューションです。

 
list_subseq_min(Ls, Subs, Min) :- 
    prefix_dist(Ls, Subs, D0), 
    min_sublist(Ls, Subs, D0, Min). 

absdiff(X, Y, Z):- Z #= abs(X-Y). 

lists_dist(Ls1, Ls2, D) :- 
    maplist(absdiff, Ls1, Ls2, Ds), 
    sum(Ds, #=, D). 

prefix_dist(Ls, Ps, D) :- 
    same_length(Front, Ps), 
    append(Front, _, Ls), 
    lists_dist(Front, Ps, D). 

min_sublist(Ls0, Subs, D0, D) :- 
    ( prefix_dist(Ls0, Subs, D1) -> 
     D2 #= min(D0,D1), 
     Ls0 = [_|Ls], 
     min_sublist(Ls, Subs, D2, D) 
    ; D #= D0 
    ). 

例のクエリとその結果:

 
?- list_subseq_min([1,2,3,1,2,5], [1,2,4], D). 
D = 1. 

それは非常に単純明快だし、簿記が唯一の述語に限定されているので、semicontext表記を使用すると、本当に報われません。半文字表記—と 一般—のDCGを使用することは、説明されているものが異なるルールに属し、それらの間の通信が困難な場合に特に便利です。

実行時間 O(N × M)です。

そして今、私は練習として残してポイント:以前に見つかった最小がすでに超過した場合、以前のプルーンからこのソリューションを修正します。 の純粋なの方法で、または少なくとも可能な限り純粋なものにしてください:assertz/1と友人は 質問のうちの間違いなく質問です。引数を渡し、距離を徐々に増やしてください!これはもちろん、最悪の場合の複雑さではありませんが、平均実行時間を改善するのに役立ちます。

これは、半文字表記が最後に有用になる可能性がある、異なる節間の状態を渡すためのものです。

EDIT:プルーニングを行うソリューションを実装しました。私は今も私のことを示すでしょう。私は上からの補助述語absdiff/3lists_dist/3を再利用し、次の追加補助述語:

 
same_length_prefix(Ls, Ps, Front) :- 
     same_length(Front, Ps), 
     append(Front, _, Ls). 

list_subseq_min/3は今若干異なります。

 
list_subseq_min(Ls, Subs, Min) :- 
     same_length_prefix(Ls, Subs, Front), 
     lists_dist(Front, Subs, D0), 
     phrase(min_sublist(Ls, Subs), [D0-Front], [Min-_]). 

そして今ポイント:min_sublist//2DCGある 非終端記号は、アルゴリズムの主なアイデアを簡潔に説明しています。

 
min_sublist(Ls0, Subs) --> 
     ( front(Ls0, Subs) -> 
      { Ls0 = [_|Ls] }, 
      min_sublist(Ls, Subs) 
     ; [] 
     ). 

この説明から、リストを要素によって検討していることは明らかです。以前よりも少ない(明示的な)引数を使用します。追加の2つの引数は暗黙のうちにのペア   D-Frontとして渡され、これまで見つかった最適な距離とサブシーケンスを追跡します。 DCG  の表記法が計算の中心をどのように公開し、この場所に関係のないものが隠されるかに注意してください。

残りは完全に自明であり、実装したプルーニングに似ています。このプログラムでは半文字表記法を単独で使用することを強調しています。これにより、これまでに見つかった最適な配列の変化を表現できます。

 
?- list_subseq_min([21,34,40,11,20,40,100,120,150], [10,20,30], D). 
D = 11. 
:私は現代的な浮動小数点数の意地の悪さと原始を受け入れるように自分自身をもたらすことはできないので、私は、整数演算を保持し、それらが整数になるように、単にあなたが表示され、すべての数字を掛けてきた

 
front(Ls, Subs), [D-Front] --> 
     [Current], 
     { same_length_prefix(Ls, Subs, Front1), 
      capped_dist(Front1, Subs, Current, 0-Front1, D-Front) }. 

capped_dist([], [], _, DF, DF). 
capped_dist([L|Ls], [P|Ps], Current, D1-Front1, DF) :- 
     absdiff(L, P, D2), 
     D3 #= D1 + D2, 
     Current = D0-_, 
     ( D3 #> D0 -> DF = Current 
     ; capped_dist(Ls, Ps, Current, D3-Front1, DF) 
     ). 

これを拡張して、見つかったサブシーケンスも簡単に表示できるようにします。

重要な注意点:キャッピングは距離の計算にのみ影響します。特に実行時間がが  front//2で使用されているために  Θ(N × M)であることに注意してください。私はこれをやや難しい運動として改善し続けます。

+0

もう一度有益な答えをありがとう。私は今、私が今あるものを更新しましたが、それは良いですが、まだそれが偉大であることを確信していません!私はハミング距離ではなくマンハッタン距離を使用していると思います - 残念ですが、これははっきりしていませんでした。 [1,2,3,4]から[5,2,3,4]までのマンハッタンの距離は4で、ハミング距離が1であると私は思いますか? – user27815

+0

もちろん、Manhatten距離! – mat

+1

本当に助けていただきありがとうございます。 – user27815

関連する問題