Я думаю, у вас есть несколько хороших ответов на вопрос (1).
Вопрос (2):
Возможно, вы хотите, чтобы косинусное сходство сравнивалось с двумя произвольными наборами n-грамм (чем больше, тем лучше). Это дает вам диапазон от 0,0 до 1,0 без необходимости масштабирования. Страница Википедии дает уравнение , а перевод F # довольно прост:
let cos a b =
let dot = Seq.sum (Seq.map2 ( * ) a b)
let magnitude v = Math.Sqrt (Seq.sum (Seq.map2 ( * ) v v))
dot / (magnitude a * magnitude b)
Для ввода вам нужно выполнить что-то вроде ответа Томаса, чтобы получить две карты, а затем удалить ключи, которые существуют только в одной:
let values map = map |> Map.toSeq |> Seq.map snd
let desparse map1 map2 = Map.filter (fun k _ -> Map.containsKey k map2) map1
let distance textA textB =
let a = ngramSplit 3 textA |> Map.ofSeq
let b = ngramSplit 3 textB |> Map.ofSeq
let aValues = desparse a b |> values
let bValues = desparse b a |> values
cos aValues bValues
С символьными n-граммами я не знаю, насколько хорошими будут ваши результаты. Это зависит от того, какие особенности текста вас интересуют. Я занимаюсь обработкой на естественном языке, поэтому обычно мой первый шаг - это пометка части речи. Затем я сравниваю н-граммы частей речи. Я использую T'n'T для этого, но у него есть странные проблемы с лицензированием. Некоторые из моих коллег вместо этого используют ACOPOST , бесплатную альтернативу (как в случае пива и свободы). Я не знаю, насколько точна точность, но POS-теги - это хорошо понятная проблема в наши дни, по крайней мере, для английского языка и родственных языков.
Вопрос (3):
Лучший способ сравнить две почти одинаковые строки - это Расстояние Левенштейна . Я не знаю, так ли это здесь, хотя вы можете ослабить допущения несколькими способами, например, для сравнения цепочек ДНК.
Стандартной книгой по этому предмету является книга Санькоффа и Крускала "Деформации времени, струнные правки и маромолекулы" Он довольно старый (1983 г.), но дает хорошие примеры того, как адаптировать базовый алгоритм к ряду приложений.