内存不足的Java字谜

内存不足的Java字谜,java,anagram,Java,Anagram,我正试图解决这个古老的字谜问题。多亏了很多教程,我能够遍历一组字符串,递归地找到所有排列,然后将它们与英语单词列表进行比较。我发现的问题是,在大约三个单词(通常是“变形”之类的单词)之后,我会出现一个OutOfMemory错误。我试着把我的批次分成几个小集合,因为它似乎是消耗我所有内存的递归部分。但即使只是“变形”也会把它锁起来 在这里,我把一个文件中的单词读到一个列表中 Scanner scanner = new Scanner(resource.getInputStream()); w

我正试图解决这个古老的字谜问题。多亏了很多教程,我能够遍历一组字符串,递归地找到所有排列,然后将它们与英语单词列表进行比较。我发现的问题是,在大约三个单词(通常是“变形”之类的单词)之后,我会出现一个OutOfMemory错误。我试着把我的批次分成几个小集合,因为它似乎是消耗我所有内存的递归部分。但即使只是“变形”也会把它锁起来

在这里,我把一个文件中的单词读到一个列表中

Scanner scanner = new Scanner(resource.getInputStream());
   while (scanner.hasNext()) {
       String s = scanner.nextLine();
        uniqueWords.add(s.toLowerCase());
   }
现在我将它们分成更小的集合,并调用一个类来生成字谜:

List<List<String>> subSets = Lists.partition(new ArrayList(uniqueWords), SET_SIZE);

for (List<String> set: subSets) {
      // tried created as class attribute & injection, no difference 
      AnagramGenerator anagramGenerator = new AnagramGenerator();
      List<Word> anagrams = anagramGenerator.createWordList(set);
      wordsRepository.save(anagrams);
      LOGGER.info("Inserted {} records into the database", anagrams.size());
 }
List subSets=Lists.partition(新的ArrayList(uniqueWords),设置大小);
对于(列表集:子集){
//尝试创建为类属性和注入,没有区别
anagramggenerator anagramggenerator=新anagramggenerator();
List anagrams=anagramggenerator.createWordList(set);
保存(字谜);
info(“将{}条记录插入数据库”,anagrams.size());
}
最后是我的发电机:

public class AnagramGenerator {

private Map<String, List<String>> map = new Hashtable<>();
public List<Word> createWordList(List<String> dictionary) {

   buildAnagrams(dictionary);

   List<Word> words = new ArrayList<>();
   for (Map.Entry<String, List<String>> entry : map.entrySet()) {
       words.add(new Word(entry.getKey(), entry.getValue()));
   }
    return words;
   }

private Map<String, List<String>> buildAnagrams(List<String> dictionary) {

        for (String str : dictionary) {
            String key = sortString(str);
            if (map.get(key) != null) {
                map.get(key).add(str.toLowerCase());
            } else {
                if (str.length() < 2) {
                    map.put(key, new ArrayList<>());
                } else {
                    Set<String> permutations = permutations(str);
                    Set<String> anagramList = new HashSet<>();

                    for (String temp : permutations) {
                        if (dictionary.contains(temp) && !temp.equalsIgnoreCase(str)) {
                            anagramList.add(temp);
                        }
                    }
                    map.put(key, new ArrayList<>(anagramList));
                }
            }
        }
        return map;
    }

   private Set<String> permutations(String str) {    
        if (str.isEmpty()) {
            return Collections.singleton(str);
        } else {
            Set<String> set = new HashSet<>();
            for (int i = 0; i < str.length(); i++)
                for (String s : permutations(str.substring(0, i) + str.substring(i + 1)))
                    set.add(str.charAt(i) + s);
            return set;
        }
    }
公共类AnagramGenerator{
私有映射映射=新哈希表();
公共列表createWordList(列表字典){
建筑语法(字典);
List words=new ArrayList();
对于(Map.Entry:Map.entrySet()){
添加(新词(entry.getKey(),entry.getValue());
}
返回单词;
}
私有地图构建语法(列表字典){
for(字符串str:dictionary){
字符串键=排序字符串(str);
if(map.get(key)!=null){
get(key).add(str.toLowerCase());
}否则{
如果(str.length()<2){
put(key,newarraylist());
}否则{
集合置换=置换(str);
Set anagramList=new HashSet();
用于(字符串临时:排列){
if(dictionary.contains(temp)&&!temp.equalsIgnoreCase(str)){
anagramList.add(临时);
}
}
map.put(key,newarraylist(anagramList));
}
}
}
返回图;
}
私有集置换(字符串str){
if(str.isEmpty()){
返回集合。单例(str);
}否则{
Set=newhashset();
对于(int i=0;i
编辑: 基于出色的反馈,我将生成器从排列更改为工作查找:

public class AnagramGenerator {
private Map<String, Set<String>> groupedByAnagram = new HashMap<String, Set<String>>();

    private Set<String> dictionary;

    public AnagramGenerator(Set<String> dictionary) {

        this.dictionary = dictionary;
    }

 public List<Word> searchAlphabetically() {

        List<Word> words = new ArrayList<>();
        for (String word : dictionary) {
            String key = sortString(word);
            if (!groupedByAnagram.containsKey(key)) {
                groupedByAnagram.put(key, new HashSet<>());
            }
            if (!word.equalsIgnoreCase(key)) {
                groupedByAnagram.get(key).add(word);
            }
        }

        for (Map.Entry<String, Set<String>> entry : groupedByAnagram.entrySet()) {
            words.add(new Word(entry.getKey(), new ArrayList(entry.getValue())));
        }

        return words;
    }
 private String sortString(String goodString) {

        char[] letters = goodString.toLowerCase().toCharArray();
        Arrays.sort(letters);
        return new String(letters);
    }
公共类AnagramGenerator{
私有映射groupedByAnagram=newHashMap();
专用词典;
公共anagram生成器(集合字典){
这本字典=字典;
}
公共列表按字母顺序搜索(){
List words=new ArrayList();
for(字符串:字典){
字符串键=排序字符串(word);
如果(!groupedByAnagram.containsKey(键)){
put(key,new HashSet());
}
if(!word.equalsIgnoreCase(关键字)){
groupedByAnagram.get(key).add(word);
}
}
对于(Map.Entry:groupedByAnagram.entrySet()){
add(新单词(entry.getKey(),新数组列表(entry.getValue()));
}
返回单词;
}
私有字符串排序字符串(字符串goodString){
char[]letters=goodString.toLowerCase().toCharArray();
数组。排序(字母);
返回新字符串(字母);
}

它有更多的调整,所以我不添加一个单词作为它自己的字谜,但除此之外,它看起来很快。而且,代码更干净。谢谢大家!

做一个快速的计算:“变形”有12个字母,它给出12!=479001600个排列。每个字符串至少需要12个字节(假设UTF-8仅包含ASCII字符),这意味着总大小为12*479001600字节,约为6GB

现在,据我所知,默认堆大小设置为1GB或(如果更小)可用内存的四分之一,这小于所需的6GB

有两种解决方法:

  • 在执行程序时增加堆大小,但由于排列呈指数级增长,它对更长的字不起作用:只需再增加一个字母,“完成”就需要78GB

  • 通过排列进行流式处理,而不是将它们具体化为一组字符串。具体来说,这意味着仍然使用递归,但不是存储每个递归生成的排列,而是立即对其进行处理,然后在继续下一个排列时将其遗忘


现在,如果需要对整个字典执行此操作,如果您可以访问集群,另一种方法是计算字典本身的笛卡尔积,将其存储在像HDFS这样的分布式文件系统中(应该是十亿个条目的数量级),然后使用MapReduce并行检查所有对,并输出相互之间的错字。这需要更多的努力,但复杂性从单词长度的指数级下降到字典大小的二次级。

快速计算:“错字”有12个字母,表示12!=479001600个排列。每个字符串至少占用12个字节(假设UTF-8仅包含ASCII字符),这意味着总大小为12*479001600字节,约为6GB

现在,据我所知,默认堆大小设置为1GB或(如果更小)可用内存的四分之一,这小于所需的6GB

有两种解决方法:

  • 在执行exe时增加堆大小
    10! milliseconds = ~1 hour
    12! milliseconds = ~5.54 days
    15! milliseconds = ~41.44 years
    
     sorted_input = sort_alphabetically(input_word)
     for each dictionary_word // probably a file readline()
         sorted_dictionary_word = sort_alphabetically(dictionary_word)
         if(sorted_dictionary_word = sorted_input)
             it's an anagram! Handle it
         end 
     end
    
      multimap = new MultiMap<String, String> // or whatever
    
      def build_dict:
          for each dictionary_word // probably a file readline()
              multimap.add(
                   sort_alphabetically(dictionary_word), 
                   dictionary_word)
          end
      end
    
      def lookup_anagrams(word):
          return multimap.get(sort_alphabetically(word))
      end 
    
    Map<String, Set<String>> groupedByAnagram = new HashMap<String, Set<String>>();
    
    for(String word: dictionary)
    {
      String footprint = sort_alphabetically(word);
      if(!groupedByAnagram.contains(footprint))
      {
        groupedByAnagram.put(footprint, new HashSet<String>>());
      }
      groupedByAnagram.get(footprint).insert(word); 
    }
    
    for(Set<String> anagram: groupedByAnagram.values())
    {
      if(anagram.size() > 1)
      {
        System.out.println("Anagram found.");
        for (String word: anagram)
        {
          System.out.println(word);
        }
      } 
    }