对内存约束中的单词进

时间:2014-10-27 05:31:22

标签: java algorithm memory limit

采访准备qn:给定一长串单词,返回一个不同单词列表以及仅使用16gb内存的发生次数。

我正在考虑使用HashMap,它只存储该单词的一个副本,然后如果再次遇到相同的单词则递增该值。这是我能想到的唯一方法。整体复杂性将是O(n),因为我需要遍历整个单词列表一次,用单词填充我的hashmap和/或增加其计数。

我不确定的是如何将16GB内存事实纳入我的答案中?

如果每个单词是100个字节(可能不是字长可以变化),那么我的hashmap可以是x个字长,那么什么?我如何在逻辑上达到我的解决方案的极限?我有点不确定如何在这里继续..

6 个答案:

答案 0 :(得分:3)

首先,这不是排序问题。从根本上讲,这是一个整理问题。

我可以想到解决它的三种方法......取决于你所拥有的不同单词的数量。

  • 如果单词的数量足够小,您可以在某种地图中保存每个不同的单词和计数(例如TreeMap<String, Integer>),那么解决方案很简单明了。这需要与不同单词的数量成比例的空间(在存储器中)。 (TreeMap按字顺序为您提供计数...)

  • 如果你不能这样做,那么有两种方法:

    • 排序合并方法:

      1. 将输入文件分为N个子文件。
      2. 对每个子文件(在内存中)中的单词进行排序并写出来。
      3. 打开N个已排序的子文件,并对每个单词进行读取和计数,每次选择并计算N个文件中显示的最小单词。
      4. 可以使用与N成比例的空间执行最后一步,并且可以将计数列表直接写入文件。除非N(子文件的数量)很大,否则您可以轻松容纳带有“太多”不同单词的输入文件。

      5. 分区方法:

        1. 选择N个单词,将单词的“空格”划分为N个大致相等的部分。
        2. 然后读取输入文件N次,选择单词 i 和单词 i + 1 之间的单词,整理/计算它们(使用TreeMap如上所述)。
        3. 在每个整理/计数之后,将计数列表附加到输出文件。

假设使用O(MlogM),这三种方法的运行时复杂度大致相同(TreeMap)。如果使用HashMap,则会导致O(M)复杂度,但输出列表不会被排序......并且它不适用于“复杂”方法。

答案 1 :(得分:1)

下面将使用O(n)

Class WordStore
{
    private static HashSet<String> words;
    private int count;
    private long byte;
    //Singleton approach
    public static int addWord(String word)
    {
        if(byte!=17179869184 || (byte+word.length()<17179869184) //checking if the words size is upto 16GB 
        {
            words.add(word);
            count++;
            byte+=byte+word.length();
            return 0;
        }
        else
        {
            System.out.println("Word Count in 16GB :"+count);
            return 1;
        }
    }
}
Class Loader
{
    public static void main(String[] a)
    {
        while(1)
        {       
            String a=readWordOneByOne();
            if(WordStore.addWord(a))
            {
                break;
            }
        }
    }
}

答案 2 :(得分:1)

读入列表并转换word -> (word, 1)之类的字词。然后在收集器中收集具有相同单词的所有对,与(word,1) + (word,1) = (word,2)之类的计数器相加。一个hashmap在这里运行良好。

如果收集器增长超过特定限制,请将元组写入磁盘(调整的一个好点,就像只编写一些不太频繁的单词)。收集器的输出为((word1, count1,) ... (wordN,countN))。在编写时,你应该通过散列键(单词)将元组拆分成一些小于内存的文件,比如说filename= "split" + numwrites + (key%5),其中numwrites只是分割的计数器。

由于每个单词都在特定的分割后缀(键%5)中,您现在只需读取并汇总具有相同结尾的所有分割。

请注意,如果您对分割数量有所了解,这种基于散列的方法将起作用。如果您没有,收集器可以为每个输出文件排序元组,并在最后阶段合并它们。 Merge-Sort没有任何更大的内存约束,但具有复杂度O(n * log(n))

有关一般方法的说明,请参阅MapReduce

答案 3 :(得分:1)

根据列表的大小,有三种可能的方法: -

  1. 如果列表大小适合内存:然后加载整个列表并使用快速排序对其进行排序并运行计数并将其存储在输出文件中。

  2. 如果列表的大小对于内存来说太大但没有。唯一单词的大小可以适合内存:使用count来维护TreeSet或HashMap并逐个读取单词并在HashMap中添加计数。

  3. 即使是唯一的单词也不适合记忆:

  4.   
        
    1. 将内存分为两部分:一部分用于获取和处理单词,第二部分用于维护计数。
    2.   
    3. 读取输入直到它填满第一部分
    4.   
    5. 对输入进行排序。
    6.   第二部分中的
    7. 维护TreeSet和数组。
    8.   
    9. TreeSet将word映射到数组中的索引
    10.   
    11. 以顺序方式遍历排序数组
    12.   
    13. 在计算word之后将其计数添加到数组并命名为TreeSet。
    14.   
    15. 当输入结束时,重新加载下一组单词,直到第一部分被填满。
    16.   
    17. 如果第二部分已满,则需要替换条目并将其保存到磁盘
    18.   
    19. 可以仔细选择替换候选者作为按字典顺序最远离当前单词的单词,这样数字   所需的更换将会减少。
    20.   
    21. 通过检查文件名是否存在来检查是否已经遇到过单词。
    22.   
    23. 如果文件名存在,则从文件名加载计数并在数组中添加一个
    24.   

答案 4 :(得分:0)

因为你有内存限制,所以只使用磁盘/文件。假设您有一个48GB的文件。将文件的前1/3加载到内存(16GB)。在迭代单词时,如果不存在单词名称的文件,请按单词名称创建文件并将1写入其中。如果存在名称文件,则将文件中的值递增1并保存。然后,对文件的后半部分和文件的后半部分重复此过程。最后,每个唯一单词都有一个文件,文件内容将是遇到单词的次数。

答案 5 :(得分:0)

最好的方法(已知)是使用线程(具有消费者和生产者功能和半工作),通过哈希存储,以及读取块。线程数和块大小应该取决于文件的大小。

这样,唯一的限制是可以写多少。因此,字符计数就足够了,让程序在获得更多内存的情况下从中断处回来。