在计算机竞赛中,我遇到了一个问题,我不得不操纵输入数据。输入已经split()到一个数组中,其中data [0]是重复次数。最多可重复10 ^ 18次。我的程序返回Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
,但我没有参加比赛。
这是我的代码占用了内存和CPU:
long product[][]=new long[data[0]][2];
product[0][0]=data[1];
product[0][1]=data[2];
for(int a=1;a<data[0];a++){
product[a][0]=((data[5]*product[a-1][0] + data[6]) % data[3]) + 1; // Pi = ((A*Pi-1 + B) mod M) + 1 (for all i = 2..N)
product[a][1]=((data[7]*product[a-1][1] + data[8]) % data[4]) + 1; // Wi = ((C*Wi-1 + D) mod K) + 1 (for all i = 2..N)
}
以下是一些输入数据:
980046644627629799 9 123456 18 10000000 831918484 451864686 840000324 650000765
972766173386786486 123 1 10000000 10000000 590000001 680000000 610000001 970000002
299896237124947938 681206 164538 2280874 981991 416793690 904023823 813682336 774801135
我的程序最多只能工作7位或8位数,然后运行需要几分钟。有18个数字,几乎在我点击Eclipse中的“Run”时就崩溃了。
我很好奇如何在普通计算机上操作那么多数据。如果我的问题不清楚或您需要更多信息,请告诉我。谢谢!
答案 0 :(得分:3)
你不能拥有也不需要如此庞大的数组。您只需跟踪最近的2个值。例如,只有product1和product2。
此外,考虑测试每次迭代后其中任何一个数字是否为NaN。如果是这样,抛出一个Exception并给出迭代号。 因为一旦你得到NaN,他们都将是NaN。除了你使用很长,所以刮。 “没关系”。 : - )
答案 1 :(得分:2)
long product[][]=new long[data[0]][2];
这是您粘贴的代码中唯一分配内存的行。您分配一个长度为data[0]
的数组!随着数据的增长,数组也在增长。你想在这里申请的公式是什么?
您提供的第一个输入数据:
980046644627629799
已经太大,甚至无法声明数组。尝试使用它作为长度创建单个维度数组,看看会发生什么......
你确定你不只是想要一个积累的1 x 2矩阵吗?清楚地解释您的预期算法,我们可以帮助您提供更优化的解决方案。
答案 2 :(得分:0)
让我们把数字放在一边。
内存:一个long
占用8个字节。 10 18 long
需要 16,000,000太字节。太多了。
时间:10,000,000次操作≈1秒。 10 18 步骤≈ 30世纪。也太过分了。
您可以通过意识到您只需要最新的值来解决内存问题,并且整个阵列都是多余的:
long currentP = data[1];
long currentW = data[2];
for (int a = 1; a < data[0]; a++)
{
currentP = ((data[5] * currentP + data[6]) % data[3]) + 1;
currentW = ((data[7] * currentW + data[8]) % data[4]) + 1;
}
解决时间问题有点棘手。由于使用了模数,您可以观察到数字必须在某个点进入循环。一旦找到循环,就可以预测n次迭代后的值,而不必手动完成每次迭代。
查找周期的最简单方法是跟踪您是否访问过每个元素,然后直到遇到您之前看过的元素。在这种情况下,所需的存储量与M和K(数据[3]和数据[4])成比例。如果它们太大,则必须使用更节省空间的循环检测算法。
以下是找到P:
的值的示例public static void main(String[] args)
{
// value = (A * prevValue + B) % M + 1
final long NOT_SEEN = -1; // the code used for values not visited before
long[] data = { 980046644627629799L, 9, 123456, 18, 10000000, 831918484, 451864686, 840000324, 650000765 };
long N = data[0]; // the number of iterations
long S = data[1]; // the initial value of the sequence
long M = data[3]; // the modulus divisor
long A = data[5]; // muliply by this
long B = data[6]; // add this
int max = (int) Math.max(M, S); // all the numbers (except first) must be less than or equal to M
long[] seenTime = new long[max + 1]; // whether or not a value was seen and how many iterations it took
// initialize the values of 'seenTime' to 'not seen'
for (int i = 0; i < seenTime.length; i++)
{
seenTime[i] = NOT_SEEN;
}
// find the cycle
long count = 0;
long cycleValue = S; // the current value in the series
while (seenTime[(int)cycleValue] == NOT_SEEN)
{
seenTime[(int)cycleValue] = count;
cycleValue = (A * cycleValue + B) % M + 1;
count++;
}
long cycleLength = count - seenTime[(int)cycleValue];
long cycleOffset = seenTime[(int)cycleValue];
long result;
if (N < cycleOffset)
{
// Special case: requested iteration occurs before the cycle starts
// Straightforward simulation
long value = S;
for (long i = 0; i < N; i++)
{
value = (A * value + B) % M + 1;
}
result = value;
}
else
{
// Normal case: requested iteration occurs inside the cycle
// Simulate just the relevant part of one cycle
long positionInCycle = (N - cycleOffset) % cycleLength;
long value = cycleValue;
for (long i = 0; i < positionInCycle; i++)
{
value = (A * value + B) % M + 1;
}
result = value;
}
System.out.println(result);
}
我只是给你解决方案,因为看起来比赛结束了。从中学到的重要教训是,在开始编码之前,应始终检查边界,看看您的解决方案是否切实可行。