.net Distinct()和复杂的条件

时间:2013-02-24 19:10:05

标签: c# .net duplicates distinct

假设我有一个班级

public class Audio
{
    public string artist   { get; set; }
    public string title    { get; set; }
    // etc.
}

现在我想通过相似性(非完全匹配)条件来过滤此类音频列表中的重复项。基本上它是Levenstein距离,通过字符串总长度进行阈值校正。问题是,关于IEqualityComparer的一般提示是“始终实现GetHashCode和Compare”。我无法在GetHashCode中计算字符串之间的距离,因为它根本不是比较方法。但是在这种情况下,即使是类似的音频也会返回不同的哈希值,而Distinct()会将其视为不同的对象而且compare()方法不会被触发。

我试图强制GetHashCode始终返回0,因此Compare调用集合中的每个对象,但这很慢。所以,最后,一个问题:我是否可以使用.net开箱即用,或者我应该搜索一些好的过滤算法?

2 个答案:

答案 0 :(得分:3)

我建议(首先)不要使用 Distinct GetHashCode

GetHashCode 对您的情况过于严格(正如@Gabe指出的那样)。 你能做的是:

  1. 承认您必须使用Levenshtein比较实例对的整个三角形(O(n ^ 2)复杂度)
  2. 尝试使用书中的每一个技巧来优化它:如何计算从空字符串到当前一个声音的 Levenshtein距离(对于每个音频实例,可能是单独的两个字符串属性)?
  3. 这可能最终(有人可能会说)有一个很好的 GetHashCode 。 但你不能像 GetHashCode 那样使用它,你应该像这样使用它:

    bool AreSimilar(Audio me, Audio you) {
      int cheapLevenshtein = Math.Abs(me.AbsoluteQuasiLevenshtein - you.AbsoluteQuasiLevenshtein);
    
      if (cheapLevenshtein < THRESHOLD) {
    
        int expensiveLevenshtein = Audio.LevenshteinBetween(me, you);
        var result = (expensiveLevenshtein < LIMIT);
        return result;
    
      } else
        return false;
    }
    

    然后你会得到更好或更差的算法。这只是一个想法,当然:你不能使用Distinct()。如果您愿意,您可以编写自己的扩展方法,从用户程序员的角度来看整个事情看起来很棒。

    是的 AbsoluteQuasiLevenshtein 对于像“ab”和“zy”这样的东西是相同的但是在“ab”和“blahblahblahblah”之间它会有很大差异,至少你会稍微优化一下。 ( GetHashCode + Distinct 方法带来了额外的问题 - GetHashCode 的严格性。)

答案 1 :(得分:1)

BKTree代码,c#中的简单“c#互操作性”层和示例在这里:

https://bitbucket.org/ptasz3k/bktree

这是VS 2012解决方案。

首先从所有对象构建树,传递选择器函数(例如x =&gt; x.Key.ToLowerInvariant()),然后搜索给定的键和levenshtein距离,树返回所有匹配的对象。

所以,如果我理解你的问题:

var bk = BKTree.CSharp.CreateBK(x => x.artist, audios);
var allArtists = audios.Select(x => x.artist);
var possibleDuplicates = allArtists.Select(x => new 
    { Key = x, Similiar = BKTree.CSharp.FindInBk(bk, x, treshold).ToList());

希望这有帮助。