Левенштейн Попробуйте неправильное расстояние

я искал в Интернете реализацию тройки Левенштейна и нашел это: Проблема расстояния Левенштейна: причины. я попытался добавить кусок кода для нормализации слов. Если слово, например, состоит из 5 букв («Яблоко»), и у меня есть это слово («Яблоко»), расстояние равно 1, и я принимаю его как то же самое. Когда у меня, например, есть более длинное слово («обстоятельства»), вы можете сделать больше ошибок. Если у вас есть две ошибки в этом слове, исходный код рассчитает минимальное расстояние равным 2 и не примет его. Поэтому я хочу использовать логарифм. С логарифмом расстояние между «обстоятельствами» и «обстоятельствами» будет меньше 2, а из-за приведения к int оно будет равно 1. Вот что я хочу сделать.

public class LevenshteinTrie {
    private int distance = -1;
    private Trie trie = null;

    public LevenshteinTrie(int distance, Set<String> words) {
        this.distance = distance;
        this.trie = new Trie();

        for(String word : words) {
            this.trie.insert(word);
        }
    }

    public Set<String> discoverFriends(String word, boolean normalized) {
        Set<String> results = new HashSet<String>();

        int[] currentRow = new int[word.length() + 1];

        List<Character> chars = new ArrayList<Character>(word.length());

        for(int i = 0; i < word.length(); i++) {
            chars.add(word.charAt(i));
            currentRow[i] = i;
        }

        currentRow[word.length()] = word.length();

        for(Character c : this.trie.getRoot().getChildren().keySet()) {
            this.traverseTrie(this.trie.getRoot().getChildren().get(c), c, chars, currentRow, results, normalized);
        }

        return results;
    }

    private void traverseTrie(TrieNode node, char letter, List<Character> word, int[] previousRow, Set<String> results, boolean normalized) {
        int size = previousRow.length;
        int[] currentRow = new int[size];

        currentRow[0] = previousRow[0] + 1;

        int minimumElement = currentRow[0];

        int insertCost = 0;
        int deleteCost = 0; 
        int replaceCost = 0;

        for(int i = 1; i < size; i++) {
            insertCost = currentRow[i - 1] + 1;
            deleteCost = previousRow[i] + 1;

            if(word.get(i - 1) == letter) {
                replaceCost = previousRow[i - 1];
            } else {
                replaceCost = previousRow[i - 1] + 1;
            }

            currentRow[i] = Math.min(Math.min(insertCost, deleteCost), replaceCost);

            if(currentRow[i] < minimumElement) {
                if(normalized) {
                    minimumElement = (int)(currentRow[i] / (Math.log10(word.size())));
                } else {
                    minimumElement = currentRow[i];
                }
            }
        }

        int tempCurrentRow = currentRow[size - 1];

        if(normalized) {
            tempCurrentRow = (int)(currentRow[size - 1] / (Math.log10(word.size())));
        }

        System.out.println(tempCurrentRow);

        if(tempCurrentRow <= this.distance && node.getWord() != null) {
            results.add(node.getWord());
        }

        if(minimumElement <= this.distance) {
            for(Character c : node.getChildren().keySet()) {
                this.traverseTrie(node.getChildren().get(c), c, word, currentRow, results, normalized);
            }
        }
    }
}

public class Trie {
    private TrieNode root = null;;

    public Trie() {
        this.root = new TrieNode();
    }

    public void insert(String word) {
        TrieNode current = this.root;

        if (word.length() == 0) {
            current.setWord(word);
        }

        for (int i = 0; i < word.length(); i++) {
            char letter = word.charAt(i);

            TrieNode child = current.getChild(letter);

            if (child != null) {
                current = child;
            } else {
                current.getChildren().put(letter, new TrieNode());
                current = current.getChild(letter);
            }

            if (i == word.length() - 1) {
                current.setWord(word);
            }
        }
    }
 }

public class TrieNode {
    public static final int ALPHABET = 26;
    private String word = null;
    private Map<Character, TrieNode> children = null;

    public TrieNode() {
        this.word = null;
        this.children = new HashMap<Character, TrieNode>(ALPHABET);
    }

    public TrieNode getChild(char letter) {
        if(this.children != null) {
            if(children.containsKey(letter)) {
                return children.get(letter);
            }
        }

        return null;
    }

    public String getWord() {
        return word;
    }
}

К сожалению, этот код работает некорректно. Я установил максимальное расстояние на 1. Теперь, когда я запускаю программу и ищу «вдимир путин» (у меня есть «владимир путин» в моем списке), программа не примет его как друга. Когда я распечатываю временные расчетные расстояния, это выглядит так:

tempCurrentRows, когда максимальное расстояние = 1:

11
11
10
10
10
10
11
11
11
11
10
11
11
11
11
11
11
11
10
10
10
10
10
10
10
10
10
10
9
11
11
10
10
10
10

Но когда я устанавливаю максимальное расстояние на 2, временные расстояния меняются:

tempCurrentRows при максимальном расстоянии = 2:

11
11
11
10
10
10
10
9
9
8
7
6
5
4
3
2
1
11
11
10
10
9
9

Значит, в коде должна быть огромная ошибка. Но я не понимаю, где и почему и как я должен изменить код, чтобы он работал так, как я хочу, чтобы он работал.


person Mulgard    schedule 13.06.2014    source источник


Ответы (2)


Как вы реализовали поиск «вдимир путин»? Ваш код кажется правильным. Я тестировал это с помощью:

public static void main(String[] args) {
    HashSet<String> words = new HashSet<String>();
    words.add("vdimir putin");
    LevenshteinTrie lt = new LevenshteinTrie(2, words);
    Set<String> friends = lt.discoverFriends("vladimir putin", false);
    System.out.println(friends.iterator().next());
}

это печатает «вдимир путин», что означает, что у «владимира путина» есть друг с Левенштейном Расстояние 2

person Fortega    schedule 13.06.2014
comment
Я думаю, это больше комментарий? - person christopher; 13.06.2014
comment
Это связано с тем, что вы установили максимальное расстояние равным 2. Если вы добавите System.out.println(tempCurrentRow); перед if(tempCurrentRow <= this.distance && node.getWord() != null), вы увидите, что это большая разница между maxdistance = 1 и maxdistance = 2. Из-за логарифма, который я реализовал, расстояние 2 будет принято с максимальное расстояние 1. потому что логарифм 2 меньше 2, и java сокращает его до 1. именно так, как я хочу. - person Mulgard; 13.06.2014
comment
извините, я не имел в виду, что логарифм 2 меньше 2. Я имел в виду 2 / Math.log10 (длина слова) меньше 2. - person Mulgard; 13.06.2014
comment
@christopher, возможно, это так, но эти большие блоки кода в комментариях нечитаемы. - person Fortega; 13.06.2014
comment
@ Малгард, я не понимаю, что ты здесь говоришь. Пожалуйста, добавьте тестовый код (основной метод, который что-то печатает?) в вопрос, в котором мы видим, что расстояние до вдимира путина равно 11, а не 2 - person Fortega; 13.06.2014
comment
я сказал это в своем комментарии: добавьте System.out.println(tempCurrentRow); перед if(tempCurrentRow ‹= this.distance && node.getWord() != null) и снова запустите тест. - person Mulgard; 13.06.2014
comment
и не используйте 2 как максимум. используйте 1 - person Mulgard; 13.06.2014
comment
Я отредактировал свой пост. может быть, теперь это легче понять. - person Mulgard; 13.06.2014

О, я думаю, если нужно также нормализовать минимальный элемент:

if(normalized) {
    tempCurrentRow = (int)(currentRow[size - 1] / (Math.log10(word.size())));
    minimumElement = (int)(minimumElement / (Math.log10(word.size())));
}

И замените это:

 if(normalized) {
     minimumElement = (int)(currentRow[i] / (Math.log10(word.size())));
 } else {
     minimumElement = currentRow[i];
 }

с этим:

minimumElement = currentRow[i];

С этим небольшим изменением он работает так, как я хочу, чтобы он работал. Когда я теперь ищу «вдмир путин» и имею максимальное расстояние 1, он правильно находит «владимир путин».

person Mulgard    schedule 13.06.2014