web-dev-qa-db-ja.com

NSNumbersのNSArrayで最小値と最大値を見つける

NSArrayNSNumbersを含むfloatsのすべての値を比較して、最大値と最小値を見つけるための効果的で素晴らしい方法は何ですか?

これをObjective-Cですばやく簡単に行う方法はありますか?

47

実行速度プログラミング速度ではない)が重要な場合、明示的なループが最速です。 1000000の乱数の配列で次のテストを行いました。

バージョン1:配列の並べ替え:

NSArray *sorted1 = [numbers sortedArrayUsingSelector:@selector(compare:)];
// 1.585 seconds

バージョン2:「doubleValue」を使用したキー値コーディング:

NSNumber *max=[numbers valueForKeyPath:@"@max.doubleValue"];
NSNumber *min=[numbers valueForKeyPath:@"@min.doubleValue"];
// 0.778 seconds

バージョン3:「自己」を使用したキー値コーディング:

NSNumber *max=[numbers valueForKeyPath:@"@max.self"];
NSNumber *min=[numbers valueForKeyPath:@"@min.self"];
// 0.390 seconds

バージョン4:明示的なループ:

float xmax = -MAXFLOAT;
float xmin = MAXFLOAT;
for (NSNumber *num in numbers) {
    float x = num.floatValue;
    if (x < xmin) xmin = x;
    if (x > xmax) xmax = x;
}
// 0.019 seconds

バージョン5:ブロック列挙:

__block float xmax = -MAXFLOAT;
__block float xmin = MAXFLOAT;
[numbers enumerateObjectsUsingBlock:^(NSNumber *num, NSUInteger idx, BOOL *stop) {
    float x = num.floatValue;
    if (x < xmin) xmin = x;
    if (x > xmax) xmax = x;
}];
// 0.024 seconds

テストプログラムは、1000000個の乱数の配列を作成し、すべての並べ替え手法を同じ配列に適用します。上記のタイミングは1回の実行の出力ですが、各実行で非常によく似た結果で約20回実行します。キャッシュ効果を除外するために、5つの並べ替え方法が適用される順序も変更しました。

更新:私は今、(うまくいけば)より良いテストプログラムを作成しました。完全なソースコードは次のとおりです。 https://Gist.github.com/anonymous/5356982 。 1000000個の乱数の配列を並べ替える平均時間は次のとおりです(3.1 GHz Core i5 iMacでは、リリースコンパイル)。

並べ替え1.404 
 KVO1 1.087 
 KVO2 0.367 
高速列挙0.017 
ブロック列挙0.021 

更新2:ご覧のように、高速列挙はブロック列挙よりも高速です(こちらにも記載されています: http:// blog。 bignerdranch.com/2337-incremental-arrayification/ )。

EDIT:以下は、完全に間違っています。ロックとして使用されるオブジェクトを初期化するのを忘れたためです。 Hot Licksは正しく認識されたため、同期はまったく行われません。 lock = [[NSObject alloc] init];同時列挙はとても遅いので、結果を表示しないことを敢えてする。おそらく、より高速な同期メカニズムが役立つかもしれません...)

NSEnumerationConcurrentオプションをブロック列挙に追加すると、これは劇的に変わります。

__block float xmax = -MAXFLOAT;
__block float xmin = MAXFLOAT;
id lock;
[numbers enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(NSNumber *num, NSUInteger idx, BOOL *stop) {
    float x = num.floatValue;
    @synchronized(lock) {
        if (x < xmin) xmin = x;
        if (x > xmax) xmax = x;
    }
}];

ここでのタイミングは


同時列挙0.009 

そのため、高速列挙の約2倍の速度です。結果は、利用可能なスレッドの数に依存するため、おそらく代表的なものではありません。とにかく面白い! 「最も使いやすい」同期方法を使用していることに注意してください。これは最速ではないかもしれません。

141
Martin R

NSNumberの下にラップしてフロートを保存してから

NSNumber *max=[numberArray valueForKeyPath:@"@max.doubleValue"];
NSNumber *min=[numberArray valueForKeyPath:@"@min.doubleValue"];

* コンパイルおよびチェックされていない、すでにintValueでチェックされている、doubleまたはfloatが不明

14
Anoop Vaidya

それを並べ替えます。最初と最後の要素を取ります。

btw:フロートをNSArrayに保存することはできません。それらをNSNumberオブジェクトでラップする必要があります。

NSArray *numbers = @[@2.1, @8.1, @5.0, @.3];
numbers = [numbers sortedArrayUsingSelector:@selector(compare:)];

float min = [numbers[0] floatValue];
float max = [[numbers lastObject] floatValue];
2
vikingosegundo

配列を並べ替えて最初と最後の要素を選択することに同意しますが、このソリューションはよりエレガントであることがわかります(これは、ブロック内の比較を変更することで非数値オブジェクトでも機能します):

NSArray *unsortedArray = @[@(3), @(5), @(1)];
NSArray *sortedArray = [unsortedArray sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
    NSNumber *item1 = (NSNumber *)obj1;
    NSNumber *item2 = (NSNumber *)obj2;
    return [item1 compare:item2];
}];

あなたが本当に空想を得て、本当に長いリストを持っていて、メインスレッドをブロックしたくない場合、これはうまくいくはずです:

 NSComparator comparison = ^NSComparisonResult(id obj1, id obj2) {
    NSNumber *item1 = (NSNumber *)obj1;
    NSNumber *item2 = (NSNumber *)obj2;
    return [item1 compare:item2];
};

void(^asychSort)(void) = ^
{
    NSArray *sortedArray = [unsortedArray sortedArrayUsingComparator:comparison];
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"Finished Sorting");
        //do your callback here
    });
};

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), asychSort);
0
JonahGabriel

シンプルに

NSArray *numbers = @[@2.1, @8.1, @5.0, @.3];
numbers = [numbers sortedArrayUsingSelector:@selector(compare:)];

float min = [numbers[0] floatValue];
float max = [[numbers lastObject] floatValue];

NSLog(@"MIN%f",min);
NSLog(@"MAX%f",max);
0
Frank