大数的有效幂(我说的是Googols)
Efficient Exponentiation For HUGE Numbers (I'm Talking Googols)
我正在解决一个简单的组合问题,其解为2^(n-1)。
唯一的问题是1<=n<=2^31-1(带符号32位整数的最大值)
我试着使用Java的BigInteger类,但对于2^31/10^4及更大的数字,它会超时,所以这显然不起作用。
此外,我仅限于使用Java或C++的内置类。
知道我需要速度,我选择用C++构建一个对字符串进行运算的类。
现在,当我进行乘法运算时,我的程序会像我们在纸上进行乘法运算一样进行乘法运算,以提高效率(而不是重复添加字符串)。
但即使这样,我也不能把2本身乘以2^31-1倍,这还不够有效。
所以我开始阅读关于这个问题的文本,我找到了。。。
2^n = 2^(n/2) * 2^(n/2) * 2^(n%2)
(其中/表示整数除法,%表示模数)
这意味着我可以用对数乘法来求解幂运算。但对我来说,我无法回避如何将这种方法应用于我的代码?我该如何选择下界?什么是最有效的方法来跟踪我最终乘法所需的各种数字?
如果有人知道如何解决这个问题,请详细说明(感谢示例代码)。
更新
感谢大家的帮助!显然,这个问题是要以现实的方式解决的,但我确实设法通过只执行ceil(log2(n))迭代的幂函数来超越java.math.BigInteger
。
如果有人对我制作的代码感兴趣,这里是…
using namespace std;
bool m_greater_or_equal (string & a, string & b){ //is a greater than or equal to b?
if (a.length()!=b.length()){
return a.length()>b.length();
}
for (int i = 0;i<a.length();i++){
if (a[i]!=b[i]){
return a[i]>b[i];
}
}
return true;
}
string add (string& a, string& b){
if (!m_greater_or_equal(a,b)) return add(b,a);
string x = string(a.rbegin(),a.rend());
string y = string(b.rbegin(),b.rend());
string result = "";
for (int i = 0;i<x.length()-y.length()+1;i++){
y.push_back('0');
}
int carry = 0;
for (int i =0;i<x.length();i++){
char c = x[i]+y[i]+carry-'0'-'0';
carry = c/10;
c%=10;
result.push_back(c+'0');
}
if (carry==1) result.push_back('1');
return string(result.rbegin(),result.rend());
}
string multiply (string&a, string&b){
string row = b, tmp;
string result = "0";
for (int i = a.length()-1;i>=0;i--){
for (int j= 0;j<(a[i]-'0');j++){
tmp = add(result,row);
result = tmp;
}
row.push_back('0');
}
return result;
}
int counter = 0;
string m_pow (string&a, int exp){
counter++;
if(exp==1){
return a;
}
if (exp==0){
return "1";
}
string p = m_pow(a,exp/2);
string res;
if (exp%2==0){
res = "1"; //a^exp%2 is a^0 = 1
} else {
res = a; //a^exp%2 is a^1 = a
}
string x = multiply(p,p);
return multiply(x,res);
//return multiply(multiply(p,p),res); Doesn't work because multiply(p,p) is not const
}
int main(){
string x ="2";
cout<<m_pow(x,5000)<<endl<<endl;
cout<<counter<<endl;
return 0;
}
正如@Oli的回答所提到的,这不是计算2^n
的问题,因为它只是二进制的1
和0
s。
但是,既然你想用十进制打印出来,那么对于非常大的数字,如何从二进制转换为十进制就成了一个问题。
我的答案是这不现实(我希望这个问题只是出于好奇。)
您提到尝试计算2^(2^31 - 1)
并以十进制形式打印出来。该数字为646456993位数字长。
- JavaBigInteger做不到。它适用于小数字,并使用
O(n^2)
算法 - 正如评论中提到的,C++中没有内置的BigNum库
- 即使Mathematica也无法处理:
General::ovfl : Overflow occurred in computation.
- 您最好的选择是使用GMP库
如果你只是想看看部分答案:
2^(2^31 - 1) = 2^2147483647 =
880806525841981676603746574895920 ... 7925005662562914027527972323328
(total: 646,456,993 digits)
这是使用一个来源密切的库完成的,在Core i7 2600K@4.4 GHz上花费了大约37秒和3.2 GB的内存,包括将6.46亿个数字全部写入一个巨大的文本文件所需的时间
(记事本打开文件的时间比计算文件所需的时间长。)
现在,为了回答你关于如何在一般情况下实际计算这样一个幂的问题,@dasblinkenlight给出了这个问题的答案,它是平方幂的一个变体。
对于大数字,从二进制转换为十进制是一项困难得多的任务。这里的标准算法是"分治"转换。
我不建议您尝试实现后者,因为它远远超出了启动程序员的范围。(也有点数学密集型)
您根本不需要进行任何乘法运算。2^(n-1)只是1 << (n-1)
,即1后面跟着(n-1)
零(二进制)。
在代码中应用此方法的最简单方法是以最直接的方式递归地应用它。它适用于任何数字a
,而不仅仅适用于2
,所以我编写了以a
为参数的代码,以使其更有趣:
MyBigInt pow(MyBigInt a, int p) {
if (!p) return MyBigInt.One;
MyBigInt halfPower = pow(a, p/2);
MyBigInt res = (p%2 == 0) ? MyBigInt.One : a;
return res * halfPower * halfPower;
}
- 欧拉项目#8答案是大以获得有效答案
- 调整大小后指向元素值的指针unordered_map有效?
- 为什么是0;C++中的有效语句
- 最高有效数字侧的第N位
- GCC对可能有效的代码抛出init list生存期警告
- 有效地使用std::unordered_map来插入或增加键的值
- c++中O(n^(1/3))中一个数的除数的有效计数
- 使用无符号字符数组有效存储内存
- 自定义先决条件对移动分配运算符有效吗
- 为什么将值返回函数传递给重载=运算符对运算符函数有效,而对其他运算符无效
- 有哪些有效的方法可以消除一组 100 万个字符串>重复数据?
- 为什么这种直接初始化有效?(C++17)
- 递归函数有效,但无法记忆
- 在C++中初始化向量映射的最有效方法
- 如果变量名称不跟在 char* 后面,const char* 是否有效?
- 钳制迭代器是否有效
- 如何有效地在 std::vector 中插入一对?
- C++模板 t 不是有效的模板类型
- 检查由括号、方括号和大括号组成的一组方括号是否有效?
- 大数的有效幂(我说的是Googols)