2012-05-19 11 views
15

明らかに平方根を計算するのはあまり効率的ではありません。それで、2つの円の間の距離(以下で範囲と呼ぶ)を見つけるのが最善の方法は何ですか?javaの2つの円の間の距離を見つける最も効率的な方法は?

Find range between two circles

ので、通常私は仕事になります。

a^2 + b^2 = c^2 
dy^2 + dx^2 = h^2 
dy^2 + dx^2 = (r1 + r2 + range)^2 
(dy^2 + dx^2)^0.5 = r1 + r2 + range 
range = (dy^2 + dx^2)^0.5 - r1 - r2 

を平方根を避けるためにしようと、「範囲」は、衝突のために0であるとき、あなただけの状況を探したときに正常に動作します

if ((r1 + r2 + 0)^2 > (dy^2 + dx^2)) 

しかし、私がその距離の範囲を解決しようとすると、私はいくつかの扱いにくい式のようになります:

range(range + 2r1 + 2r2) = dy^2 + dx^2 - (r1^2 + r2^2 + 2r1r2) 

これはどこにもありません。少なくとも私は明白な答えは、その後trignometryと最初のFindシータある

...ここから範囲のためにそれを解決する方法がわからない:

Tan(theta) = dy/dx 
    theta = dy/dx * Tan^-1 

その後hypotemuse 罪(シータ)を見つけます= DY/H H = DY/SIN(シータ)

最後範囲 範囲+ R1 + R2 = DY/SIN(シータ) 範囲= DY/SIN(シータ)うまく - R1は - R2

だからだから私の質問は、平方根を見つけることよりも日焼けして罪より効率的に使用されている

private int findRangeToTarget(ShipEntity ship, CircularEntity target){ 


    //get the relevant locations 
    double shipX = ship.getX(); 
    double shipY = ship.getY(); 
    double targetX = target.getX(); 
    double targetY = target.getY(); 
    int shipRadius = ship.getRadius(); 
    int targetRadius = target.getRadius(); 

    //get the difference in locations: 
    double dX = shipX - targetX; 
    double dY = shipY - targetY; 

    // find angle 
    double theta = Math.atan( (dY/dX)); 

    // find length of line ship centre - target centre 
    double hypotemuse = dY/Math.sin(theta); 

    // finally range between ship/target is: 
    int range = (int) (hypotemuse - shipRadius - targetRadius); 

    return range; 

} 

:?私がやったとこのようになります方法を持っているものです時

私のコードのいくつかをリファクタリングして、別の方法(私はそれを解決する必要があります)からテータ値を得ることができますか?

また別の方法がありますか?

私は

任意のヒントやアドバイスを歓迎...私が何かをする高校の数学を使用していたので、長い時間がかかった、明白なことを尋ねる、または任意の基本ミスをしてるなら、私を許しなさい!

**** **** EDIT

具体的に私はなど遠ざかる/敵/障害物が接近している時に検出し、ゲームに「スキャナ」のデバイスを作成しようとしているスキャナがこれを中継しますオーディオトーンまたはグラフィックバーまたは何かを介して情報を送信する。私は正確な数を必要としないためものの、理想的には私が知っていると思います:

  1. ターゲットは/近いさらにより
  2. ターゲットAは近いの前に/さらにターゲットB、C、D ...
  3. より
  4. 0(衝突)と最大範囲(ある定数)に対してターゲットからどれくらい離れているかを表すA(線形のうまくいけば?)の比率
  5. いくつかのターゲットは非常に大きいでしょう)私は半径を考慮に入れる必要があります

私はいくつかの賢明な最適化/近似が可能です(dx + dy +(長いdx、dy?あなたは本当に、あなたが本当に平方根を避けることができない、正確な距離を必要とする...

+1

私はあなたの斜辺で遊んでいます! –

+0

'toDegrees'を使用しません:' Math.sin'はラジアンを使用します(Mathのすべてのtrig関数を実行します) –

+2

'sqrt'でコードをプロファイリングしましたか? – siamii

答えて

12

Math.hypotは、フォームsqrt(x^2 + y^2)のより高速で正確な計算を行うように設計されています。したがって、このだけ

return Math.hypot(x1 - x2, y1 - y2) - r1 - r2; 

私はこれよりもシンプル、でも速いだろう任意のコードを想像することはできませんする必要があります。

+1

+1、興味深い、私は以前にその方法に気づいたことがない! –

+1

あまり心配しないで他の答えから手がかりを取って、これは今のところ最も賢明な解決策だと思う。将来パフォーマンスが問題になる場合は、さらに近似値とルックアップテーブルを調べます。 – kiman

+0

本当に速いですか?どうして知っていますか? – Inverse

9

場合。悪化していない場合は三角関数は、平方根の計算と少なくとも同じ悪いです。

しかし、あなたが必要な場合はおおよその距離、または必要な場合はの相対の距離あなたができることは間違いありません。たとえば、相対距離だけが必要な場合、平方根は平方根と同じより大きい関係にあることに注意してください。異なるペアのみを比較している場合は、平方根のステップをスキップすると、同じ答えが得られます。

近似距離だけが必要な場合、hは、隣接する長い方の面とおおよそ同じであると考えられます。この近似は決して2倍以上には決してありません。または、任意の平方根のルックアップテーブルよりも実用的な三角関数のルックアップテーブルを使用することもできます。

+0

ありがとうアーネスト、そこにいくつかの便利なポインタがあります。私は、dx + dy(半径を引いたもの)と両辺の長さを追加するようなものが、私の条件のほとんどを満たす(私はちょうど私のポストにそれらを追加しました)まともな近似を与えるかもしれないかと思います。大きな問題は、このデータをスムーズに表現してユーザーに出力したいと思うことです...とにかく、次の日に何か改善ができない場合は、この回答を正しいとマークします。 – kiman

1

私がtan、sineを使うときの答えが最初にsqrt関数を使うときと同じかどうかは分かりません。

public static void main(String[] args) throws Exception { 
    // TODO Auto-generated method stub 
    double shipX = 5; 
     double shipY = 5; 
     double targetX = 1; 
     double targetY = 1; 
     int shipRadius = 2; 
     int targetRadius = 1; 

     //get the difference in locations: 
     double dX = shipX - targetX; 
     double dY = shipY - targetY; 

     // find angle 
     double theta = Math.toDegrees(Math.atan( (dY/dX))); 

     // find length of line ship centre - target centre 
     double hypotemuse = dY/Math.sin(theta); 
     System.out.println(hypotemuse); 
     // finally range between ship/target is: 
     float range = (float) (hypotemuse - shipRadius - targetRadius); 
     System.out.println(range); 

     hypotemuse = Math.sqrt(Math.pow(dX,2) + Math.pow(dY,2)); 
     System.out.println(hypotemuse); 
     range = (float) (hypotemuse - shipRadius - targetRadius); 
     System.out.println(range); 
} 

私が得た答えはした 4.700885452542996

1.7008854

5.656854249492381

2.6568542

今sqrtのものは、より正確なものとの値の差があるようです。 639.3204215458475、 610.32043、 -

public static void main(String[] args) throws Exception { 
    // TODO Auto-generated method stub 
    long lStartTime = new Date().getTime(); //start time 
    double shipX = 555; 
     double shipY = 555; 
     double targetX = 11; 
     double targetY = 11; 
     int shipRadius = 26; 
     int targetRadius = 3; 

     //get the difference in locations: 
     double dX = shipX - targetX; 
     double dY = shipY - targetY; 

     // find angle 
     double theta = Math.toDegrees(Math.atan( (dY/dX))); 

     // find length of line ship centre - target centre 
     double hypotemuse = dY/Math.sin(theta); 
     System.out.println(hypotemuse); 
     // finally range between ship/target is: 
     float range = (float) (hypotemuse - shipRadius - targetRadius); 
     System.out.println(range); 


     long lEndTime = new Date().getTime(); //end time 

      long difference = lEndTime - lStartTime; //check different 

      System.out.println("Elapsed milliseconds: " + difference); 
} 

回答:パフォーマンスアプト話

  1. :私のように出てくる業績の時間を計算し

: コードスニペットを考えてみましょう経過したミリ秒:2

そして、私たちがsqrt roで試してみるとOT 1:

public static void main(String[] args) throws Exception { 
    // TODO Auto-generated method stub 
    long lStartTime = new Date().getTime(); //start time 
    double shipX = 555; 
     double shipY = 555; 
     double targetX = 11; 
     double targetY = 11; 
     int shipRadius = 26; 
     int targetRadius = 3; 

     //get the difference in locations: 
     double dX = shipX - targetX; 
     double dY = shipY - targetY; 

     // find angle 
     double theta = Math.toDegrees(Math.atan( (dY/dX))); 

     // find length of line ship centre - target centre 

     double hypotemuse = Math.sqrt(Math.pow(dX,2) + Math.pow(dY,2)); 
     System.out.println(hypotemuse); 
     float range = (float) (hypotemuse - shipRadius - targetRadius); 
     System.out.println(range); 

     long lEndTime = new Date().getTime(); //end time 

      long difference = lEndTime - lStartTime; //check different 

      System.out.println("Elapsed milliseconds: " + difference); 
} 

回答 - 769.3321779309637、 740.33215、 経過したミリ秒:1

は、今、私たちは違いを確認する場合は2つの答えの違いも巨大です。

私はあなたがゲームをより正確にすると、データがより楽しくなると言いますが、それはユーザーのためになるはずです。

+0

テストのおかげで、答えの違いを見て興味深い。正確さとは対照的に、私にとっては正確さはあまり重要ではありません。 (私はそれらが正しい方法で丸められたと思う)いずれにしても、将来の参照のためにそれを念頭に置いておく。 – kiman

1

"ハード"ジオメトリソフトウェアでは、通常sqrtで問題が発生しますが、パフォーマンスは低下しますが、精度は低下します。あなたの場合、sqrtはうまく法案に適合します。

sqrtが本当にパフォーマンスペナルティをもたらしていることが分かっている場合は、必要なときにのみ最適化します。線形近似で試すことができます。

f(x) ~ f(X0) + f'(x0) * (x - x0) 
sqrt(x) ~ sqrt(x0) + 1/(2*sqrt(x0)) * (x - x0) 

だから、あなたはxを与えsqrtと、のためのルックアップテーブル(LUT)を計算し、最も近いx0使用しています。もちろん、通常のコンピューティングに後退しなければならないときに可能な範囲が制限されます。さて、いくつかのコード。

class MyMath{ 
    private static double[] lut; 
    private static final LUT_SIZE = 101; 
    static { 
     lut = new double[LUT_SIZE]; 
     for (int i=0; i < LUT_SIZE; i++){ 
      lut[i] = Math.sqrt(i); 
     } 
    } 
    public static double sqrt(final double x){ 
     int i = Math.round(x); 
     if (i < 0) 
      throw new ArithmeticException("Invalid argument for sqrt: x < 0"); 
     else if (i >= LUT_SIZE) 
      return Math.sqrt(x); 
     else 
      return lut[i] + 1.0/(2*lut[i]) * (x - i); 
    } 
} 

はまた、すべてこれを書いた後、おそらくそこに、すでにいくつかのおおよその、効率的で、代替の数学ライブラリがある(私はこのコードをテストしていない、赦し、すべてのエラーを修正してください)。あなたはそれを探すべきですが、パフォーマンスが本当に必要であることがわかった場合はのみです。

+0

Hmの場合、 'x <0'でも' Math.round(x)> = 0'でも答えが得られます。 –

+0

私はあなたの助言に従い、まだ最適化についてあまり心配しないと思います。最終的に私のゲームを携帯に移植したいので、私はそれを最適化しなければならないと思う。その点では、線形近似とルックアップテーブルがおそらく答えになります。 – kiman

関連する問題