1

長いシーケンスと短いシーケンスの間の距離は、短いシーケンスと短いシーケンスと同じ長さの長いシーケンスの任意のサブシーケンスとの間の最小距離です。

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

この最初のバージョンは、早期放棄のない素朴な実装を示しています。同じ長さのすべてのサブシーケンスを生成し、これらをマップしてそれらと短いシーケンスの間の距離を見つけ、aggregate/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 は、バックトラッキングによってサブシーケンスを生成しています。テストされる最初のサブシーケンスは、最小距離に設定する必要があります。次にループしたいので、トラックに戻って別のシーケンスを生成します。しかし、後戻りするには失敗しなければなりません。しかし、次のシーケンスを確認するために、これまでの最小距離を戻すことはできません。

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

この時点で

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

しかし、私はこれを効率的な方法で行う必要があります。

更新- Mat からの回答のおかげで、私はこれを手に入れました。これは大きな改善だと思います。誰でもこれに対するさらなる改善を見ることができますか? 私は二重にネストされた -> 構造と ! accumate_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].
4

1 に答える 1

2

1 つの重要な注意: リスト間のマンハッタン 距離について話していることを明確にしておく必要があります。これはあなたのコードからのみ明らかでしたが、あなたの言葉遣いは、編集 距離について話していると読者に容易に思わせる可能性があります。

リストを単純にたどり、最小値を追跡し、最終的に見つかった最小値を生成するソリューションを次に示します。

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

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

list_dist(Ls1, Ls2, D) :-
    maplist(absdiff, Ls1, Ls2, Ds),
    sum(Ds, #=, D)。

prefix_dist(Ls, Ps, D) :-
    same_length(フロント、Ps)、
    append(フロント、_、Ls)、
    lists_dist(Front, Ps, D).

min_sublist(Ls0, Subs, D0, D) :-
    ( prefix_dist(Ls0, Subs, D1) ->
        D2 #= 分(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。

これは非常に簡単です。ブックキーピングは述語が 1 つだけに制限されているため、セミコンテキスト表記法を使用しても実際には効果がありません。記述されているものが異なるルールにまたがり、それらの間の通信が困難になる場合、セミコンテキスト表記 (および一般的な DCG) を使用することは特に役立ちます。

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

そして今、私が演習として残すポイント:以前に見つかった最小値を既に超えている場合は、このソリューションを修正して、より早くプルーニングします。純粋な方法で、または少なくとも可能な限り純粋な方法で行ってください。assertz/1友人は、これらの述語を単独でテストすることを妨げるため、間違いなく問題外です。引数を渡して、距離をより段階的に構築してください! これは、もちろん最悪の場合の複雑さではありませんが、平均実行時間を改善するのに役立つ場合があります。

セミコンテキスト表記法が最終的に有用になるのは、この異なる節間での状態の受け渡しのためです。

編集:非常によく、プルーニングを行うソリューションを実装しました。私も今、私のものを見せます。absdiff/3上記の補助述語とlists_dist/3次の追加の補助述語を再利用します。

same_length_prefix (Ls、Ps、フロント) :-
        same_length(フロント、Ps)、
        append(フロント、_、Ls)。

list_subseq_min/3はわずかに異なります。

list_subseq_min(Ls, Subs, Min) :-
        same_length_prefix(Ls、サブ、フロント)、
        lists_dist(フロント、サブ、D0)、
        フレーズ(min_sublist(Ls、Subs)、[D0-Front]、[Min-_])。

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

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

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

残りは自明であり、実装したプルーニングに類似しています。このプログラムでは、セミコンテキスト表記を 1 つだけ使用していることを強調します。これにより、これまでに見つかった最適なシーケンスの変更を表現できます。

front(Ls, Subs), [D-Front] -->
        [現時点の]、
        { 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、
        電流 = D0-_,
        ( D3 #> D0 -> DF = 現在
        ; capped_dist(Ls, Ps, 現在, D3-Front1, DF)
        )。

現代の浮動小数点数の厄介さと原始性を受け入れることができないので、整数演算を保持し、表示されるすべての数値を単純に乗算して整数にします。

?- list_subseq_min([21,34,40,11,20,40,100,120,150], [10,20,30], D)。
D = 11。

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

1 つの重要な注意: キャッピングは距離の計算にのみ影響します。特に、 での使用 方法により、実行時間がΘ(N×M)であることに注意してください 。これを改善することは、少し難しい演習として残します。same_length_prefix/3front//2

于 2016-06-01T09:28:33.990 に答える