Советы по оптимизации кода Java - PullRequest
2 голосов
/ 03 ноября 2011

Итак, я написал проверку орфографии на Java, и все работает как надо.Единственная проблема заключается в том, что если я использую слово, в котором максимально допустимое расстояние редактирования слишком велико (например, 9), то в моем коде заканчивается память.Я профилировал свой код и поместил кучу в файл, но я не знаю, как использовать его для оптимизации своего кода.

Кто-нибудь может предложить какую-нибудь помощь?Я более чем готов выложить файл / использовать любой другой подход, который могут быть у людей.

-Edit-

Многие люди спрашивали подробности в комментариях.Я подумал, что другие люди найдут их полезными, и они могут быть похоронены в комментариях.Вот они:

  • Я использую Три для хранения самих слов.

  • Чтобы улучшить эффективность времени, я неЯ вычисляю расстояние Левенштейна заранее, но я вычисляю его по ходу.Под этим я подразумеваю, что я храню в памяти только две строки таблицы LD.Поскольку Trie - это дерево префиксов, это означает, что каждый раз, когда я повторяю узел, предыдущие буквы слова (и, следовательно, расстояние для этих слов) остаются неизменными.Поэтому я вычисляю расстояние только с этой новой буквой, а предыдущая строка остается неизменной.

  • Сгенерированные мной предложения хранятся в HashMap.Строки таблицы LD хранятся в ArrayLists.

Вот код функции в Три, которая приводит к проблеме.Построить Trie довольно просто, и я не включил код для этого здесь.

/*
 * @param letter: the letter that is currently being looked at in the trie
 *        word: the word that we are trying to find matches for
 *        previousRow: the previous row of the Levenshtein Distance table
 *        suggestions: all the suggestions for the given word
 *        maxd: max distance a word can be from th query and still be returned as suggestion
 *        suggestion: the current suggestion being constructed
 */


public void get(char letter, ArrayList<Character> word, ArrayList<Integer> previousRow, HashSet<String> suggestions, int maxd, String suggestion){

// the new row of the trie that is to be computed.
ArrayList<Integer> currentRow = new ArrayList<Integer>(word.size()+1);
currentRow.add(previousRow.get(0)+1);

int insert = 0;
int delete = 0;
int swap = 0;
int d = 0;

for(int i=1;i<word.size()+1;i++){
    delete = currentRow.get(i-1)+1;
    insert = previousRow.get(i)+1;

    if(word.get(i-1)==letter)
    swap = previousRow.get(i-1);
    else
    swap = previousRow.get(i-1)+1;

    d = Math.min(delete, Math.min(insert, swap));
    currentRow.add(d);
}

// if this node represents a word and the distance so far is <= maxd, then add this word as a suggestion
if(isWord==true && d<=maxd){
    suggestions.add(suggestion);
    }

// if any of the entries in the current row are <=maxd, it means we can still find possible solutions. 
// recursively search all the branches of the trie
for(int i=0;i<currentRow.size();i++){
    if(currentRow.get(i)<=maxd){
    for(int j=0;j<26;j++){
        if(children[j]!=null){
        children[j].get((char)(j+97), word, currentRow, suggestions, maxd, suggestion+String.valueOf((char)(j+97))); 
        }
    }
    break;
    }   
}
}

Ответы [ 3 ]

4 голосов
/ 03 ноября 2011

Вот некоторый код, который я быстро создал, показывающий один из способов создания кандидатов и их «ранжирования».

Хитрость в том, что вы никогда не «проверяете» недопустимого кандидата.

Мне твое: "У меня заканчивается память, когда у меня есть расстояние редактирования 9" , кричит "комбинаторный взрыв".

Конечно, чтобы уклониться от комбинаторного взрыва, ты нене пытайтесь сгенерировать все слова, которые находятся на расстоянии от «9» от вашей работы с ошибками.Вы начинаете со слова с ошибкой и генерируете (довольно много) возможных кандидатов, но воздерживаетесь от создания слишком большого количества кандидатов, потому что тогда у вас возникнут проблемы.

(также обратите внимание, что это неНе имеет большого смысла вычислять до расстояния редактирования Левенштейна, равного 9, потому что технически любое слово, содержащее менее 10 букв, может быть преобразовано в любое другое слово, содержащее менее 10 букв в макс. 9 преобразованиях)

Вотпочему вы просто не можете проверить все слова на расстоянии до 9 без ошибки OutOfMemory или просто программы, которая никогда не завершается:

  • , генерирующей все светодиоды до 1 дляслово "ptmizing" , при добавлении только одной буквы (от a до z) генерируется уже 9 * 26 вариантов (то есть 324 варианта) [есть 9 позиций, в которые можно вставить одну из 26 букв)
  • генерация всех светодиодов до 2, добавление только одной буквы к тому, что, как мы знаем, уже генерирует 10 * 26 * 324 вариаций (60 840)
  • геноценка всех светодиодов до 3 дает: 17 400 240 вариаций

И это только с учетом случая, когда мы добавляем одну, добавляем две или добавляем три буквы (мыне считая удаления, свопов и т. д.).И это слово с ошибкой, длина которого составляет всего девять символов.На «настоящих» словах он взрывается еще быстрее.

Конечно, вы могли бы стать «умными» и генерировать это таким образом, чтобы не было слишком много дураков и т. Д., Но суть осталась: взрывается комбинаторный взрыв быстро .

Во всяком случае ... Вот пример.Я просто передаю словарь допустимых слов (содержащий в данном случае только четыре слова) соответствующему методу, чтобы сохранить это сокращение.

Вы, очевидно, захотите заменить вызов на светодиод своим собственным светодиодом.реализация.

Двойной метафон - всего лишь пример: в реальной проверке орфографии слова, которые звучат «одинаково», несмотря на дальнейший светодиод, должны считаться «более правильными» и, следовательно, часто предлагать сначала.Например, «оптимизация» и «aupteemising» довольно далеки от светодиодной точки зрения, но используя двойной метафон, вы должны получить «оптимизацию» в качестве одного из первых предложений.

(заявление об отказе:следующее было проверено через несколько минут, оно не учитывает заглавные буквы, неанглийские слова и т. д .: это не настоящая проверка орфографии, просто пример)

   @Test
    public void spellCheck() {
        final String src = "misspeled";
        final Set<String> validWords = new HashSet<String>();
        validWords.add("boing");
        validWords.add("Yahoo!");
        validWords.add("misspelled");
        validWords.add("stackoverflow");
        final List<String> candidates = findNonSortedCandidates( src, validWords );
        final SortedMap<Integer,String> res = computeLevenhsteinEditDistanceForEveryCandidate(candidates, src);
        for ( final Map.Entry<Integer,String> entry : res.entrySet() ) {
            System.out.println( entry.getValue() + " @ LED: " + entry.getKey() );
        }
    }

    private SortedMap<Integer, String> computeLevenhsteinEditDistanceForEveryCandidate(
            final List<String> candidates,
            final String mispelledWord
    ) {
        final SortedMap<Integer, String> res = new TreeMap<Integer, String>();
        for ( final String candidate : candidates ) {
            res.put( dynamicProgrammingLED(candidate, mispelledWord), candidate );
        }
        return res;
    }

    private int dynamicProgrammingLED( final String candidate, final String misspelledWord ) {
        return Levenhstein.getLevenshteinDistance(candidate,misspelledWord);
    }

ЗдесьВы генерируете всех возможных кандидатов, используя несколько методов.Я реализовал только один такой метод (и быстро, поэтому он может быть поддельным, но это не главное;)

    private List<String> findNonSortedCandidates( final String src, final Set<String> validWords ) {
        final List<String> res = new ArrayList<String>();
        res.addAll( allCombinationAddingOneLetter(src, validWords) );
//        res.addAll( allCombinationRemovingOneLetter(src) );
//        res.addAll( allCombinationInvertingLetters(src) );
        return res;
    }

    private List<String> allCombinationAddingOneLetter( final String src, final Set<String> validWords ) {
        final List<String> res = new ArrayList<String>();
        for (char c = 'a'; c < 'z'; c++) {
            for (int i = 0; i < src.length(); i++) {
                final String candidate = src.substring(0, i) + c + src.substring(i, src.length());
                if ( validWords.contains(candidate) ) {
                    res.add(candidate); // only adding candidates we know are valid words
                }
            }
            if ( validWords.contains(src+c) ) {
                res.add( src + c );
            }
        }
        return res;
    }
2 голосов
/ 03 ноября 2011

Одна вещь, которую вы можете попробовать, это увеличить размер кучи Java, чтобы преодолеть «ошибку нехватки памяти».

Следующая статья поможет вам понять, как увеличить размер кучи в Java

http://viralpatel.net/blogs/2009/01/jvm-java-increase-heap-size-setting-heap-size-jvm-heap.html

Но я думаю, что лучший подход к решению вашей проблемы - найти лучший алгоритм, чем текущий алгоритм

0 голосов
/ 03 ноября 2011

Что ж, без дополнительной информации по теме сообщество мало что может сделать для вас ... Вы можете начать со следующего:

  1. Посмотрите, что говорит ваш профилировщик (посленемного побежало): что-нибудь накапливается?Много ли объектов - это, как правило, дает вам подсказку о том, что не так с вашим кодом.

  2. Опубликуйте где-нибудь сохраненный дамп и свяжите его в своем вопросе, чтобы кто-то еще могвзгляните на это.

  3. Сообщите нам, каким профилировщиком вы пользуетесь, тогда кто-то может дать вам подсказки о том, где искать ценную информацию.

  4. После того, как вы сузилисьсвести вашу проблему к определенной части вашего кода, и вы не можете понять, почему в вашей памяти так много объектов $FOO, опубликовать фрагмент соответствующей части.

...