2013-02-24 2 views
5

Предположим, у меня есть класс.net Четкие() и сложные условия использования

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

Теперь я хочу, чтобы отфильтровать дубликаты в списке таких аудио-х по сходству (не Четк.совп) условия. В основном это расстояние Левенштейна с коррекцией treshold по общей длине строки. Проблема в том, что общий совет о IEqualityComparer - «Всегда применять как GetHashCode, так и Compare». Я obviuosly не могу рассчитать расстояние между строками в GetHashCode, потому что это не метод сравнения вообще. Однако в этом случае даже аналогичные звуковые сигналы возвращают разные хэши, а Distinct() будут обрабатывать его как разные объекты, а метод compare() не запускается.

Я попытался заставить GetHashCode всегда возвращать 0, поэтому Compare вызывает каждый объект в коллекции, но это медленно. Итак, наконец, вопрос: есть ли что-нибудь, что я могу сделать с .net из коробки, или мне нужно найти хороший алгоритм фильтрации?

+8

Я думаю, что вы можете злоупотреблять «Distinct» здесь. Например, вы можете рассматривать 'ab' как дубликат' bc' и 'bc' как дубликата' cd', но вы не считаете 'ab' дубликат' cd'. Это делает «Distinct» не работает для вас. – Gabe

+0

Спасибо, Гейб, я об этом не думал. Я вижу, я должен просто прочитать хорошую книгу об алгоритмах поиска. – Tommi

+0

Если у вас есть статический длинный список объектов - взгляните на деревья BK, они могут помочь вам в том, что вы пытаетесь выполнить. Я написал реализацию в F # один раз, это вполне применимо для вашей цели. Вы можете сохранить в нем любой объект, сравнить его с levenshtein на любом свойстве с помощью функции выбора. Если вам интересно, я могу загрузить код в битбакет. – rkrahl

ответ

3

Я хотел бы предложить (в первую очередь) не используется Distinct или GetHashCode.

GetHashCode слишком строг для вашего случая (как @Gabe указал отлично). Что вы можете сделать, это:

  1. Признайтесь, что вы должны сравнить весь треугольник (O (N^2) сложность) экземпляры пара с помощью Левенштейн
  2. Try оптимизировать, что использование каждого трюка в книга: Как вычисляется расстояние Левенштейна от пустой строки до текущей звук (то есть для каждого экземпляра аудио и, возможно, отдельно для обоих свойств строки)?

Это может закончиться (можно сказать) с проклятой 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» и «З.Ы.», но это будет значительно отличаться от «AB» и «blahblahblahblah» и, по крайней мере, вы бы оптимизировать вещи немного. (GetHashCode + Отличительный подход поставил дополнительную проблему - строгость GetHashCode).

+0

Получаю ваше мнение. Я полагаю, что самый простой 'AbsoluteQuasiLevenshtein' - длина строки? – Tommi

+0

Действительно. И если нет, то вам решать, что лучше выбрать (особенно для вашего случая). И если вам удастся, пожалуйста, поделитесь :) –

1

Код для BKTree с простым слоем "C# совместимости" и, например, в C# здесь:

https://bitbucket.org/ptasz3k/bktree

Это VS 2012 решения.

Вы начинаете с построения дерева из всех ваших объектов, передавая функцию селектора (x => 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()); 

Надеется, что это помогает.

+0

Выглядит неплохо, я попробую скоро, спасибо. – Tommi

+0

Если вы посмотрите на код f #, вы заметите, что вы можете параметризовать bk-дерево с помощью вашей собственной функции «key» -> int (или любого другого типа, реализующего сравнение, если быть более конкретным), где «ключ может быть» object_stored , Я не допустил этого через C#, но это очень легко сделать. Однако есть одно условие, и оно специфично для bk-деревьев. Функция расстояния должна быть метрической. Я думаю, вам будет сложно официально подтвердить, что ваша пользовательская функция. Извините, что больше не могу помочь. Удачи в вашем квесте и дать некоторую информацию, когда вы ее закончите! – rkrahl

Смежные вопросы