我已经实现了两个字节数组的乘法,它工作正常。更确切地说,我需要将64字节操作数与32字节操作数相乘。
我以最简单的方式实现它:我进行双循环,并为每个数组中的每个字节计算产品。
因此,对于特定值,需要64 * 32 = 2048
步。
我尝试使用Karatsuba
方法对其进行优化。
所以我按照以下方式进行:
a
的长度为64个字节,b
的长度为32个字节。
我们将a
分成:a = p * 16^(32) + q
(所以p
和q
的长度均为32字节)和计算:a * b = p * b * 16^(32) + q * b
(产品是计算的)我之前描述的功能。)
我得到了正确的结果,但计算需要相同的时间:2个32字节数组的2次乘法:32 * 32 * 2 = 64 * 32 = 2048
。
我的问题如下:使用Karatsuba
优化我的乘法,我应该完全递归编程吗?以其他方式它永远不会更快?
提前谢谢你:)
答案 0 :(得分:3)
哇!我作为程序员的第一份工作之一是优化COBOL运行时系统的乘法算法 - 这是31年前的事。
我认为你会发现有效的技术是将字节组合成更大的单位。当时只有32位可用,因此两个字节合并为一个短路,短路相乘以得到一个32位的int。但是在Java中你有64位可用,所以你可以乘以两个整数来获得一个长的。
因此,你应该通过添加字节来创建一个16个整数的数组 a1 和一个数组 b1 8个整数。例如。有时这样:
a1[0] = (a[0] << 24) + (a[1] << 16) + (a[2] << 8) + a[3]
或者你可以写一个循环来更简洁地做到这一点。
然后乘以a1和b1,这应该进行128次操作。
我会担心溢出和已签名与无符号值。最高位后面的数字应该是无符号的,但Java没有无符号修饰符。但是,在Java 8中,对未签名操作有一些支持:请参阅Primitive Data Types。
如果无法使用ints / long进行无符号处理,则可以将2或3个字节的组合组合成int并浪费一些顶部位,为符号位提供空间。
答案 1 :(得分:2)
是的,Karatsuba算法只有在递归的情况下才有效。但请记住:Karatsuba 并不总是比更快,需要O(n^2)
(通常我们假设两个数字具有相同的长度,如果我们要增加大数字)。对于小输入(可以是1,它可以是15,取决于你的CPU),简单的算法可以更快,因此Karatsuba的最佳使用是:
size > MIN_SIZE_FOR_KARATSUBA
(你必须通过实验找到它),然后进行拆分并递归调用Karatsuba。size <= MIN_SIZE_FOR_KARATSUBA
,则只需将它们与简单算法相乘。另外,不要在乘法中将数组拆分为字节,将它们存储在 base 1000 中的整数或类似的内容中,因为您可以轻松地溢出类型。
描述了Karatsuba算法的最佳实现in this link。通常Karatsuba会占用O(n log n)
内存,但这里有一些技巧只需要O(n)
内存。
如果您不想多次使用函数调用(因为函数调用是编程中最慢的操作),那么您可以自己使用循环并实现堆栈,如my implementation中所示。