2013-02-24 2 views
5

Angenommen, ich habe eine Klasse.net Distinct() und komplex conditons

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

Jetzt mag ich Duplikate in Liste solchen Audio der durch Ähnlichkeit filtern (nicht Exact Match) Zustand. Grundlegend ist die Levendein-Distanz mit der Schwellwertkorrektur durch die Gesamtlänge der Saite. Das Problem ist, dass ein allgemeiner Hinweis zu IEqualityComparer lautet: "Implementiere immer GetHashCode und Compare". In GetHashCode kann ich den Abstand zwischen den Strings natürlich nicht berechnen, da es sich überhaupt nicht um eine Vergleichsmethode handelt. In diesem Fall werden jedoch ähnliche Audiodaten unterschiedliche Hashwerte zurückgeben und Distinct() behandelt sie als unterschiedliche Objekte und die compare() -Methode wird nicht ausgelöst.

Ich habe versucht, GetHashCode immer zu zwingen, gibt 0 zurück, also Compare für jedes-zu-jedem Objekt in der Sammlung aufgerufen, aber das ist langsam. So, endlich, eine Frage: Gibt es irgendetwas, was ich tun kann mit .net aus der Box oder sollte ich einen guten Algorithmus für die Filterung suchen?

+8

Ich denke, Sie missbrauchen 'Distinct' hier. Zum Beispiel können Sie "ab" als ein Duplikat von "bc" und "bc" als ein Duplikat von "cd" betrachten, aber Sie würden "ab" nicht als ein Duplikat von "cd" betrachten. Dies macht "Distinct" nicht für Sie arbeiten. – Gabe

+0

Danke, Gabe, ich habe nicht darüber nachgedacht. Ich sehe, ich sollte gerade ein gutes Buch über Suchalgorithmen lesen. – Tommi

+0

Wenn Sie eine statische, lange Liste von Objekten haben - werfen Sie einen Blick auf BK Trees, sie können Ihnen sehr dabei helfen, was Sie erreichen wollen. Ich habe einmal die Implementierung in F # geschrieben, es ist ziemlich brauchbar für dein Ziel. Sie können jedes Objekt darin speichern, vergleichen Sie es mit Levenshtein auf einer beliebigen Eigenschaft mit der Selektorfunktion. Wenn Sie interessiert sind, kann ich Code auf bitbucket hochladen. – rkrahl

Antwort

3

würde ich (in erster Linie) legen nahe, nicht Distinct oder GetHashCode verwenden.

GetHashCode ist zu streng für Ihren Fall (wie @Gabe tadellos darauf hingewiesen). Was Sie tun können ist:

  1. zugeben, dass Sie ein ganzes Dreieck (O (n^2) Komplexität) von Instanzen Paare unter Verwendung der Levenshtein
  2. Versuche zu optimieren müssen vergleichen, die jeden Trick in der Verwendung Buch: Wie berechnet sich die Levenshtein Abstand von der leeren Zeichenfolge zum aktuellen Sound (das ist für jede Instanz von Audio und wahrscheinlich für beide String-Eigenschaften getrennt)?

, dass bis Ende könnte (man könnte sagen) mit einem verflixt gut GetHashCode. Aber man kann es nicht verwenden, wie ein GetHashCode, man sollte es eher wie so verwenden:

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; 
} 

Und dann würden Sie mit einem besseren oder schlechteren Algorithmus enden. Das war nur eine Idee und natürlich: Du kannst Distinct() nicht benutzen. Wenn Sie möchten, können Sie eine eigene Erweiterungsmethode schreiben, damit das Ganze aus der Sicht eines Anwenders gut aussieht.

Und ja der AbsoluteQuasiLevenshtein würde für Dinge wie gleich sein: „ab“ und „zy“, aber es wäre sehr zwischen „ab“ und „blahblahblahblah“ unterscheiden und zumindest würden Sie die Dinge ein wenig optimieren. (Die GetHashCode + Distinct Ansatz stellte ein zusätzliches Problem - die Strenge GetHashCode).

+0

Ich verstehe Ihren Standpunkt. Ich nehme an, am einfachsten 'AbsoluteQuasiLevenshtein' ist eine String-Länge? – Tommi

+0

In der Tat. Und wenn nicht, liegt es an dir, einen besseren zu finden (speziell für deinen Fall). Und wenn Sie Erfolg haben, teilen Sie bitte :) –

1

-Code für BKTree, mit einfachen "C# Interoperabilität" Schicht und Beispiel in C# ist hier:

https://bitbucket.org/ptasz3k/bktree

Es ist VS 2012-Lösung.

Sie beginnen mit dem Erstellen von Baum aus all Ihren Objekten, Übergabe Selektor-Funktion (x => x.Key.ToLowerInvariant() in Beispiel), dann suchen Sie nach einem gegebenen Schlüssel und Levenshtein Abstand und Baum liefert alle übereinstimmenden Objekte.

Also, wenn ich Ihr Problem richtig verstehen:

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()); 

Hoffnung, das hilft.

+0

Sieht gut aus, ich werde es bald versuchen, danke. – Tommi

+0

Wenn Sie einen Blick auf f # Code werfen, werden Sie bemerken, dass Sie bk tree mit Ihrer eigenen Abstandsfunktion 'key -> int (oder jedem numerischen Typ, der einen Vergleich durchführt, um genauer zu sein) parametrisieren können, wobei' key 'object_stored sein kann . Ich habe es nicht durch C# erlaubt, aber es ist sehr einfach zu tun. Es gibt jedoch eine Bedingung, und es ist spezifisch für bk-Bäume. Ihre Distanzfunktion muss metrisch sein. Ich denke, es wird in Ihrem Fall schwer sein, Ihre benutzerdefinierte Funktion formal zu beweisen. Entschuldigung, dass ich nicht mehr helfen konnte. Viel Glück auf Ihrer Suche und geben Sie einige Informationen, wenn Sie es beenden! – rkrahl