需要制作一个非常大的数组[2 ^ 16 + 1] [2 ^ 16 + 1] - 数组的大小太大:(

时间:2011-02-02 11:13:35

标签: c

问候

我需要计算一个由16位字组成的信号的一阶熵(Markov源,就像在wiki上http://en.wikipedia.org/wiki/Entropy_(information_theory)。 这意味着,我必须计算在数据流中发生a-> b(符号b出现在a之后)的每个组合的频率。 当我只使用4个不太重要或4个更重要的位时,我使用了一个二维数组,其中第一维是第一个符号,第二维是第二个符号。

我的算法看起来像这样

  1. 读取当前符号
  2. 数组[prev_symbol] [curr_symbol] ++
  3. prev_symbol = curr_symbol
  4. 向前移动1个符号
  5. 然后,Array [a] [b]将表示符号b在流中出现符号a后的次数。

    现在,我明白C中的数组是一个递增得到精确值的指针,比如从数组[10] [10]获取元素[3] [4]我必须将指针递增到数组[0] [0] by(3 * 10 + 4)(存储在数组中的变量的大小)。我理解问题必须是2 ^ 32个unsigned long类型的元素必须占用太多。

    但是,有没有办法解决它?

    或许还有另一种方法可以实现这一目标?

3 个答案:

答案 0 :(得分:5)

具有32'000乘32'000个元素的二维整数数组(4字节)占用大约16 GB的RAM。你的机器有那么多内存吗?

无论如何,在超过10亿个数组元素中,只有极少数数组元素的数量不同于零。因此,使用某种稀疏存储可能会更好。

一种解决方案是使用字典,其中元组(a,b)是关键字,出现次数是值。

答案 1 :(得分:1)

也许您可以对数据进行多次传递。从符号X开始的对的熵贡献基本上独立于以任何其他符号开头的对(当然除了它们的总数),因此您可以计算所有这些对的熵,然后丢弃分布数据。最后,结合2 ^ 16个部分熵值得到总数。您不一定要对数据进行2 ^ 16次传递,您可以在一次传递中“感兴趣”尽可能多的初始字符,因为您有空间。

或者,如果您的数据小于2 ^ 32个样本,那么您肯定知道您不会看到所有可能的对,因此您实际上不需要为每个对分配计数。如果样本足够小,或者熵足够低,那么某种稀疏阵列将比完整的16GB矩阵使用更少的内存。

答案 2 :(得分:1)

在Ubuntu 10.10 x64上进行了快速测试

gt@thinkpad-T61p:~/test$ uname -a
Linux thinkpad-T61p 2.6.35-25-generic #44-Ubuntu SMP Fri Jan 21 17:40:44 UTC 2011 x86_64 GNU/Linux
gt@thinkpad-T61p:~/test$ cat mtest.c
#include <stdio.h>
#include <stdlib.h>

short *big_array;


int main(void)
{
    if((big_array = (short *)malloc(4UL*1024*1024*1024*sizeof (short))) == NULL) {
        perror("malloc");
        return 1;
    }

    big_array[0]++;
    big_array[100]++;
    big_array[1UL*1024*1024*1024]++;
    big_array[2UL*1024*1024*1024]++;
    big_array[3UL*1024*1024*1024]++;

    printf("array[100] = %d\narray[3G] = %d\n", big_array[100], big_array[3UL*1024*1024*1024]);

    return 0;
}
gt@thinkpad-T61p:~/test$ gcc -Wall mtest.c -o mtest
gt@thinkpad-T61p:~/test$ ./mtest 
array[100] = 1
array[3G] = 1
gt@thinkpad-T61p:~/test$ 

只要你有足够的内存和/或交换,看起来linux上的虚拟内存系统可以胜任。

玩得开心!