C ++中俄罗斯农民算法中的整数溢出
Integer overflow in Russian Peasant Algorithm in c++
我遇到了俄罗斯农民求幂 (RPE) Link is here 的问题,它计算指数的速度比寻找 x 的 n 次方的传统方法快得多。
常规方法
int power(int base, int exponent) {
int result = 1;
for(register int i = 1; i <= exponent; i++) {
result *= base;
}
return result;
}
我实现了复数算法,因为乘法会导致溢出我正在打印 re(z) mod m
和 im(z) mod m
作为 2 space 分隔的整数,但我的实现是不正确,因为它导致了一些奇怪的答案,任何人都可以指出问题以及如何纠正它。这是我的代码
#include<iostream>
#include<complex>
using namespace std;
class Solution {
int m;
long long int k;
complex<long long int> num;
complex<long long int> russianPeasantExponentiation(), multiply(complex<long long int>, complex<long long int>);
public:
void takeInput(), solve();
};
void Solution::takeInput() {
int a, b;
cin >> a >> b >> k >> m;
num = complex<long long int> (a, b);
}
void Solution::solve() {
complex<long long int> res = russianPeasantExponentiation();
cout << real(res) << " " << imag(res) << endl;
}
complex<long long int> Solution::russianPeasantExponentiation() {
complex<long long int> temp1(1, 0), temp2 = num;
while(k) {
if(k % 2) {
temp1 = multiply(temp1, temp2);
}
temp2 = multiply(temp2, temp2);
k /= 2;
}
return temp1;
}
complex<long long int> Solution::multiply(complex<long long int> a, complex<long long int> b) {
long long int ar = real(a), ai = imag(a), br = real(b), bi = imag(b);
complex<long long int> result(((ar * br) % m - (ai * bi) % m) % m, ((ar * bi)%m + (ai * br)%m)%m);
return result;
}
int main() {
int q;
cin >> q;
while(q--) {
Solution obj;
obj.takeInput();
obj.solve();
}
return 0;
}
问题指出输入包含一个整数 q
,它定义了编号。的查询。每个查询由 4 个数字组成,由 space a, b, k, m
分隔。对于每个查询,我必须找到 z = (a + ib)^k
,因为 re(z)
和 im(z)
的值可能非常大,所以我必须打印 re(z) mod m
和 im(z) mod m
问题出现在测试用例中
8 2 10 1000000000
预期输出是 880332800 927506432
而我的输出是 -119667200 -72493568
你需要更换
((ar * br) % m - (ai * bi) % m) % m
和
((ar * br) % m + m - (ai * bi) % m) % m
因为上面的表达式可以得到负值
这是一个非常巧妙的算法,我最终自己写了一个!
我看不出在计算过程中减少中间结果如何使数学运算有效,恰恰相反。
虽然使用 complex 有效。
我计划将此算法添加到我的工具箱中,因此它与您的实现有点不同。我使用了这篇论文的算法,它减少了 1 个乘法。负数取模的处理方式在main()
#include <complex>
#include <iostream>
template <typename T>
T fastExp(T x, unsigned int e)
{
if (e == 0)
return T(1);
while (!(e & 1))
{
x *= x;
e >>= 1;
}
auto y = x;
e >>= 1;
while (e)
{
x *= x;
if (e & 1)
y *= x;
e >>= 1;
}
return y;
}
int main()
{
std::complex<double> x{ 8, 2 };
auto y = fastExp(x, 10);
long long k = 1000000000LL;
std::complex<double> z;
y -= { floor(y.real() / k), floor(y.imag() / k) };
std::complex<long long> r{ (long long)y.real(), (long long)y.imag() };
while (r.real() < 0)
r._Val[0] += k;
while (r.imag() < 0)
r._Val[1] += k;
std::cout << "result: " << r.real() << " + " << r.imag() << " i" << "\n";
}
我遇到了俄罗斯农民求幂 (RPE) Link is here 的问题,它计算指数的速度比寻找 x 的 n 次方的传统方法快得多。
常规方法
int power(int base, int exponent) {
int result = 1;
for(register int i = 1; i <= exponent; i++) {
result *= base;
}
return result;
}
我实现了复数算法,因为乘法会导致溢出我正在打印 re(z) mod m
和 im(z) mod m
作为 2 space 分隔的整数,但我的实现是不正确,因为它导致了一些奇怪的答案,任何人都可以指出问题以及如何纠正它。这是我的代码
#include<iostream>
#include<complex>
using namespace std;
class Solution {
int m;
long long int k;
complex<long long int> num;
complex<long long int> russianPeasantExponentiation(), multiply(complex<long long int>, complex<long long int>);
public:
void takeInput(), solve();
};
void Solution::takeInput() {
int a, b;
cin >> a >> b >> k >> m;
num = complex<long long int> (a, b);
}
void Solution::solve() {
complex<long long int> res = russianPeasantExponentiation();
cout << real(res) << " " << imag(res) << endl;
}
complex<long long int> Solution::russianPeasantExponentiation() {
complex<long long int> temp1(1, 0), temp2 = num;
while(k) {
if(k % 2) {
temp1 = multiply(temp1, temp2);
}
temp2 = multiply(temp2, temp2);
k /= 2;
}
return temp1;
}
complex<long long int> Solution::multiply(complex<long long int> a, complex<long long int> b) {
long long int ar = real(a), ai = imag(a), br = real(b), bi = imag(b);
complex<long long int> result(((ar * br) % m - (ai * bi) % m) % m, ((ar * bi)%m + (ai * br)%m)%m);
return result;
}
int main() {
int q;
cin >> q;
while(q--) {
Solution obj;
obj.takeInput();
obj.solve();
}
return 0;
}
问题指出输入包含一个整数 q
,它定义了编号。的查询。每个查询由 4 个数字组成,由 space a, b, k, m
分隔。对于每个查询,我必须找到 z = (a + ib)^k
,因为 re(z)
和 im(z)
的值可能非常大,所以我必须打印 re(z) mod m
和 im(z) mod m
问题出现在测试用例中
8 2 10 1000000000
预期输出是 880332800 927506432
而我的输出是 -119667200 -72493568
你需要更换
((ar * br) % m - (ai * bi) % m) % m
和
((ar * br) % m + m - (ai * bi) % m) % m
因为上面的表达式可以得到负值
这是一个非常巧妙的算法,我最终自己写了一个!
我看不出在计算过程中减少中间结果如何使数学运算有效,恰恰相反。
虽然使用 complex
我计划将此算法添加到我的工具箱中,因此它与您的实现有点不同。我使用了这篇论文的算法,它减少了 1 个乘法。负数取模的处理方式在main()
#include <complex>
#include <iostream>
template <typename T>
T fastExp(T x, unsigned int e)
{
if (e == 0)
return T(1);
while (!(e & 1))
{
x *= x;
e >>= 1;
}
auto y = x;
e >>= 1;
while (e)
{
x *= x;
if (e & 1)
y *= x;
e >>= 1;
}
return y;
}
int main()
{
std::complex<double> x{ 8, 2 };
auto y = fastExp(x, 10);
long long k = 1000000000LL;
std::complex<double> z;
y -= { floor(y.real() / k), floor(y.imag() / k) };
std::complex<long long> r{ (long long)y.real(), (long long)y.imag() };
while (r.real() < 0)
r._Val[0] += k;
while (r.imag() < 0)
r._Val[1] += k;
std::cout << "result: " << r.real() << " + " << r.imag() << " i" << "\n";
}