【问题标题】:Integer overflow in Russian Peasant Algorithm in c++C ++中俄罗斯农民算法中的整数溢出
【发布时间】:2017-06-28 09:42:24
【问题描述】:

我遇到了俄罗斯农民指数 (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 mim(z) mod m 打印为 2 个空格分隔的整数,但我的实现不正确,因为它会导致一些奇怪的答案任何人指出问题,以及如何纠正它。这是我的代码

#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 个用空格分隔的数字组成 a, b, k, m。对于每个查询,我必须找到z = (a + ib)^k,因为re(z)im(z) 的值可能非常大,所以我必须打印re(z) mod mim(z) mod m

问题出现在测试用例中 8 2 10 1000000000 预期输出是 880332800 927506432 而我的输出是 -119667200 -72493568

【问题讨论】:

  • 什么是奇怪的答案?展示一些你得到的预期输出/输出的例子
  • 补充一下 gdelab 的建议,如果您为此编写测试工具,它可能会为您提供很好的服务。
  • 请花点时间阅读How to debug small programs。为了帮助您进行调试,请尝试将大型表达式分解为较小的表达式,以便更轻松地查看中间结果。
  • 您对代码进行了过度设计。这不应该是一个类。读取输入是一个与求解幂完全无关的问题。我也不清楚您使用复数的原因:它们不会让溢出神奇地消失。
  • 使用调试器!

标签: c++ algorithm integer-overflow


【解决方案1】:

你需要更换

((ar * br) % m - (ai * bi) % m) % m

((ar * br) % m + m - (ai * bi) % m) % m

因为你可以通过上面的表达式得到一个负值

【讨论】:

  • @MichaëlRoy,但为什么呢?表达式末尾有% m
  • 如果(ar * br) % m &gt;= 0那么((ar * br) % m + m &gt;= m这是不对的。
  • 无论如何,在计算 x**y 时使用中间值的任意模数在数学上并不合理。
  • @MichaëlRoy,没关系,因为最后我们得到了除以 m 的余数。
【解决方案2】:

这是一个非常简洁的算法,我最终自己编写了!

我看不出在计算过程中减少中间结果会如何使数学工作,恰恰相反。

不过,使用 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";
}

【讨论】:

    猜你喜欢
    • 2010-09-20
    • 2013-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-27
    相关资源
    最近更新 更多