我需要解决一个Dynamic Programming
问题,我需要在内存中创建一个N*N
大小矩阵。如果我创建的byte[][]
矩阵的大小为N = 100000;
,则会引发java.lang.OutOfMemoryError: Java heap space
错误。
在此矩阵中,我将在特定ith
和jth
索引处存储0或1。所以我的使用仅限于一点,我需要一种方法,这样我每个矩阵单元只能使用1位大小,而不是溢出存储器,在需要仅为1位的单元中保留8位。
我怎样才能做到这一点?
请注意我的关注点并不是增加JVM的堆积,我只是在寻找一种方法来最佳地实现动态问题的解决方案。
答案 0 :(得分:2)
更新:我刚刚检查过,遗忘了BitSet
。
BitSet
的原始API建立在int
索引之上。整数范围超出N * N
的范围。因此,您需要自己实施一些东西。一个建议:
public class BetterBitSet {
private final long[] values;
public BetterBitSet(int dimension) {
values = new long[(int) ((long) dimension) * dimension / 8 / 64 + 1];
}
public void set(int i, int j, boolean value) {
long index = index(i, j);
int arrayIndex = arrayIndex(index);
if (value) {
values[arrayIndex] = set(values[arrayIndex], offset(index));
} else {
values[arrayIndex] = unset(values[arrayIndex], offset(index));
}
}
public boolean isSet(int i, int j) {
long index = index(i, j);
return isSet(values[arrayIndex(index)], offset(index));
}
private boolean isSet(long value, int bitIndex) {
return (value & (1L << bitIndex)) != 0;
}
private long set(long value, int bitIndex) {
return value | (1L << bitIndex);
}
private long unset(long value, int bitIndex) {
return value & ~(1L << bitIndex);
}
private long index(int i, int j) {
return j * 8L + i;
}
private int arrayIndex(long index) {
return (int) index / 64;
}
private int offset(long index) {
return (int) index % 64;
}
}
如有疑问,请查看BitSet
的源代码并尝试类似的内容。
旧答案:问题是byte[][]
的每个索引都需要8位,只存储1位信息。这需要9,536 MB用于在堆上分配此数组,因此您的空间不足。这个内存量很可能对您的机器来说太大了。但是,对于存储位,您仍然需要1,192 MB。 (不考虑由BitSet
引起的任何开销。)这仍然是一个很高的数字,因此请确保您的机器提供了足够的空间,您必须另外分配给您的JVM实例。
答案 1 :(得分:1)
Java提供了一个名为BitSet
的托管位数据结构。我说这是管理的,因为Java没有用于存储位的本机类型,而是BitSet
将各个位存储为long
的组件,并保留long[]
以支持组。这是一种非常高效的存储方法,但implementation of BitSet
每64位中有6位用于“寻址”,这相当于原始存储层中10%的开销。
这意味着您可以通过保留并仔细查找自己的BitSet
来节省long[]
存储空间,但代价是代码中存在一些复杂性和风险。除非你对空间有很大的限制,否则可能不值得保存BitSet
的~10%原始开销。
上述两种解决方案都是一维的位数组,这也许值得一无所知。通过将一维数组视为行的连接,您可以轻松地将二维数组转换为一维数组(至少,在这种情况下,您似乎假设行宽相等)。寻址矩阵中的特定单元格只是:
row * (row_width) + column
答案 2 :(得分:0)
在许多情况下,您不需要所有子问题来评估当前问题,所以我会建议使用hashmap的DP问题的递归自顶向下解决方案来存储解决方案。一旦使用了子问题,您可以将其从hashmap中删除以防止溢出。 有时会有轻微的性能损失但会阻止内存完全错误。