問題:
N点は2次元平面上に与えられます。同じ直線ライン上のポイントの最大数はいくつですか?
問題はO(N2)解決策:各ポイントを調べ、現在のポイントとの関係で同じdx / dy
を持つポイントの数を見つけます。効率を上げるために、dx / dy
リレーションをハッシュマップに格納します。
この問題の解決策としてO(N2)?
標準的な計算モデルでは、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を複製し、次に重複を削除してから、同一線上の点を見つける問題。
問題をOriginを通る線に限定すると、ポイントを極座標(角度、Originからの距離)に変換し、角度順に並べ替えることができます。同じ角度のすべての点は同じ線上にあります。 O(n logn)
一般的なケースでは、もっと速い解決策はないと思います。
ハフ変換 はおおよその解を与えることができます。ビニングテクニックではパラメーター空間の解像度が限られているため、これはおおよその値であり、最大のビンは可能なラインの限られた範囲を提供します。
すでに述べたように、この問題の一般的なケースを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演算の大きさが必要です。)
ここでも、疑似コードを使用したO(n ^ 2)ソリューションです。アイデアは、行自体をキーとしてハッシュテーブルを作成することです。線は、線がx軸を切る点と線がy軸を切る点の2つの点の間の勾配によって定義されます。
ソリューションでは、JavaやC#などの言語を想定しており、オブジェクトのequalsメソッドとhashcodeメソッドがハッシュ関数に使用されます。
3つのフィールドを持つオブジェクトを作成(SlopeObjectを呼び出す)
poix
//(無限大、いくつかのy値)または(x値、0)poix
は点(x、y)のペアになります。行がx軸と交差する場合、poix
は(いくつかの数値、0)になります。線がx軸に平行な場合、poix =(無限大、ある数)ここでy値は線がy軸と交差する場所です。 Slope
とpoix
が等しい場合に2つのオブジェクトが等しい場合、equalsメソッドをオーバーライドします。
ハッシュコードは、Slope
とpoix
の値の組み合わせに基づいてハッシュコードを提供する関数でオーバーライドされます。以下のいくつかの疑似コード
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);