Xcodeで2点間の距離を測定するために最適な方法

  • 4
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

ゲームなどでの2点間の距離を測る機会が多いのですが、三平方の定理を使うためのリソースが重荷になりがちです。
そこで、距離の測定にはどの方法が最適なのかを検証してみることにしました。

検証に使ったコードは以下のとおりです。

ViewController.m
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    CGPoint pointA = CGPointMake(100.0f, 100.0f);
    CGPoint pointB = CGPointMake(300.0f, 200.0f);
    NSArray *repeatTimes = @[@(1000),
                             @(10000),
                             @(100000),
                             @(1000000),
                             @(10000000)
                             ];

    for (NSNumber *num in repeatTimes) {
        NSDate *beforeDate = [NSDate date];
        for (long i=0; i<[num intValue]; i++) {
//            [self doNothing:pointA with:pointB];
            [self calcDisntaceA:pointA with:pointB];
//            [self calcDisntaceB:pointA with:pointB];
//            [self calcDisntaceC:pointA with:pointB];
        }
        NSTimeInterval interval =fabs([beforeDate timeIntervalSinceNow]);
        NSLog(@"%d times repeat took %f seconds!", num.intValue, interval);
    }
}

//何も計算しない
-(CGFloat)doNothing:(CGPoint)pointA with:(CGPoint)pointB{
    CGFloat distance;
    return distance;
}

//方法A
-(CGFloat)calcDisntaceA:(CGPoint)pointA with:(CGPoint)pointB{
    CGFloat xDistance = pointA.x - pointB.x;
    CGFloat yDistance = pointA.y - pointB.y;
    CGFloat distance = sqrtf(xDistance*xDistance + yDistance*yDistance);
    return distance;
}

//方法B
-(CGFloat)calcDisntaceB:(CGPoint)pointA with:(CGPoint)pointB{
    CGFloat xDistance = pointA.x - pointB.x;
    CGFloat yDistance = pointA.y - pointB.y;
    CGFloat distance = sqrtf(powf(xDistance, 2) + powf(yDistance, 2));
    return distance;
}

//方法C
-(CGFloat)calcDisntaceC:(CGPoint)pointA with:(CGPoint)pointB{
    CGFloat xDistance = pointA.x - pointB.x;
    CGFloat yDistance = pointA.y - pointB.y;
    CGFloat distance = hypotf(xDistance, yDistance);
    return distance;
}

何も計算を行わないメソッドを用意し、他の方法の所要時間から引くためのバックグラウンドとします。
方法Aは、x軸方向、y軸方向それぞれの距離を掛け合わせて平方根をとります。
方法Bが、C言語の関数関数powfを用いて2乗の計算をします。
方法Cは、C言語の関数hypotfを用いて2つの引数のそれぞれの2条の和の平方根をとります。

なるべく同じ条件で測定できるように、測定は全てiPhone5の実機を用いて行い、ある方法で計算する場合は他の方法をコメントアウトしました。
また、再現性を得るために何度も実行して結果が変わらないことを確認しました。

結果は以下の通りです。

2点間の距離測定の所要時間4.png

方法Aが最も速く、方法Bが最も遅いという結果が得られました。
方法Bではpowfの指数の型が小数のため、内部で複雑な計算が行われているものと考えられます。
方法Cではmath.hのhypotf関数を用いましたが、シンプルな方法Aよりも遅いという結果になりました。これは、以下のリンクにあるように、2乗の計算結果の桁数が大きすぎて精度を損ねることを防ぐための処置が内部に記述されているためと考えられます。
http://en.wikipedia.org/wiki/Hypot

結論: 速度を重要視する場合はシンプルな方法Aが、精度を重要視する場合はC言語標準の関数を用いた方法Cを選択するのがいいかと思います。といっても距離の精度は7桁もあれば大抵の場合十分であり、方法Aは方法Cの半分の所要時間なので、大抵の場合は方法Aを用いるのがいいかと思います。
方法Bは方法Aの10倍弱所要時間がかかるので問題外です。powf関数は距離の測定には用いないほうがいいようです。

2点間の距離 = sqrtf(x軸方向の距離 * x軸方向の距離 + y軸方向の距離 * y軸方向の距離)
というシンプルな計算が大抵の場合はベストかという結論に落ち着きました。