49

NSArray什么是比较包含NSNumbersfrom的所有值floats以找到最大和最小的有效且出色的方法?

任何想法如何在Objective-C中快速完成这项工作?

4

5 回答 5

146

如果执行速度(不是编程速度)很重要,那么显式循环是最快的。我用一个包含 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 GHz Core i5 iMac 上,发布编译):

排序 1.404
KVO1 1.087
KVO2 0.367
快速枚举 0.017
块枚举 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;
    }
}];

这里的时间是

并发枚举 0.009

所以它的速度大约是快速枚举的两倍。结果可能不具有代表性,因为它取决于可用的线程数。但还是很有趣!请注意,我使用了“最容易使用”的同步方法,这可能不是最快的。

于 2013-04-10T16:43:46.667 回答
14

然后通过包裹在 NSNumber 下保存浮点数

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

*没有编译和检查,已经用intValue检查过,不确定是double还是float

于 2013-04-10T16:20:49.720 回答
2

把它分类。取第一个和最后一个元素。

顺便说一句:您不能将浮点数存储在 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];
于 2013-04-10T16:15:15.930 回答
0

我同意对数组进行排序,然后选择第一个和最后一个元素,但我发现这个解决方案更优雅(通过更改块内的比较,这也适用于非数字对象):

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);
于 2013-04-10T16:56:32.023 回答
0

变得简单

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);
于 2017-01-14T15:09:05.953 回答