Подтвердить что ты не робот

Поиск наименьшего и самого большого значения в NSArray NSNumbers

Какой эффективный и отличный способ сравнить все значения NSArray, содержащие NSNumbers от floats, чтобы найти самый большой и самый маленький?

Любые идеи, как сделать это красиво и быстро в objective-c?

4b9b3361

Ответ 1

Если скорость выполнения (не скорость программирования) важна, то явный цикл является самым быстрым. Я провел следующие тесты с массивом из 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: кодирование с ключом, используя "self": ​​

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 случайных чисел, а затем применяет все сортировки методы для одного и того же массива. Вышеуказанные тайминги являются результатом одного запуска, но я делаю около 20 прогонов с очень похожими результатами в каждом прогоне. Я также изменил порядок, в котором применяются 5 методов сортировки для исключения эффектов кеширования.

Обновление: Я создал (надеюсь) лучшую тестовую программу. Полный исходный код находится здесь: https://gist.github.com/anonymous/5356982. Среднее время для сортировки массив из 1000000 случайных чисел (в секундах, на 3,1 ГГц Core i5 iMac, выпуск компиляции):

Sorting      1.404
KVO1         1.087
KVO2         0.367
Fast enum    0.017
Block enum   0.021

Обновление 2: Как видно, быстрое перечисление выполняется быстрее, чем перечисление блоков (что также указано здесь: http://blog.bignerdranch.com/2337-incremental-arrayification/).

РЕДАКТИРОВАТЬ: Неправильно следующее: я забыл инициализировать объект, используемый как блокировка, как правильно заметил 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;
    }
}];

Сроки здесь

Concurrent enum  0.009

так что это примерно в два раза быстрее, чем быстрое перечисление. Результат, вероятно, не является репрезентативным потому что это зависит от количества доступных потоков. Но интересно все равно! Обратите внимание, что я использовали "самый простой в использовании" метод синхронизации, который может быть не самым быстрым. Забастовкa >

Ответ 2

Сохранить float путем упаковки под NSNumber, затем

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

* Не скомпилирован и не проверен, уже проверен с помощью intValue, не уверен в двойном или float

Ответ 3

сортировать. возьмите первый и последний элементы.

btw: вы не можете хранить float в 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];

Ответ 4

Я соглашаюсь на сортировку массива, затем выбор первого и последнего элементов, но я считаю это решение более элегантным (это также будет работать для не числовых объектов, изменив сравнение внутри блока):

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

Ответ 5

Сделано просто

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

Ответ 6

Любой, кто ищет Swift 3.x -

let arrayOfNumbers = [1.0,4.2,10.3,15.2]
print(arrayOfNumbers.max() ?? 0.0)//Max number
print(arrayOfNumbers.min() ?? 0.0)//Min number