对于Project Euler的问题3,我遇到了一个奇怪的问题。该程序适用于其他小的数字,如13195,但当我试图处理像600851475143这样的大数字时会抛出此错误:
Exception in thread "main" java.lang.ArithmeticException: / by zero
at euler3.Euler3.main(Euler3.java:16)
这是我的代码:
//Number whose prime factors will be determined
long num = 600851475143L;
//Declaration of variables
ArrayList factorsList = new ArrayList();
ArrayList primeFactorsList = new ArrayList();
//Generates a list of factors
for (int i = 2; i < num; i++)
{
if (num % i == 0)
{
factorsList.add(i);
}
}
//If the integer(s) in the factorsList are divisable by any number between 1
//and the integer itself (non-inclusive), it gets replaced by a zero
for (int i = 0; i < factorsList.size(); i++)
{
for (int j = 2; j < (Integer) factorsList.get(i); j++)
{
if ((Integer) factorsList.get(i) % j == 0)
{
factorsList.set(i, 0);
}
}
}
//Transfers all non-zero numbers into a new list called primeFactorsList
for (int i = 0; i < factorsList.size(); i++)
{
if ((Integer) factorsList.get(i) != 0)
{
primeFactorsList.add(factorsList.get(i));
}
}
为什么只有大数字会导致此错误?
答案 0 :(得分:5)
您的代码只使用Integer
,这是一个32位类型,最大值为2147483647.当用于比这大得多的数字时,它的失败并不令人惊讶。请注意,您的初始循环使用int
作为循环变量,因此如果它没有抛出异常,它将实际循环。 i
的值将从2147483647变为-2147483648并继续。
使用BigInteger
处理任意大的值,或Long
如果您对有限范围但更大的值感到满意。 (long
/ Long
的最大值为9223372036854775807L。)
但是,我怀疑这是真正的预期方法......对于像这样的大数字来说,它需要长时间。
答案 1 :(得分:1)
除了Jon Skeet提到的BigInteger
问题外,请注意以下事项:
sqrt(num)
num
除以该因子,然后再次测试该因子我的解决方案(最初用Perl编写)在Java中看起来像这样:
long n = 600851475143L; // the original input
long s = (long)Math.sqrt(n); // no need to test numbers larger than this
long f = 2; // the smallest factor to test
do {
if (n % f == 0) { // check we have a factor
n /= f; // this is our new number to test
s = (long)Math.sqrt(n); // and our range is smaller again
} else { // find next possible divisor
f = (f == 2) ? 3 : f + 2;
}
} while (f < s); // required result is in "n"
答案 2 :(得分:1)
不确定是否是这种情况,因为我不知道哪一行是哪个 - 但我注意到你的第一个循环使用了一个int。
//Generates a list of factors
for (int i = 2; i < num; i++)
{
if (num % i == 0)
{
factorsList.add(i);
}
}
由于num很长,因此可能num > Integer.MAX_INT
和你的循环在MAX_INT
回绕到负数,然后循环到0,给你一个num % 0
操作。
答案 3 :(得分:1)
井号在硬件中是离散的。离散意味着你有最小值和最大值。 Java使用two's complement来存储负值,因此2147483647+1 == -2147483648
。这是因为对于int
类型,最大值为2147483647
。这样做叫做溢出。
好像你有一个overflow bug
。可变值i
首先变为负数,最终变为0,因此您得到java.lang.ArithmeticException: / by zero
。如果你的计算机每秒可以循环1000万个语句,这将需要 1h 10min 来重现,所以我把它留作假设而不是证据。
这也是像a+b
这样的简单陈述可能会产生错误的原因。
package margusmartseppcode.From_1_to_9;
public class Problem_3 {
static long lpf(long nr) {
long max = 0;
for (long i = 2; i <= nr / i; i++)
while (nr % i == 0) {
max = i;
nr = nr / i;
}
return nr > 1 ? nr : max;
}
public static void main(String[] args) {
System.out.println(lpf(600851475143L));
}
}
您可能会想:“那么这是如何运作的?”
我的艰难过程就像:
{2,3,5,7,11,13,17, ...}
最多值 x i &gt; nr / 2 ,然后找到最大的素因子是微不足道的:
我在这里使用了第二种方法。
但是请注意,如果我的号码只是一点点而且只有{600851475013, 600851475053, 600851475067, 600851475149, 600851475151}
之一,那么我的方法假设就会失败,程序将花费很长的时间来运行。如果计算机每秒可执行10米语句,则需要 6.954天才能找到正确的答案。
在您的暴力方法中,只需生成一系列因素就会花费更长的时间 - 假设您之前没有耗尽内存。
当然,在Mathematica中你可以把它写成:
P3[x_] := FactorInteger[x][[-1, 1]]
P3[600851475143]
或仅FactorInteger[600851475143]
,并查找最大值。
这是有效的,因为在Mathematica中你有任意大小的整数。 Java还有一个名为BigInteger
的任意大小的整数类。