我已经为我的教育目标实施了Karatsuba乘法算法。现在我正在寻找进一步的改进。我已经实现了某种长算法,并且无论我是否使用超过 100 的整数表示的基础,它都能正常工作。
基数 10 并使用范围clang++ -O3
中的两个随机整数的[10^50000, 10^50001]
进行编译:
Naive algorithm took me 1967 cycles (1.967 seconds)
Karatsuba algorithm took me 400 cycles (0.4 seconds)
基数 100 的相同数字:
Naive algorithm took me 409 cycles (0.409 seconds)
Karatsuba algorithm took me 140 cycles (0.14 seconds)
有没有办法改善这个结果? 现在我使用这样的函数来完成我的结果:
void finalize(vector<int>& res) {
for (int i = 0; i < res.size(); ++i) {
res[i + 1] += res[i] / base;
res[i] %= base;
}
}
正如您所看到的那样,它计算每个步骤并将其推送到下一个数字。如果我采用基础>=1000
,结果将会溢出。
如果你看到我的代码,我使用int的向量来表示长整数。根据我的基数,一个数字将分为向量的不同部分。 现在我看到几个选项:
long long
类型,但对于长度很大的整数也可能会溢出在我看到一些评论后,我决定扩大这个问题。假设我们想要将长整数表示为int的向量。对于instanse:
ULLONG_MAX = 18446744073709551615
对于输入,我们传递第210个Fibonacci数34507973060837282187130139035400899082304280
,它不适合任何标准类型。如果我们在带有10000000的int的向量中表示它,它将类似于:
v[0]: 2304280
v[1]: 89908
v[2]: 1390354
v[3]: 2187130
v[4]: 6083728
v[5]: 5079730
v[6]: 34
当我们进行乘法时,我们可能得到(为简单起见,它是两个相同的数字)
(34507973060837282187130139035400899082304280)^2
:
v[0] * v[0] = 5309706318400
...
v[0] * v[4] = 14018612755840
...
这只是第一行,我们必须做这样的六个步骤。当然,某些步骤会在乘法或进位计算后引起溢出。
如果我错过了什么,请告诉我,我会改变它。 如果您想查看完整版,则会在我的github
上答案 0 :(得分:0)
Base 2^64
和base 2^32
是进行高精度算术的最常用基础。通常,数字存储在无符号整数类型中,因为它们在溢出方面具有良好的语义。
例如,可以通过以下方式检测添加的进位:
uint64_t x, y; // initialize somehow
uint64_t sum = x + y;
uint64_t carry = sum < x; // 1 if true, 0 if false
此外,汇编语言通常有一些“随附携带”指令;如果你可以编写内联汇编(或访问内在函数),你可以利用这些。
对于乘法,大多数计算机都有机器指令,可以计算一个机器字 - &gt;两机字产品;有时,获得两半的指令称为“乘法高”和“乘低”。您需要编写汇编来获取它们,尽管许多编译器提供了更大的整数类型,使用它们可以访问这些指令:例如在gcc
中,你可以实现乘以hi
uint64_t mulhi(uint64_t x, uint64_t y)
{
return ((__uint128_t) x * y) >> 64;
}
当人们不能使用它时,他们会在2^32
中进行乘法运算,以便他们可以使用相同的方法来实现便携式mulhi指令,使用uint64_t
作为两位数类型。
如果你想编写有效的代码,你真的需要利用这些更大的乘法指令。基数2^32
中的数字乘以基数10
中的数字乘以数字的功能的九十倍以上。基数2^64
中的乘数比其强大四倍。而且你的计算机可能比你为基数10乘法实现的任何东西都更快。