Finding smallest and biggest value in NSArray of NSNumbers

萝らか妹 提交于 2019-12-17 02:42:56

问题


What's an effective and great way to compare all the values of NSArray that contains NSNumbers from floats to find the biggest one and the smallest one?

Any ideas how to do this nice and quick in Objective-C?


回答1:


If execution speed (not programming speed) is important, then an explicit loop is the fastest. I made the following tests with an array of 1000000 random numbers:

Version 1: sort the array:

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

Version 2: Key-value coding, using "doubleValue":

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

Version 3: Key-value coding, using "self":

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

Version 4: Explicit loop:

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: Block enumeration:

__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

The test program creates an array of 1000000 random numbers and then applies all sorting techniques to the same array. The timings above are the output of one run, but I make about 20 runs with very similar results in each run. I also changed the order in which the 5 sorting methods are applied to exclude caching effects.

Update: I have now created a (hopefully) better test program. The full source code is here: https://gist.github.com/anonymous/5356982. The average times for sorting an array of 1000000 random numbers are (in seconds, on an 3.1 GHz Core i5 iMac, release compile):

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

Update 2: As one can see, fast enumeration is faster than block enumeration (which is also stated here: http://blog.bignerdranch.com/2337-incremental-arrayification/).

EDIT: The following is completely wrong, because I forgot to initialize the object used as lock, as Hot Licks correctly noticed, so that no synchronization is done at all. And with lock = [[NSObject alloc] init]; the concurrent enumeration is so slow that I dare not to show the result. Perhaps a faster synchronization mechanism might help ...)

This changes dramatically if you add the NSEnumerationConcurrent option to the block enumeration:

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

The timing here is

Concurrent enum  0.009

so it is about twice as fast as fast enumeration. The result is probably not representative because it depends on the number of threads available. But interesting anyway! Note that I have used the "easiest-to-use" synchronization method, which might not be the fastest.




回答2:


Save float by wrapping under NSNumber then

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

*Not compiled and checked, already checked with intValue, not sure about double or float




回答3:


sort it. take the first and the last element.

btw: you cant store floats in an NSArray, you will need to wrap them in NSNumber objects.

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:


I agree with sorting the array then picking the first and last elements, but I find this solution more elegant (this will also work for non numeric objects by changing the comparison inside the block):

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

If you really want to get fancy and have a really long list and you don't want to block your main thread, this should work:

 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:


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


来源:https://stackoverflow.com/questions/15931112/finding-smallest-and-biggest-value-in-nsarray-of-nsnumbers

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!