web-dev-qa-db-ja.com

ほとんどのポイントを通る直線を見つけるための最も効率的なアルゴリズムは何ですか?

問題:

N点は2次元平面上に与えられます。同じ直線ライン上のポイントの最大数はいくつですか?

問題はO(N2)解決策:各ポイントを調べ、現在のポイントとの関係で同じdx / dyを持つポイントの数を見つけます。効率を上げるために、dx / dyリレーションをハッシュマップに格納します。

この問題の解決策としてO(N2)?

47
Leonid

標準的な計算モデルでは、O(n ^ 2)よりもはるかに優れたこの問題の解決策はおそらくありません。

3つの同一線上の点を見つける問題は、ほとんどの点を通る線を見つけるという問題に減少し、3つの同一線上の点を見つけることは3SUM困難です。理論上の結果。

3つの同一線上の点を見つけることについては、 前の質問 を参照してください。

参考のために(既知の証明を使用して)、リストXでx + y + z = 0となるx、y、zを見つけるなどの3SUM問題に回答したいとします。共線点問題の高速アルゴリズムがある場合、そのアルゴリズムを使用して、次のように3SUM問題を解決できます。

Xの各xについて、点(x、x ^ 3)を作成します(今のところ、Xの要素は別個であると想定しています)。次に、作成されたポイントの中から3つの同一線上のポイントが存在するかどうかを確認します。

これが機能することを確認するには、x + y + z = 0の場合、xからyへの直線の傾きが

(y ^ 3-x ^ 3)/(y-x)= y ^ 2 + yx + x ^ 2

そして、xからzへの線の傾きは

(z ^ 3-x ^ 3)/(z-x)= z ^ 2 + zx + x ^ 2 =(-(x + y))^ 2-(x + y)x + x ^ 2 = x ^ 2 + 2xy + y ^ 2-x ^ 2-xy + x ^ 2 = y ^ 2 + yx + x ^ 2

逆に、xからyへの勾配がxからzへの勾配と等しい場合、

y ^ 2 + yx + x ^ 2 = z ^ 2 + zx + x ^ 2、

それはそれを意味します

(y-z)(x + y + z)= 0、

したがって、削減が有効であることを証明するには、y = zまたはz = -x-yで十分です。

Xに重複がある場合は、最初にx + 2y = 0かどうかを確認し、(ハッシュを使用して線形時間で、またはソートを使用してO(n lg n)時間で)xを複製し、次に重複を削除してから、同一線上の点を見つける問題。

38
jonderry

問題をOriginを通る線に限定すると、ポイントを極座標(角度、Originからの距離)に変換し、角度順に並べ替えることができます。同じ角度のすべての点は同じ線上にあります。 O(n logn)

一般的なケースでは、もっと速い解決策はないと思います。

4
Erwin J.

ハフ変換 はおおよその解を与えることができます。ビニングテクニックではパラメーター空間の解像度が限られているため、これはおおよその値であり、最大のビンは可能なラインの限られた範囲を提供します。

4
ergosys

すでに述べたように、この問題の一般的なケースをO(n ^ 2)よりも解決する方法はおそらくありません。ただし、同じ線上に多数の点があると仮定し(点のセット内のランダムな点が最大点数の線上にある確率がpであるとする)、正確なアルゴリズムが必要ない場合は、ランダム化されたアルゴリズムの方が効率的です。

maxPoints = 0
Repeat for k iterations:
    1. Pick 2 random, distinct points uniformly at random
    2. maxPoints = max(maxPoints, number of points that lies on the 
       line defined by the 2 points chosen in step 1)

最初のステップで、ポイントの最大数を持つライン上にある2つのポイントを選択した場合、最適なソリューションが得られることに注意してください。 nが非常に大きいと仮定すると(つまり、2つの望ましい点を見つける確率を、置換を伴うサンプリングとして扱うことができます)、これが発生する確率はp ^ 2です。したがって、k回の反復後に準最適解を見つける確率は、(1-p ^ 2)^ kです。

偽陰性率= errを許容できると仮定します。次に、このアルゴリズムはO(nk) = O(n * log(err)/ log(1-p ^ 2))で実行されます。nとpの両方が十分に大きければ、これはかなり大きくなります。 O(n ^ 2)よりも効率的です(つまり、n = 1,000,000と想定し、同じライン上に少なくとも10,000のポイントがあることがわかっているとします。その後、ランダム化されたアルゴリズムでは、n ^ 2は10 ^ 12演算の規模で必要になります。 5 * 10 ^ -5未満のエラー率を得るには、10 ^ 9演算の大きさが必要です。)

0
Tony Cai

ここでも、疑似コードを使用したO(n ^ 2)ソリューションです。アイデアは、行自体をキーとしてハッシュテーブルを作成することです。線は、線がx軸を切る点と線がy軸を切る点の2つの点の間の勾配によって定義されます。

ソリューションでは、JavaやC#などの言語を想定しており、オブジェクトのequalsメソッドとhashcodeメソッドがハッシュ関数に使用されます。

3つのフィールドを持つオブジェクトを作成(SlopeObjectを呼び出す)

  1. スロープ//無限にできる
  2. X軸との交点-poix //(無限大、いくつかのy値)または(x値、0)
  3. カウント

poixは点(x、y)のペアになります。行がx軸と交差する場合、poixは(いくつかの数値、0)になります。線がx軸に平行な場合、poix =(無限大、ある数)ここでy値は線がy軸と交差する場所です。 Slopepoixが等しい場合に2つのオブジェクトが等しい場合、equalsメソッドをオーバーライドします。

ハッシュコードは、Slopepoixの値の組み合わせに基づいてハッシュコードを提供する関数でオーバーライドされます。以下のいくつかの疑似コード

Hashmap map;
foreach(point in the array a) {
    foeach(every other point b) {
        slope = calculateSlope(a, b);
        poix = calculateXInterception(a, b);
        SlopeObject so = new SlopeObject(slope, poix, 1); // Slope, poix and intial count 1.
        SlopeObject inMapSlopeObj = map.get(so);
        if(inMapSlopeObj == null) {
            inMapSlopeObj.put(so);
        } else {
            inMapSlopeObj.setCount(inMapSlopeObj.getCount() + 1);
        }
    }
}
SlopeObject maxCounted = getObjectWithMaxCount(map);
print("line is through " + maxCounted.poix + " with slope " + maxCounted.slope);
0