我有一个排序的次数如此
[0.0,1.2,4.3,5.9,7.2,8.0]
在播放音频文件时,我希望能够获取当前时间并找到数组中最接近的较低数字。
我的方法是遍历数组,可能以相反的顺序,因为它感觉应该更快。有没有更好的办法?
播放应该是线性的,但可能会快速转发/重绕,所以我想提出一个考虑到这一点的解决方案,但我不确定如何解决问题。
答案 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秒