在排序的NSArray中查找最近的较低数字

时间:2014-04-14 04:21:55

标签: ios objective-c arrays

我有一个排序的次数如此 [0.0,1.2,4.3,5.9,7.2,8.0]
在播放音频文件时,我希望能够获取当前时间并找到数组中最接近的较低数字。

我的方法是遍历数组,可能以相反的顺序,因为它感觉应该更快。有没有更好的办法?

播放应该是线性的,但可能会快速转发/重绕,所以我想提出一个考虑到这一点的解决方案,但我不确定如何解决问题。

3 个答案:

答案 0 :(得分:5)

您要查找的方法是-[NSArray indexOfObject:inSortedRange:options:usingComparator:]。它执行二进制搜索。使用options:NSBinarySearchingInsertionIndex选项,如果未找到该值,则返回将插入对象的索引,即最小元素的索引或数组中项的计数。

NSTimeInterval currentTime = ...;
NSUInteger index = [times indexOfObject:@(currentTime)
    inSortedRange:NSMakeRange(0, times.count)
    options:NSBinarySearchingInsertionIndex
    usingComparator:^(id object0, id object1) {
        NSTimeInterval time0 = [object0 doubleValue];
        NSTimeInterval time1 = [object1 doubleValue];
        if (time0 < time1) return NSOrderedAscending;
        else if (time0 > time1) return NSOrderedDescending;
        else return NSOrderedSame;
    }];
// If currentTime was not found exactly, then index is the next larger element
// or array count..
if (index == times.count || [times[index] doubleValue] > currentTime) {
    --index;
}

答案 1 :(得分:3)

在排序数组中查找内容的最快 * 方式是二分搜索:如果有n个项,请检查索引n / 2处的元素。如果该元素大于您要查找的元素,请检查索引为n / 4的元素;否则,如果它小于您要查找的内容,请检查索引为3n / 4的元素。继续以这种方式细分,直到找到你想要的东西,即当前时间 的位置。然后你可以选择前面的元素,因为它是最接近当前时间的元素。

但是,一旦您完成了一次,您就可以跟踪列表中的位置。当用户播放文件时,请继续检查时间是否已经过了下一个元素,依此类推。换句话说,记住你在哪里,并在你再次检查时使用它。如果用户倒带,请检查前面的元素。

* 可以说,这并不是严格正确的 - 如果能够对所讨论元素的可能位置做出好的猜测,肯定会有更快的方法。但是如果你不知道除了元素出现在数组中某个地方之外的任何东西,它通常是正确的方法。

答案 2 :(得分:1)

我不确定这是否是最佳方法,但我认为它可以完成工作(假设您的数组总是按升序排列)。

- (NSNumber *) incrementalClosestLowestNumberForNumber:(NSNumber *)aNumber inArray:(NSArray *)array {

    for (int i = 0; i < array.count; i++) {

        if ([array[i] floatValue] == [aNumber floatValue]) {
            return aNumber;
        }
        else if ([array[i] floatValue] > [aNumber floatValue]) {
            int index = (i > 0) ? i - 1 : 0;
            return array[index];
        }
    }

    return @0;
}

然后这样称呼:

NSArray * numbArray = @[@0.0, @1.2, @4.3, @5.9, @7.2, @8.0];

NSNumber * closestNumber = [self closestLowestNumberForNumber:@2.4 inArray:numbArray];
NSLog(@"closest number: %@", closestNumber);

我不确定别人是否知道一种更快的特殊方式。

根据其他一些答案/评论,我想出了这个,也许其中一个人可以指出整体是否在某个地方。

- (NSNumber *) quartalClosestLowestNumberForNumber:(NSNumber *)compareNumber inArray:(NSArray *)array {

    int low = 0;
    int high = array.count - 1;

    NSNumber * lastNumber;
    int currentIndex = 0;
    for (currentIndex = low + (high - low) / 2; low <= high; currentIndex = low + (high - low) / 2) {
        NSNumber * numb = array[currentIndex];

        if (numb.floatValue < compareNumber.floatValue) {
            low = currentIndex + 1;
        }
        else if (numb.floatValue > compareNumber.floatValue) {
            high = currentIndex - 1;
        }
        else if (numb.floatValue == compareNumber.floatValue) {
            return numb;
        }

        lastNumber = numb;

    }
    if (lastNumber.floatValue > compareNumber.floatValue && currentIndex != 0) {
        lastNumber = array[currentIndex - 1];
    }

    return lastNumber;
}

我现在真的很无聊,所以我正在尝试测试最快的方法。我就是这样做的。

NSMutableArray * numbersArray = [NSMutableArray new];
for (int i = 0; i < 100000; i++) {
    float floater = i / 100.0;
    [numbersArray addObject: @(floater)];
}

// courtesy @RobMayoff
NSDate * binaryDate = [NSDate date];
NSNumber * closestNumberBinary = [self binaryClosestLowestNumberForNumber:@4.4 inArray:numbersArray];
NSLog(@"Found closest number binary: %@ in: %f seconds", closestNumberBinary, -[binaryDate timeIntervalSinceNow]);

// The Quartal Version
NSDate * quartalDate = [NSDate date];
NSNumber * closestNumberQuartal = [self quartalClosestLowestNumberForNumber:@4.4 inArray:numbersArray];
NSLog(@"Found closest number quartal: %@ in: %f seconds", closestNumberQuartal, -[quartalDate timeIntervalSinceNow]);

// The incremental version
NSDate * incrementalDate = [NSDate date];
NSNumber * closestNumberIncremental = [self incrementalClosestLowestNumberForNumber:@4.4 inArray:numbersArray];
NSLog(@"Found closest number incremental: %@ in: %f seconds", closestNumberIncremental, -[incrementalDate timeIntervalSinceNow]);

这是输出:

  

找到最接近的二进制数:4.4英寸:0.000030秒

     

找到最接近的数字四分之一:4.4英寸:0.000015秒

     

找到最接近的数字增量:4.4英寸:0.000092秒

另一个测试用例:

  

找到最接近的二进制数:751.48英寸:0.000030秒

     

找到最接近的数字四分位数:751.48英寸:0.000016秒

     

找到最接近的数字增量:751.48英寸:0.013042秒