我编写了一个小型实用程序来识别 iTunes 中的重复曲目。曲目的实际匹配需要很长时间,我想对其进行优化。我将轨道数据存储在 NSMutableDictionary 中,该 NSMutableDictionary 将单个轨道数据存储在由 trackID 键入的 NSMutableDictionaries 中。这些单独的曲目词典至少具有以下键:
- 跟踪ID
- 姓名
- 艺术家
- 持续时间(以毫秒为单位####.####)
要确定是否有任何曲目相互匹配,我必须检查:
- 如果两首曲目的时长在 5 秒内
- 名称匹配
- 艺人比赛
我这样做的缓慢方法是使用两个 for 循环:
-(void)findDuplicateTracks {
NSArray *allTracks = [tracks allValues];
BOOL isMatch = NO;
int numMatches = 0;
// outer loop
NSMutableDictionary *track = nil;
NSMutableDictionary *otherTrack = nil;
for (int i = 0; i < [allTracks count]; i++) {
track = [allTracks objectAtIndex:i];
NSDictionary *summary = nil;
if (![claimedTracks containsObject:track]) {
NSAutoreleasePool *aPool = [[NSAutoreleasePool alloc] init];
NSUInteger duration1 = (NSUInteger) [track objectForKey:kTotalTime];
NSString *nName = [track objectForKey:knName];
NSString *nArtist = [track objectForKey:knArtist];
// inner loop - no need to check tracks that have
// already appeared in i
for (int j = i + 1; j < [allTracks count]; j++) {
otherTrack = [allTracks objectAtIndex:j];
if (![claimedTracks containsObject:otherTrack]) {
NSUInteger duration2 = (NSUInteger)[otherTrack objectForKey:kTotalTime];
// duration check
isMatch = (abs(duration1 - duration2) < kDurationThreshold);
// match name
if (isMatch) {
NSString *onName = [otherTrack objectForKey:knName];
isMatch = [nName isEqualToString:onName];
}
// match artist
if (isMatch) {
NSString *onArtist = [otherTrack objectForKey:knArtist];
isMatch = [nArtist isEqualToString:onArtist];
}
// save match data
if (isMatch) {
++numMatches;
// claim both tracks
[claimedTracks addObject:track];
[claimedTracks addObject:otherTrack];
if (![summary isMemberOfClass:[NSDictionary class]]) {
[track setObject:[NSNumber numberWithBool:NO] forKey:@"willDelete"];
summary = [self dictionarySummaryForTrack:track];
}
[otherTrack setObject:[NSNumber numberWithBool:NO] forKey:@"willDelete"];
[[summary objectForKey:kMatches]
addObject:otherTrack];
}
}
}
[aPool drain];
}
}
}
这对于大型音乐库来说变得非常慢,并且只使用 1 个处理器。一项推荐的优化是使用块并批量处理轨道(100 条轨道)。我试过了。如果我的代码最初需要 9 个小时才能运行,那么现在在四核上大约需要 2 个小时。那还是太慢了。但是(在这里谈论我的工资等级)也许有一种方法可以将我需要的所有值存储在“适合堆栈”的 C 结构中,然后我就不必从较慢的内存中获取值。这对我来说似乎太低级了,但如果我有一个例子,我愿意学习。
顺便说一句,我在 Instruments 中对此进行了分析,[NSCFSet member:]
占用了 86.6% 的 CPU 时间。
然后我想我应该将所有持续时间提取到一个排序数组中,这样我就不必在字典中查找持续时间值。我认为这是一个好主意,但是当我开始实施它时,我想知道如何确定最佳批量大小。
如果我有以下持续时间:
2 2 3 4 5 6 6 16 17 38 59 Duration
0 1 2 3 4 5 6 7 8 9 10 Index
然后只需遍历数组,我就知道要在索引 0 处找到歌曲的匹配曲目,我只需要将其与索引 6 之前的歌曲进行比较。太好了,我有了第一批。但是现在我必须从索引 1 重新开始,才发现它的批处理也应该在索引 6 处停止并排除索引 0。我假设我在这里浪费了很多处理周期来确定批处理应该是什么/持续时间火柴。这似乎是一个“集合”问题,但在我的算法介绍课上我们并没有做太多。
我的问题是:
1) 识别匹配曲目的最有效方法是什么?是不是和上面的类似?它是否使用略高于我的知识水平的不相交和 [统一] 集合操作?它是使用 NSArray 过滤数组吗?是否有描述此问题和解决方案的在线资源?
我愿意以最有效的方式(数据结构)重组曲目字典。起初我以为我需要通过 TrackID 执行许多查找,但现在不再如此。
2)有没有更有效的方法来解决这个问题?您如何摇滚明星从第 1 段到优化的解决方案?
我寻找答案的时间比我愿意承认的要长,并发现了这些有趣但无益的答案:
感谢您提供的任何帮助,兰斯