匹配重复项的优化算法

| 我已经编写了一个小型实用程序,用于识别iTunes中的重复曲目。 轨道的实际匹配需要很长时间,我想对其进行优化。 我将曲目数据存储在NSMutableDictionary中,该曲目存储单个曲目数据 由trackID键控的NSMutableDictionaries。这些单独的曲目词典有 至少以下几个键: TrackID 名称 艺术家 持续时间(以毫厘####。####) 要确定是否有任何曲目彼此匹配,我必须检查: 如果两条音轨的持续时间相距不超过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个小时才能运行,而现在 四核。那仍然太慢了。但是(在这里超过我的薪水等级) 也许有一种方法可以将我需要的所有值存储在“适合堆栈”的C结构中,这样我就不必从速度较慢的内存中获取值。对于我来说,这似乎太低级了,但是如果我有一个例子,我愿意学习。 顺便说一句,我在乐器中对此进行了介绍,ѭ1占了 CPU时间的86.6%。 然后我想我应该将所有持续时间提取到一个排序的数组中,这样我就不会 在字典中查找持续时间值。我觉得那很好 这个想法,但是当我开始执行它时,我想知道如何确定 最佳批次大小。 如果我有以下持续时间:
    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款转到优化解决方案? 我搜索答案的时间超过了我愿意承认的时间,因此发现 这些有趣但无济于事的答案: 查找重复 查找排序数组中的所有重复项和缺失值 感谢您的任何帮助,您可以提供, 长矛     
已邀请:
我的第一个想法是将一些排序的集合作为索引保留在字典中,这样您就可以停止进行O(n ^ 2)搜索,将每个轨道与其他轨道进行比较。 如果您具有按持续时间排序的TrackID数组,那么对于任何轨道,您都可以进行更有效的O(log n)二进制搜索,以找到持续时间在5秒公差内的轨道。 对于艺术家和名称来说,甚至更好,您可以存储以艺术家或曲目名称为键,其值是TrackID数组的字典。然后,您只需要进行O(1)查找即可获取特定艺术家的曲目集,这将使您能够非常迅速地确定是否存在任何可能的重复项。 最后,如果您已经建立了TrackID标题的字典,那么您就可以浏览其所有键,并且仅当有多个具有相同标题的音轨时才搜索重复项。仅当存在多个具有相同标题的曲目时才进行进一步比较,这将消除库的很大一部分并大大减少您的搜索时间(构建字典的时间降至O(n),在最坏的情况下,可以减少另一个O(n)的搜索时间)重复项仍将您留在O(n),而不是您现在拥有的O(n ^ 2)。 如果没有其他事情可以做最后的优化,那么对于没有大量重复项的库而言,所带来的性能提升应该是巨大的:
NSMutableArray *possibleDuplicates = [NSMutableArray array];
NSMutableDictionary *knownTitles = [NSMutableDictionary dictionary];
for (NSMutableDictionary *track in [tracks allKeys]) {
    if ([knownTitles objectForKey:[track objectForKey:@\"title\"]] != nil) {
        [possibleDuplicates addObject:track];
    }
    else {
        [knownTitles addObject:[track objectForKey:@\"TrackID\"] forKey:[track objectForKey:@\"title\"]];
    }
}
//check for duplicates of the tracks in possibleDuplicates only.
    
有几种方法可以做到这一点,但这是我的首次天真猜测: 拥有可变的字典。 该词典中的键是歌曲的名称。 每个键的值是另一个可变字典。 该次可变字典的关键字是美工。 每个键的值是可变的歌曲数组。 您最终将得到以下内容:
NSArray *songs = ...; //your array of songs
NSMutableDictionary *nameCache = [NSMutableDictionary dictionary];

for (Song *song in songs) {
  NSString *name = [song name];
  NSMutableDictionary *artistCache = [nameCache objectForKey:name];
  if (artistCache == nil) {
    artistCache = [NSMutableDictionary dictionary];
    [nameCache setObject:artistCache forKey:name];
  }

  NSString *artist = [song artist];
  NSMutableArray *songCache = [artistCache objectForKey:artist];
  if (songCache == nil) {
    songCache = [NSMutableArray array];
    [artistCache setObject:songCache forKey:artist];
  }

  for (Song *otherSong in songCache) {
    //these are songs that have the same name and artist
    NSTimeInterval myDuration = [song duration];
    NSTimeInterval otherDuration = [otherSong duration];
    if (fabs(myDuration - otherDuration) < 5.0f) {
      //name matches, artist matches, and their difference in duration is less than 5 seconds
    }
  }
  [songCache addObject:song];
}
这是最坏的O(n2)算法(如果每首歌曲的名称,艺术家和时长都相同)。这是最佳情况下的O(n)算法(如果每首歌曲的名称/艺术家/持续时间不同),并且最终会比O(n2)更接近O(n)(最有可能)。     

要回复问题请先登录注册