Encontrar el valor más pequeño y más grande en NSArray de NSNumbers

¿Cuál es una forma efectiva y excelente de comparar todos los valores de NSArray que contienen NSNumbers de floats para encontrar el más grande y el más pequeño?

¿Alguna idea de cómo hacer esto bien y rápido en objective-c ?

Si la velocidad de ejecución (no la velocidad de progtwigción ) es importante, entonces un bucle explícito es el más rápido. Hice las siguientes pruebas con una matriz de 1000000 números aleatorios:

Versión 1: ordenar la matriz:

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

Versión 2: encoding de clave-valor, usando “doubleValue”:

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

Versión 3: encoding de clave-valor, usando “self”:

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

Versión 4: bucle explícito:

 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 

Versión 5: enumeración de bloques:

 __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 

El progtwig de prueba crea una matriz de 1000000 números aleatorios y luego aplica todas las técnicas de clasificación a la misma matriz. Los tiempos anteriores son el resultado de una ejecución, pero realizo aproximadamente 20 ejecuciones con resultados muy similares en cada ejecución. También cambié el orden en que se aplican los 5 métodos de clasificación para excluir los efectos de almacenamiento en caché.

Actualización: ahora he creado un (mejor) mejor progtwig de prueba. El código fuente completo está aquí: https://gist.github.com/anonymous/5356982 . Los tiempos promedio para ordenar una matriz de 1000000 números aleatorios son (en segundos, en un Core i5 iMac de 3,1 GHz, comstackción de versiones):

 Clasificación 1.404
 KVO1 1.087
 KVO2 0.367
 Enum rápido 0.017
 Bloque enum 0.021

Actualización 2: Como se puede ver, la enumeración rápida es más rápida que la enumeración de bloques (que también se indica aquí: http://blog.bignerdranch.com/2337-incremental-arrayification/ ).

EDITAR: Lo siguiente es completamente incorrecto , porque olvidé inicializar el objeto utilizado como locking, como Hot Licks notó correctamente, de modo que no se realiza ninguna sincronización. Y con lock = [[NSObject alloc] init]; la enumeración concurrente es tan lenta que no me atrevo a mostrar el resultado. Tal vez un mecanismo de sincronización más rápido podría ayudar …)

Esto cambia drásticamente si agrega la opción NSEnumerationConcurrent a la enumeración de bloques:

 __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; } }]; 

El tiempo aquí es

 Enum concurrente 0.009

así que es aproximadamente el doble de rápido que la enumeración rápida. El resultado probablemente no sea representativo porque depende del número de subprocesos disponibles. ¡Pero interesante de todos modos! Tenga en cuenta que he utilizado el método de sincronización “más fácil de usar”, que podría no ser el más rápido.

Guarde el flotante envolviendo NSNumber y luego

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

* No comstackdo y verificado, ya verificado con intValue, no estoy seguro acerca de doble o flotar

ordénalo. toma el primer y el último elemento.

Por cierto: no puedes almacenar flotadores en un NSArray, tendrás que envolverlos en objetos 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]; 

Estoy de acuerdo con ordenar la matriz y luego elegir el primer y el último elemento, pero encuentro esta solución más elegante (esto también funcionará para objetos no numéricos cambiando la comparación dentro del bloque):

 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 realmente quieres ser elegante y tienes una lista realmente larga y no quieres bloquear tu hilo principal, esto debería funcionar:

  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); 

Hecho simple

 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);