最短距離の合計を見つける


10

このタスクでは、コードは、整数XおよびYの2つの並べ替えられた配列を入力として受け取る必要があります。Xの各整数とYの最も近い数の間の絶対距離の合計を計算する必要があります。

例:

X = (1 5,9)
Y = (3,4,7)

距離は2 + 1 + 2です。

X = (1,2,3)
Y = (0,8)

距離は1 + 2 + 3です。

コードは、都合のよい方法で入力を受け取ることができます。

主な制限は、コードが2つの配列の長さの合計で線形時間で実行される必要があることです。。(2つの整数の加算には一定の時間がかかると想定できます。)


配列の代わりにリストまたはストリームを使用できますか?
アドホックガーフハンター2018

@CatWizardはい、できます!
アヌッシュ

1
および1 + 2 + 3からどのように派生しますか?X = (1,2,3)Y = (0,8)
guest271314

1
@ guest271314最も近い番号2のそれぞれ12、と3YIS 0。このような違いがあります1-02-03-0
ディルナン2018

1
@FreezePhoenix両方のリストが並べ替えられているので、O(n + m)で行うことができます。これは、リストを反復処理して各要素に1回アクセスし、X iに最も近い要素Y jを追跡している限り、チェックY JY J + 1人の1からは、に最も近いXのI + 1バツYjバツYjYj+1バツ+1
ジュゼッペ・

回答:


6

Haskell70 64バイト

a%b=abs$a-b
x@(a:b)#y@(c:d)|e:_<-d,a%c>a%e=x#d|1>0=a%c+b#y
_#_=0

オンラインでお試しください!

説明

まず(%)、2つの数値の絶対差を定義します。次に(#)、興味深い関数であると定義します。最初の行では、両方のリストが空でない場合に一致します。

x@(a:b)#(c:d:e)

ここから私たちの最初のケースでは、我々はバインドde:_持ちますe:_<-d。これにより、dが空でなくなり、その最初の要素がに設定されeます。

次に、()の2番目の要素が最初の()よりもX()の最初の要素に近い場合、Yの最初の要素を削除して同じXで再度呼び出します。Yecバツax#dYバツ

パターンに一致するが条件に合格しない場合:

a%c+b#y

これは、最初のアイテム削除との最初の要素からそれの差分絶対値加算Xの残りの結果です。バツバツ

最後に、パターンに一致しない場合はを返します。パターンが一致しない場合、Yは空にできないため、Xは空でなければなりません。0バツY

このアルゴリズムには、順序表記ます。O|バツ|+|Y|

Haskell、34バイト

ここで私はそれを行うだろう方法です時間:O|バツ|×|Y|

x#y=sum[minimum$abs.(z-)<$>y|z<-x]

オンラインでお試しください!


2つの整数の加算には一定の時間がかかると想定できることを質問で明らかにしました。
アヌッシュ

2

パイソン2124の 120バイト

X,Y=input()
i=j=s=0
while i<len(X):
 v=abs(Y[j]-X[i])
 if j+1<len(Y)and v>=abs(Y[j+1]-X[i]):j+=1
 else:s+=v;i+=1
print s

オンラインでお試しください!

プログラムではなく機能に移動することにより、4バイトを節約しました。

両方のリストがソートされるため、時間の複雑さの制約を満たすことが可能です。ループを回るたびに、iインクリメントされるかインクリメントされることに注意してくださいj。したがって、ループはほとんどのlen(X)+len(Y)場合実行されます。


2つの整数の加算には一定の時間がかかると想定できることを質問で明らかにしました。
アヌッシュ

1

C(gcc)、82バイト

n;f(x,y,a,b)int*x,*y;{for(n=0;a;)--b&&*x*2-*y>y[1]?++y:(++b,--a,n+=abs(*x++-*y));}

これは、2つの整数配列とその長さとして入力を受け取ります(それ以外の場合Cでは長さを取得する方法がないため)。に到達したときに終了するループの反復ごとにまたはが減るO(a+b)ので、これが実行されていることを示すことができます(それ以下に減分できない)。aba0b0

オンラインでお試しください!

n;                     // define sum as an integer
f(x,y,a,b)             // function taking two arrays and two lengths
int*x,*y;              // use k&r style definitions to shorten function declaration
{
 for(n=0;              // initialize sum to 0
 a;)                   // keep looping until x (the first array) runs out
                       // we'll decrement a/b every time we increment x/y respectively
 --b&&                 // if y has ≥1 elements left (b>1, but decrements in-place)...
 *x*2-*y>y[1]?         // ... and x - y > [next y] - x, but rearranged for brevity...
 ++y:                  // increment y (we already decremented b earlier);
 (++b,                 // otherwise, undo the in-place decrement of b from before...
 --a,n+=abs(*x++-*y))  // decrement a instead, add |x-y| to n, and then increment x
;}

いくつかのメモ:

  • 配列にインデックスを付ける代わりに、ポインタをインクリメントして直接逆参照すると、その価値があるだけの十分なバイトが節約されます(*xvs x[a]およびy[1]vs y[b+1])。

  • --b&&以下のための条件をチェックb>1遠回しで-場合b1、それがゼロと評価されます。これはを変更するのでb、3進の最初のブランチ(これはy)で変更する必要はありませんが、2番目のブランチ()で元に戻す必要がありxます。

  • return黒魔術なので、文は必要ありません。(私が考える評価されるべき最後の文は常になりますので、それはだn+=...、戻り値のために使用されるものと同じレジスタを使用する表現、。)


0

Ruby、88バイト

->(p,q){a=q.each_cons(2).map{|a|a.sum/a.size}
x=a[0]
p.sum{|n|x=a.pop if n>x
(n-x).abs}}

オンラインでお試しください!

また、楽しみのために、複雑さの制限を完全に満たしていない、より短い無名関数:

->(a,b){a.map{|x|x-b.min_by{|y|(x-y).abs}}.sum}

このコードがどのように機能するかを簡単に説明できますか?線形時間で実行されるかどうかはわかりません。
アヌッシュ

2
これは、質問の最初のテストケースと、などの入力に失敗します[5, 6], [0, 1, 5]
ドアノブ

0

JavaScript(Node.js)、80バイト

x=>g=(y,i=0,j=0,v=x[i],d=v-y[j],t=d>y[j+1]-v)=>1/v?g(y,i+!t,j+t)+!t*(d>0?d:-d):0
  • O(| X | + | Y |)で実行されます:O(1)ですべての再帰が実行され、再帰的です| X | + | Y | 回。
    • xy参照によって渡され、コンテンツはコピーされません
  • 1/vx[i]範囲外の場合は偽物、それ以外の場合は真実
  • t-> d>y[j+1]-v-> v+v>y[j]+y[j+1]は、以下の条件が満たされている限りfalseです。そして、これはy[j]最も近い数字vですy
    • v未満(y[j]+y[j+1])/2、または
    • y[j+1]範囲外であり、に変換されNaNNaN収量と比較されますfalse
      • その>ため、さらに1バイトを節約するために符号を反転できません
  • t常にブール値であり*、計算する前に0/ 1に変換します

オンラインでお試しください!


0

Mathematica、40バイト

x = {1, 5, 9};
y = {3, 4, 7};

Norm[Flatten[Nearest[y] /@ x] - x]

入力付きの完全なプログラムを作成する必要がある場合:

f[x_,y_]:= Norm[Flatten[Nearest[y] /@ x] - x]

以下は、最大1,000,000ポイント(10,000ごとにサンプリング)のタイミングですy

ここに画像の説明を入力してください

線形に近い。


1
入力は既存の変数と見なされるため、この答えはコードスニペットです。サブルーチンまたは完全なプログラムに再フォーマットする必要があります。
アドホックガーフハンター2018

これが線形時間で機能することも少し疑わしいですが、なぜそれが必要なのかについて正当な理由がありますか?Mathematicaはビルトインの複雑さの点で不透明である傾向があります。
アドホックガーフハンター2018
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.