Quelle est une manière efficace et efficace de comparer toutes les valeurs de NSArray
qui contiennent NSNumbers
de floats
pour trouver la plus grande et la plus petite?
Des idées sur la façon de faire ça Nice et rapide dans Objective-C?
Si vitesse d'exécution (pas vitesse de programmation) est important, alors une boucle explicite est la plus rapide. J'ai fait les tests suivants avec un tableau de 1000000 nombres aléatoires:
Version 1: triez le tableau:
NSArray *sorted1 = [numbers sortedArrayUsingSelector:@selector(compare:)];
// 1.585 seconds
Version 2: codage valeur-clé, en utilisant "doubleValue":
NSNumber *max=[numbers valueForKeyPath:@"@max.doubleValue"];
NSNumber *min=[numbers valueForKeyPath:@"@min.doubleValue"];
// 0.778 seconds
Version 3: codage valeur-clé, en utilisant "self":
NSNumber *max=[numbers valueForKeyPath:@"@max.self"];
NSNumber *min=[numbers valueForKeyPath:@"@min.self"];
// 0.390 seconds
Version 4: Boucle explicite:
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
Version 5: énumération de blocs:
__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
Le programme de test crée un tableau de 1000000 nombres aléatoires, puis applique toutes les techniques de tri au même tableau. Les timings ci-dessus sont la sortie d'un cycle, mais je fais environ 20 cycles avec des résultats très similaires dans chaque cycle. J'ai également changé l'ordre dans lequel les 5 méthodes de tri sont appliquées pour exclure les effets de mise en cache.
Mise à jour: J'ai maintenant créé un (si tout va bien) meilleur programme de test. Le code source complet est ici: https://Gist.github.com/anonymous/5356982 . Les délais moyens pour trier un tableau de 1000000 nombres aléatoires sont (en secondes, sur un iMac Core i5 à 3,1 GHz, compilation):
Tri 1,404 KVO1 1,087 KVO2 0,367 Énumération rapide 0,017 Énumération par blocs 0,021
Mise à jour 2: Comme on peut le voir, l'énumération rapide est plus rapide que l'énumération par blocs (qui est également indiquée ici: http: // blog. bignerdranch.com/2337-incremental-arrayification/ ).
EDIT: Ce qui suit est complètement faux, parce que j'ai oublié d'initialiser l'objet utilisé comme verrou, comme Hot Licks correctement remarqué, de sorte qu'aucune synchronisation n'est effectuée du tout. Et avec lock = [[NSObject alloc] init];
l'énumération simultanée est si lente que je n'ose pas montrer le résultat. Peut-être qu'un mécanisme de synchronisation plus rapide pourrait aider ...)
Cela change radicalement si vous ajoutez l'option NSEnumerationConcurrent
à l'énumération des blocs:
__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; } }];
Le moment est ici
Énumération simultanée 0,009
il est donc environ deux fois plus rapide que l'énumération rapide. Le résultat n'est probablement pas représentatif car il dépend du nombre de threads disponibles. Mais intéressant quand même! Notez que j'ai utilisé la méthode de synchronisation "la plus facile à utiliser", qui n'est peut-être pas la plus rapide.
Enregistrer le flotteur en encapsulant sous NSNumber puis
NSNumber *max=[numberArray valueForKeyPath:@"@max.doubleValue"];
NSNumber *min=[numberArray valueForKeyPath:@"@min.doubleValue"];
* Non compilé et vérifié, déjà vérifié avec intValue, pas sûr du double ou du float
trier. prenez le premier et le dernier élément.
btw: vous ne pouvez pas stocker de flottants dans un NSArray, vous devrez les encapsuler dans des objets 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];
Je suis d'accord pour trier le tableau puis choisir les premier et dernier éléments, mais je trouve cette solution plus élégante (cela fonctionnera également pour les objets non numériques en modifiant la comparaison à l'intérieur du bloc):
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];
}];
Si vous voulez vraiment avoir de la fantaisie et avoir une liste vraiment longue et que vous ne voulez pas bloquer votre thread principal, cela devrait fonctionner:
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);
Simplifié
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);