2016-12-17 5 views
5

点オブジェクトのリストに正方形があるかどうかをテストしたいと思います。 4点が正方形であるかどうかをテストすることができます点リスト内の正方形を検出

class Point { 
    private int x; 
    private int y; 

    public Point(int x, int y) { 
     this.x = x; 
     this.y = y; 
    } 

    public int getX() { 
     return x; 
    } 

    public void setX(int x) { 
     this.x = x; 
    } 

    public int getY() { 
     return y; 
    } 

    public void setY(int y) { 
     this.y = y; 
    } 

    public int distanceSquare(Point q) { 
     return (x - q.getX()) * (x - q.getX()) + 
       (y - q.getY()) * (y - q.getY()); 
    } 
} 

static boolean isSquare(Point p1, Point p2, Point p3, Point p4) { 
     int d2 = p1.distanceSquare(p2); // from p1 to p2 
     int d3 = p1.distanceSquare(p3); // from p1 to p3 
     int d4 = p1.distanceSquare(p4); // from p1 to p4 

     if (d2 == d3 && 2 * d2 == d4) { 
      int d = p2.distanceSquare(p4); 
      return (d == p3.distanceSquare(p4) && d == d2); 
     } 

     if (d3 == d4 && 2 * d3 == d2) { 
      int d = p2.distanceSquare(p3); 
      return (d == p2.distanceSquare(p4) && d == d3); 
     } 
     if (d2 == d4 && 2 * d2 == d3) { 
      int d = p2.distanceSquare(p3); 
      return (d == p3.distanceSquare(p4) && d == d2); 
     } 

     return false; 
    } 

しかし、私は、正方形を検索するための最良の方法を発見していなかった

これは私のPointクラスでありますPointのリスト

あなたは私を助けることができます!ここ

+1

リスト内で正方形を形成する点は連続していますか? – StaticBeagle

答えて

0

はO(n^2 LG(N)) -timeとO(N) -spaceアルゴリズムです。頂点を角度で事前にソートする場合は、すべてのペア{A、B}に対してOCとODの両方の角度を見つけることができます。 Cの座標を見つける方法

compute angle for every point -- O(n) 
sort point by angles -- O(n*lg(n)) 
for each pair -- O(n^2*lg(n)) 
    if (B.x > A.x) && (B.y > A.y) 
     compute positions of C and D 
     compute angles of C and D 
     for C and D 
      if there is a pair of points with same angles and same positions 
       return true 
return false 

enter image description here

Xc = Xb - (Yb - Ya) 
Yc = Yb + (Xb - Xa) 
5

は、ポイントのすべてのペアを取ります。各ペアについて角度、長さ、中間点を計算します。これはO(n^2)時間かかるでしょう。同一の中間点と同じ長さソート角度によって、これらのペアを有するペアの各セットについて
、これは合計O中(N^2 *ログ(n))は時間がかかります。これは同じ長さの2つの直交する対角線が同じ中間点(つまり正方形)を見つけるのに役立ちます。
総時間アルゴリズムはO(n^2 * log(n))です。また、検出されたために更新

+0

*これは、同じ長さの2つの直交する対角線が同じ中間点*を持つのに役立ちます* - すべての対についてlg(n)で行うことができるということですか?あなたは精巧にできますか?私にとってはlg(n^2)のように見えます。 – Yola

+0

@ Yola - delta = pi/2を持つ2つの値を検索するために、このソートされたリストに渡って2つのポインタを同時に移動するだけです。この操作は線形時間の複雑さを持っています –

+0

ああ、申し訳ありませんが、同じ中間点と同じ長さ*を持つペアのセットについて別の部分を見逃しました。そのようなセットをどのように入手するのかは不明ですが、この操作の複雑さは何ですか?そして、そのような集合(円上の点)が1つしかない場合、並べ替えはO(n^2 * lg(n^2))になるでしょうか? – Yola

0

が正方形に

を回転させ、私は上記のEgor Skriptunoffの答えを好きですが、私は に別の答えを与えることを試してみましょう。複雑さはO(N^2)だと私は思う。

アルゴリズム

点(P0、P1)(それらのN^2である)の任意の対について、P1に をP0からベクトルV01を見つけるし、次いで90 によってこのベクトルを回転させます度(V12になります)。それをP1に追加し、そこに という点があるかどうかを確認してください。 (これはハッシュマップルックアップで行うことができます - 詳細は以下を参照してください)もしそうなら あなたはP2を持っており、手順を続行します。

ベクトルをさらに90度回転させて(それはV23になります)、それを P2に追加して、ポイントを見つけることができるかどうかを確認します。 (再び、ハッシュマップルックアップによって)
もしそうなら、あなたはP3を持っており、手順を続行します。

は別の90度(それはV34となる)によってベクターを回し。 をP3に追加して、そこにポイントを見つけることができますか? (再び、ハッシュマップルックアップによって)。 もしそうなら、この点P4がP0と同じであるかどうかをチェックしてください。その場合は、 あなたはちょうど正方形を検出しました。

次の図は、アイデアを示しています。

enter image description here

データ構造

正方形が回転可能である場合、ポイント のx、y座標は、浮動小数点(二重)でなければならず、整数ことができません。 ので、計算の多くは(例えばSQRT(2))

しかし、二重の表現は整数として正確にすることはできませんあなたが無理数を与える、 は、私たちは近くにある二つの二重の数字を治療するための を注意する必要があります十分な倍の 値で十分です。私のコードでは、 "approximate equivalence"のために を許可する許容値としてイプシロンを使用します。イプシロンとして1E-3を選んだ。

public class Point2 { 
    private double x; 
    private double y; 
    private final double eps = 1E-3;  
    public double getEps() { 
     return eps; 
    } 

そしてequals()hashCode()に関するすべての計算では、 はあなたが彼らの元 ダブル表現、xとyの値を「スナップ」ではないを使用してください。 (グラフィカルに、 グラフィカルエディタのようにグリッドサイズ がイプシロンである「グリッドにスナップ」機能を与えると想像することができます)。また、二重reprsentations には、正の0と負の0があることに注意する必要がありますまた、 同じ値として扱う必要があります。このような

何か:

public long getXsnapped() { 
    return Math.round(this.getX()/this.getEps()); 
} 
public long getYsnapped() { 
    return Math.round(this.getY()/this.getEps()); 
} 
@Override 
public int hashCode() { 
    final int prime = 31; 
    int result = 1; 
    long temp; 
    temp = Double.doubleToLongBits(eps); 
    result = prime * result + (int) (temp^(temp >>> 32)); 
    if (Math.abs(this.getX())>this.getEps()) { 
     // include X only if it is larger than eps 
     temp = this.getXsnapped(); 
     result = prime * result + (int) (temp^(temp >>> 32)); 
    } 
    if (Math.abs(this.getY())>this.getEps()) { 
     temp = this.getYsnapped(); 
     result = prime * result + (int) (temp^(temp >>> 32)); 
    } 
    return result; 
} 
@Override 
public boolean equals(Object obj) { 
    if (this == obj) 
     return true; 
    if (obj == null) 
     return false; 
    if (getClass() != obj.getClass()) 
     return false; 
    Point2 other = (Point2) obj; 
    if (Double.doubleToLongBits(eps) != Double.doubleToLongBits(other.eps)) 
     return false; 
    boolean answer = true; 
    if (Math.abs(this.getX())>this.getEps() 
      || Math.abs(other.getX())>this.getEps()) { 
     // compare value and sign only if X of both points are larger than eps 
     if (this.getXsnapped()!= other.getXsnapped()) 
      answer = false;   
    } 
    if (Math.abs(this.getY())>this.getEps() 
      || Math.abs(other.getY())>this.getEps()) { 
     // compare value and sign only if Y of both points are larger than eps 
     if (this.getYsnapped()!= other.getYsnapped()) 
      answer &= false; 
    } 
    boolean isDebug = false; 
    Util.debugPrint(isDebug, "p1 %s; p2 %s: %b (eps is %.9f)%n" 
     , this, other, answer, this.getEps()); 
    return answer; 
} 

さらに、各正方形は4ポイントを持っています。 あなたのプログラムには、 のアルゴリズム(P0-> P1-> P2-> P3)で、角度関係 (上記のアルゴリズムを参照)を使用して、4つのポイントが順番に並んでいなければならないという規則を追加できます。しかし、あなたはまた、 と同じ4点があることを覚悟する必要があります。開始点を とする選択肢が4つあります。

P0->P1->P2->P3 
P1->P2->P3->P0 
P2->P3->P0->P1 
P3->P0->P1->P2 

これは広場オブジェクトのコンストラクタで行われ、常に 「カノニカル」 による入力がポイントを選ぶことができます。だからあなたのスクエアオブジェクト・コードは、同等のものとしてこれらの4点で指定された 次正方形を扱わなければなりません特定のセーリング機能 を開始点として使用します(例:他の点とのx値タイは、その後結ば対の中で最も低い xおよび小さいY)

テスト入力1

-1.4142, 9.8995 
-5.6569, 15.5563 
1.4142, 9.8995 
-1.4142, 14.1421 
-2.1213, 14.8492 
1.4142, 14.1421 
0.0000, 15.5563 
-2.1213, 17.6777 
7.0711, 11.3137 
5.6569, 12.7279 
4.2426, 14.1421 
6.3640, 10.6066 
7.0711, 14.1421 
5.6569, 15.5563 
1.4142, 19.7990 
7.7782, 14.8492 
を有する点を選択した場合、最低Xを有する点を選択し、そして

試験結果1

===== Given a set of following points from file src\detectSquare\inputSet1_45_1_1_0_0.txt ===== 
1: Point2 [x=-1.4142, y=9.8995] 
2: Point2 [x=-5.6569, y=15.5563] 
3: Point2 [x=1.4142, y=9.8995] 
4: Point2 [x=-1.4142, y=14.1421] 
5: Point2 [x=-2.1213, y=14.8492] 
6: Point2 [x=1.4142, y=14.1421] 
7: Point2 [x=0.0000, y=15.5563] 
8: Point2 [x=-2.1213, y=17.6777] 
9: Point2 [x=7.0711, y=11.3137] 
10: Point2 [x=5.6569, y=12.7279] 
11: Point2 [x=4.2426, y=14.1421] 
12: Point2 [x=6.3640, y=10.6066] 
13: Point2 [x=7.0711, y=14.1421] 
14: Point2 [x=5.6569, y=15.5563] 
15: Point2 [x=1.4142, y=19.7990] 
16: Point2 [x=7.7782, y=14.8492] 
===== The following squares have been found ===== 
1: SquareRotatable [points=[Point2 [x=4.2427, y=14.1421], Point2 [x=5.6569, y=12.7279], Point2 [x=7.0711, y=14.1421], Point2 [x=5.6569, y=15.5563]]] 

テスト入力2

0.0000, 0.0000 
-0.7071, 0.7071 
-1.4142, 1.4142 
0.7071, 0.7071 
0.0000, 1.4142 
-0.7071, 2.1213 
1.4142, 1.4142 
0.7071, 2.1213 
0.0000, 2.8284 

試験結果2

===== Given a set of following points from file src\detectSquare\inputSet2_45_0_0_0_0.txt ===== 
1: Point2 [x=0.0000, y=0.0000] 
2: Point2 [x=-0.7071, y=0.7071] 
3: Point2 [x=-1.4142, y=1.4142] 
4: Point2 [x=0.7071, y=0.7071] 
5: Point2 [x=0.0000, y=1.4142] 
6: Point2 [x=-0.7071, y=2.1213] 
7: Point2 [x=1.4142, y=1.4142] 
8: Point2 [x=0.7071, y=2.1213] 
9: Point2 [x=0.0000, y=2.8284] 
===== The following squares have been found ===== 
1: SquareRotatable [points=[Point2 [x=-1.4142, y=1.4142], Point2 [x=0.0000, y=0.0000], Point2 [x=1.4142, y=1.4142], Point2 [x=0.0000, y=2.8284]]] 
2: SquareRotatable [points=[Point2 [x=-0.7071, y=0.7071], Point2 [x=0.7071, y=0.7071], Point2 [x=0.7071, y=2.1213], Point2 [x=-0.7071, y=2.1213]]] 
3: SquareRotatable [points=[Point2 [x=-1.4142, y=1.4142], Point2 [x=-0.7071, y=0.7071], Point2 [x=0.0000, y=1.4142], Point2 [x=-0.7071, y=2.1213]]] 
4: SquareRotatable [points=[Point2 [x=-0.7071, y=2.1213], Point2 [x=0.0000, y=1.4142], Point2 [x=0.7071, y=2.1213], Point2 [x=-0.0000, y=2.8284]]] 
5: SquareRotatable [points=[Point2 [x=-0.7071, y=0.7071], Point2 [x=0.0000, y=0.0000], Point2 [x=0.7071, y=0.7071], Point2 [x=0.0000, y=1.4142]]] 
6: SquareRotatable [points=[Point2 [x=0.0000, y=1.4142], Point2 [x=0.7071, y=0.7071], Point2 [x=1.4142, y=1.4142], Point2 [x=0.7071, y=2.1213]]] 

JUnitのテストプログラムのテストPoint2#getXsnapped()(断片のみ)

import org.junit.Before; 
import org.junit.BeforeClass; 
import org.junit.Ignore; 
import org.junit.Test; 

public class Point2Test { 
    private List<Point2> points = new ArrayList<>(); 

    @Before 
    public void setUp() throws Exception { 
     points.add(new Point2(0.49999999f, 0)); 
     points.add(new Point2(0.50000001f, 0)); 
    } 
    ... 

    @Test 
    public void testGetXsnapped() { 
     System.out.format("testing xSnapped of two points: %s and %s%n" 
       , points.get(0), points.get(1)); 
     System.out.format("and they are %d and %d respectively%n" 
       , points.get(0).getXsnapped() 
       , points.get(1).getXsnapped()); 
     System.out.format("(Note: epsilon is %f)%n" 
       , points.get(0).getEps()); 

     assertEquals(points.get(0).getXsnapped() 
        , points.get(1).getXsnapped()); 
    } 
} 

JUnitのテスト

の出力
testing xSnapped of two points: Point2 [x=0.5000, y=0.0000] and Point2 [x=0.5000, y=0.0000] 
and they are 500 and 500 respectively 
(Note: epsilon is 0.001000) 

警告

エリックDuminilは互いに任意に近い、まだグリッド上の異なる点にスナップさ 2点が存在し得ることを指摘で正しいです。

私の頭の上から、私はこの問題を解決する方法を知らない。提案 を歓迎します!

など。 2点が異なると考えられている - 私は出力を次のようになるだろう

public static void debugPrint(boolean isDebug, String format, Object... args) { 
    if (!isDebug) { 
     return; // don't print 
    } 
    System.out.format(format, args); 
} 

public long getXsnapped() { 
    boolean isDebug = true; 
    String _ = " "; // indent 
    double X = this.getX(); 
    Util.debugPrint(isDebug, _ + "X is %E (long bits is 0x%x)%n" 
           , X, Double.doubleToLongBits(X)); 
    double eps = this.getEps(); 
    Util.debugPrint(isDebug, _ + "eps is %E%n", eps); 
    double fraction = X/eps; 
    Util.debugPrint(isDebug, _ + "fraction is %E (long bits is 0x%x)%n" 
           , fraction, Double.doubleToLongBits(fraction)); 
    long answer = Math.round(fraction); 
    Util.debugPrint(isDebug, _ + "xSnapped is %d%n", answer); 
    return answer; 
} 

Util.debugPrint():これらの追加デバッグコード付き

@Before 
public void setUp() throws Exception { 
    Point2 dummy = new Point2(0, 0); // just to get epsilon 
    points.add(new Point2(dummy.getEps()*0.5, 0)); 
    points.add(new Point2(dummy.getEps()*0.49999999999999, 0)); 
} 

testing xSnapped of two points: Point2 [x=0.0005, y=0.0000] and Point2 [x=0.0005, y=0.0000] 
    X is 5.000000E-04 (long bits is 0x3f40624dd2f1a9fc) 
    eps is 1.000000E-03 
    fraction is 5.000000E-01 (long bits is 0x3fe0000000000000) 
    xSnapped is 1 
    X is 5.000000E-04 (long bits is 0x3f40624dd2f1a9a0) 
    eps is 1.000000E-03 
    fraction is 5.000000E-01 (long bits is 0x3fdfffffffffff4c) 
    xSnapped is 0 
and they are 1 and 0 respectively 
(Note: epsilon is 0.001000) 
delta between the two x (as double) is 9.974660E-18 
    X is 5.000000E-04 (long bits is 0x3f40624dd2f1a9fc) 
    eps is 1.000000E-03 
    fraction is 5.000000E-01 (long bits is 0x3fe0000000000000) 
    xSnapped is 1 
    X is 5.000000E-04 (long bits is 0x3f40624dd2f1a9a0) 
    eps is 1.000000E-03 
    fraction is 5.000000E-01 (long bits is 0x3fdfffffffffff4c) 
    xSnapped is 0 
+0

これは回転した四角形に対して機能しますか? – Lidae

+0

申し訳ありません。それは良い点です。後で更新された回答を提供できるかどうかがわかります。 – leeyuiwah

+0

正方形が回転可能でなければならないという要件を指摘してくれてありがとう。私は上記の私の答えを更新しました。 – leeyuiwah

関連する問題