我用Java编写了一个计算2的幂的程序,但它看起来非常低效.对于较小的功率(比如2 ^ 4000),它可以在不到一秒的时间内完成.但是,我正在考虑计算2 ^ 43112609,这比最大的已知素数大一个.超过1200万个数字,运行需要很长时间.到目前为止,这是我的代码:
import java.io.*;
public class Power
{
private static byte x = 2;
private static int y = 43112609;
private static byte[] a = {x};
private static byte[] b = {1};
private static byte[] product;
private static int size = 2;
private static int prev = 1;
private static int count = 0;
private static int delay = 0;
public static void main(String[] args) throws IOException
{
File f = new File("number.txt");
FileOutputStream output = new FileOutputStream(f);
for (int z = 0; z < y; z++)
{
product = new byte[size];
for (int i = 0; i < a.length; i++)
{
for (int j = 0; j < b.length; j++)
{
product[i+j] += (byte) (a[i] * b[j]);
checkPlaceValue(i + j);
}
}
b = product;
for (int i = product.length - 1; i > product.length - 2; i--)
{
if (product[i] != 0)
{
size++;
if (delay >= 500)
{
delay = 0;
System.out.print(".");
}
delay++;
}
}
}
String str = "";
for (int i = (product[product.length-1] == 0) ?
product.length - 2 : product.length - 1; i >= 0; i--)
{
System.out.print(product[i]);
str += product[i];
}
output.write(str.getBytes());
output.flush();
output.close();
System.out.println();
}
public static void checkPlaceValue(int placeValue)
{
if (product[placeValue] > 9)
{
byte remainder = (byte) (product[placeValue] / 10);
product[placeValue] -= 10 * remainder;
product[placeValue + 1] += remainder;
checkPlaceValue(placeValue + 1);
}
}
}
Run Code Online (Sandbox Code Playgroud)
这不适用于学校项目或其他任何事情; 就是图个好玩儿.任何有关如何提高效率的帮助将不胜感激!谢谢!
凯尔
PS我没有提到输出应该是基数10,而不是二进制.
Lie*_*yan 22
这里的关键是要注意:
2^2 = 4
2^4 = (2^2)*(2^2)
2^8 = (2^4)*(2^4)
2^16 = (2^8)*(2^8)
2^32 = (2^16)*(2^16)
2^64 = (2^32)*(2^32)
2^128 = (2^64)*(2^64)
... and in total of 25 steps ...
2^33554432 = (2^16777216)*(16777216)
Run Code Online (Sandbox Code Playgroud)
然后:
2^43112609 = (2^33554432) * (2^9558177)
Run Code Online (Sandbox Code Playgroud)
你可以(2^9558177)使用相同的方法找到剩余的,因为(2^9558177 = 2^8388608 * 2^1169569),你可以找到2^1169569使用相同的方法,因为(2^1169569 = 2^1048576 * 2^120993),你可以找到2^120993使用相同的方法,等等......
编辑:以前在这一部分有一个错误,现在它已修复:
此外,通过注意:进一步简化和优化:
2^43112609 = 2^(0b10100100011101100010100001)
2^43112609 =
(2^(1*33554432))
* (2^(0*16777216))
* (2^(1*8388608))
* (2^(0*4194304))
* (2^(0*2097152))
* (2^(1*1048576))
* (2^(0*524288))
* (2^(0*262144))
* (2^(0*131072))
* (2^(1*65536))
* (2^(1*32768))
* (2^(1*16384))
* (2^(0*8192))
* (2^(1*4096))
* (2^(1*2048))
* (2^(0*1024))
* (2^(0*512))
* (2^(0*256))
* (2^(1*128))
* (2^(0*64))
* (2^(1*32))
* (2^(0*16))
* (2^(0*8))
* (2^(0*4))
* (2^(0*2))
* (2^(1*1))
Run Code Online (Sandbox Code Playgroud)
另请注意 2^(0*n) = 2^0 = 1
使用这种算法,可以计算的表2^1,2^2,2^4,2^8,2^16... 2^3355443225个乘法.然后,您可以转换43112609为其二进制表示,并2^43112609使用少于25次乘法轻松找到.总的来说,您需要使用少于50次的乘法来查找介于0和67108864之间的任何2^n位置n.
设n = 43112609.
假设:您想要以十进制打印2 ^ n .
填充比二进制代表2 ^ n的位向量是微不足道的,将该数字转换为十进制表示法需要一段时间.例如,java.math.BigInteger.toString的实现需要O(n ^ 2)次操作.这可能就是原因
BigInteger.ONE.shiftLeft(43112609).toString()
Run Code Online (Sandbox Code Playgroud)
执行一小时后仍然没有终止...
让我们从你的算法的渐近分析开始.你的外循环将执行n次.对于每次迭代,您将执行另一个O(n ^ 2)操作.也就是说,您的算法为O(n ^ 3),因此预期可扩展性较差.
您可以通过使用将其减少到O(n ^ 2 log n)
x ^ 64 = x ^(2*2*2*2*2*2)=(((((((x ^ 2)^ 2)^ 2)^ 2)^ 2)^ 2
(只需要8次乘法)而不是64次乘法
x ^ 64 = x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X
(推广到任意指数留给你作为练习.提示:将指数写为二进制数 - 或者看看Lie Ryan的答案).
为了加速乘法,您可以使用Karatsuba算法,将总运行时间减少到O(n ^((log 3)/(log 2))log n).
如上所述,2的幂对应于二进制数字.二进制是基数2,因此每个数字是前一个数字的两倍.
例如:
1 = 2^0 = b1
2 = 2^1 = b10
4 = 2^2 = b100
8 = 2^3 = b1000
...
Run Code Online (Sandbox Code Playgroud)
二进制是基数2(这就是为什么它被称为"基数2",2是指数的基数),所以每个数字是前一个数字的两倍.移位运算符(大多数语言中的"<<")用于将每个二进制数字向左移位,每个移位相当于乘以2.
例如:
1 << 6 = 2^6 = 64
Run Code Online (Sandbox Code Playgroud)
作为这样一个简单的二进制操作,大多数处理器可以非常快速地为可以放入寄存器的数字(8-64位,取决于处理器)执行此操作.使用更大的数字需要某种类型的抽象(例如Bignum),但它仍然应该是一个非常快速的操作.不过,这样做43112609位需要一些工作.
为了给你一点背景,2 << 4311260(缺少最后一位数)是1297181位长.确保您有足够的RAM来处理输出编号,如果您的计算机没有交换到磁盘,这将削弱您的执行速度.
由于程序非常简单,还可以考虑切换到直接编译成汇编的语言,例如C.
事实上,产生价值是微不足道的(我们已经知道了答案,其中一个是43112609零).将其转换为十进制需要相当长的时间.
| 归档时间: |
|
| 查看次数: |
10201 次 |
| 最近记录: |