Java 中没有 BigInteger 的 Karatsuba 算法,递归时的意外行为
Karatsuba Algorithm without BigInteger in Java, unexpected behaviour while recursion
所以我想 运行 Karatsuba 算法而不使用 Java 中的 class BigInteger,所以在遵循伪代码和 this question 之后,我得到了以下内容代码
public static long recKaratsuba(long i1, long i2){
if(i1<10 || i2<10) {
return i1*i2 ;
}
long len = Math.round(Long.toString(Math.max(i1,i2)).length());
long N = Math.round(len/2) ;
long b = (long) (i1% Math.pow(10, N)) ;
long a = (long) (i1/ Math.pow(10,N));
long d = (long) (i2% Math.pow(10,N)) ;
long c = (long) (i2/ Math.pow(10,N));
//System.out.println("a,b,c,d :" + a + b + c + d);
long ac = recKaratsuba(a, c) ;
long bd = recKaratsuba(b, d) ;
long pos = recKaratsuba(a+b,c+d) ;
return ((long)(bd + ac*Math.pow(10,len) + (pos -ac -bd)*Math.pow(10,N) )) ;
}
现在,问题在于它产生了错误的答案,1234*5678 给出了 11686652,这应该是 7006652。
作为 Java 和算法的初学者,我无法查明这段代码中的确切错误,而且我确实意识到这个程序非常低效并且不能处理超过 4 位数字(根据链接 question)。但这是我在学习伪代码后直观地想到的。
所以我的问题是,我的代码中存在什么问题以及如何在不使用 BigInteger 方法的情况下执行以下算法?
我注意到一些事情:
- 而不是 i1 和 i2 可能使用 x 和 y
- 变量len和N是int,不是long
- 无需对字符串表示的最大长度进行四舍五入:长度为整数,整数为整数且无法四舍五入
- 除法不需要除以2:整数除法总是整数(整数除法完成)
- 错误在 return 语句中:
Math.pow(10, len)
应该是 Math.pow(10, 2 * N)
,如果 N 不均匀,这很重要
- 避免多次相同的计算:尤其是
Math.pow(10, N)
固定代码为我测试过的所有示例提供了正确的结果。
public static long recKaratsuba2(long x, long y) {
if (x < 10 || y < 10) {
return x * y;
}
int len = Long.toString(Math.max(x, y)).length();
double den = Math.pow(10, len / 2);
long a = (long) (x / den);
long b = (long) (x % den);
long c = (long) (y / den);
long d = (long) (y % den);
long ac = recKaratsuba2(a, c);
long bd = recKaratsuba2(b, d);
long pos = recKaratsuba2(a + b, c + d);
return (long) (bd + den * (ac * den + (pos - ac - bd)));
}
所以我想 运行 Karatsuba 算法而不使用 Java 中的 class BigInteger,所以在遵循伪代码和 this question 之后,我得到了以下内容代码
public static long recKaratsuba(long i1, long i2){
if(i1<10 || i2<10) {
return i1*i2 ;
}
long len = Math.round(Long.toString(Math.max(i1,i2)).length());
long N = Math.round(len/2) ;
long b = (long) (i1% Math.pow(10, N)) ;
long a = (long) (i1/ Math.pow(10,N));
long d = (long) (i2% Math.pow(10,N)) ;
long c = (long) (i2/ Math.pow(10,N));
//System.out.println("a,b,c,d :" + a + b + c + d);
long ac = recKaratsuba(a, c) ;
long bd = recKaratsuba(b, d) ;
long pos = recKaratsuba(a+b,c+d) ;
return ((long)(bd + ac*Math.pow(10,len) + (pos -ac -bd)*Math.pow(10,N) )) ;
}
现在,问题在于它产生了错误的答案,1234*5678 给出了 11686652,这应该是 7006652。 作为 Java 和算法的初学者,我无法查明这段代码中的确切错误,而且我确实意识到这个程序非常低效并且不能处理超过 4 位数字(根据链接 question)。但这是我在学习伪代码后直观地想到的。
所以我的问题是,我的代码中存在什么问题以及如何在不使用 BigInteger 方法的情况下执行以下算法?
我注意到一些事情:
- 而不是 i1 和 i2 可能使用 x 和 y
- 变量len和N是int,不是long
- 无需对字符串表示的最大长度进行四舍五入:长度为整数,整数为整数且无法四舍五入
- 除法不需要除以2:整数除法总是整数(整数除法完成)
- 错误在 return 语句中:
Math.pow(10, len)
应该是Math.pow(10, 2 * N)
,如果 N 不均匀,这很重要 - 避免多次相同的计算:尤其是
Math.pow(10, N)
固定代码为我测试过的所有示例提供了正确的结果。
public static long recKaratsuba2(long x, long y) {
if (x < 10 || y < 10) {
return x * y;
}
int len = Long.toString(Math.max(x, y)).length();
double den = Math.pow(10, len / 2);
long a = (long) (x / den);
long b = (long) (x % den);
long c = (long) (y / den);
long d = (long) (y % den);
long ac = recKaratsuba2(a, c);
long bd = recKaratsuba2(b, d);
long pos = recKaratsuba2(a + b, c + d);
return (long) (bd + den * (ac * den + (pos - ac - bd)));
}