n 是拉马努金数吗——为什么我在 2^63 附近的值中出现错误?



给定一个数字,测试它是否是拉马努金数(在我们的课程中定义为两个立方体以两种不同方式的总和(。它必须在 n^(1/3( 时间内运行。

我的代码有时是有效的。当测试值接近 2^63 -1 时,我得到一些随机错误。

奇怪的是,在更改计数器的起始值以修复不同的错误之前,我正在通过该范围内的数字的测试。谁能告诉我为什么会这样?

我设置了一个 for 循环来为 a^3 创建值。

然后我为 b=(n-a^3(^(1/3( 设置值。

然后我测试 b 看看它是否是一个整数。如果是这样,请打破循环。

在这里插入了一个 if 测试以使代码工作,尽管我不知道为什么需要这样做,这就是这个问题的要点。这个 if 语句为高于和低于 n=2^63 的值设置了两个不同的 for 循环

n <2^63 的第二个循环,从 c=a+1 开始,所以我不重复。它与第一个相同。

n> 2^63 的第二个循环从 c=a 开始。

为什么这会有所作为?为什么相同的代码不适用于更小和更大的数字?

对不起,幼稚的代码,我才刚刚开始,很多功能在我的课程中都是禁止的。(例如,我不能使用 floor((,也不允许为它编写自己的函数(。

public class Ramanujan {
public static boolean isRamanujan(long n) {
if (n <= 0) return false;
long a3 = 0;
long c3 = 0;
double b = 0;
double d = 0;
for (int a = 1; a < n; a++) {
a3 = (long) a * a * a;
if (a3 > n) break;
b = Math.cbrt(n - a3);
if (b == (int) b) break;
}
if (n > Math.pow(2, 62)) {
for (int c = (int) Math.cbrt(a3); c < n; c++) {
c3 = (long) c * c * c;
if (c3 > n) break;
d = Math.cbrt(n - c3);
if (d == (int) d) break;
}
}
else {
for (int c = (int) Math.cbrt(a3) + 1; c < n; c++) {
c3 = (long) c * c * c;
if (c3 > n) break;
d = Math.cbrt(n - c3);
if (d == (int) d) break;
}
}
if (a3 + (long) b * b * b == c3 + (long) d * d * d && b * b * b != c3) 
return true;
return false;
}
public static void main(String[] args) {
long n = Long.parseLong(args[0]);
StdOut.println(isRamanujan(n));
}
}

关于为什么我需要区分较大和较小的数字的任何见解?

n超过long可以保持的值时,你会得到奇怪的结果,即Math.pow(2, 63) == Long.MAX_VALUE.此时,n将经历数字溢出。

final long l = Long.MAX_VALUE; // == 2^63
System.out.println(l); // 9223372036854775807
System.out.println(l + 1); // -9223372036854775808

Math.cbrt(a3)甚至Math.cbrt(n - a3)超出类型int的范围时,由于算术溢出,您会得到大int值的随机错误。 应对所有整数变量使用 typelong以减少这种可能性,并确保中间结果不超过类型long的范围。

下面是一个使用单个循环的更简单的实现,计算方法的数量:

public class Ramanujan {
public static boolean isRamanujan(long n) {
if (n <= 0) return false;
int count = 0;
for (long a = 1;; a++) {
long a3 = a * a * a;
if (a3 > n - a3) break;
long b = (long)Math.cbrt(n - a3);
if (a3 + b * b * b == n) count++;
}
return count >= 2;
}
public static void main(String[] args) {
if (args.length == 1) {
long n = Long.parseLong(args[0]);
StdOut.println(isRamanujan(n));
} else
if (args.length == 2) {
long n1 = Long.parseLong(args[0]);
long n2 = Long.parseLong(args[1]);
for (long n = n1; n <= n2; n++) {
if (isRamanujan(n))
StdOut.println(n);
if (n == Long.MAX_VALUE) // handle n2 == Long.MAX_VALUE
break;
}
} else {
StdOut.println("usage: Ramanujan n1 [n2]");
}
}
}

长整型可以容纳的最大数字(在Java中(是(2 ^ 63(-1(Long.MAX_VALUE(。 你为什么要计算 Math.cbrt(a3( ?如果 a3 = a * a * a,那么你已经知道是什么 Math.cbrt(a3( 是。

如果 n> 9223372036854774272,则代码中存在问题 Math.cbrt of 9223372036854774273 is 2097152 如果你立方体,你会因为溢出而得到一个负数。

问题在于将类型inta and c变量相乘以计算立方体。需要cast每个变量以long正在相乘的变量。

示例,a3 = (long) a * (long) a * (long) a;

相关内容

最新更新