繁体   English   中英

添加负数和正数最多 10^100000

[英]Adding negative and positive numbers up to 10^100000

我一直在尝试解决这个问题(从学校开始)大约一个星期了。 我们得到了两个数字,从 -(10^100000) 到 +that。

当然最简单的解决方案是实现书面加法,所以我就是这样做的。 我决定使用两个函数将数字存储为字符串:

int ti(char a) { // changes char to int
    int output = a - 48;
    return output;
}

char tc(int a) { // changes int to char
    char output = a + 48;
    return output;
}

这样我就可以存储负数,比如-2。 考虑到这一点,我实现了一个 toMinus 函数:

void toMinus(std::string &a) { // 123 -> -1 -2 -3
    for (auto &x : a) {
        x = tc(-ti(x));
    }
}

我还创建了一个 changeSize 函数,它将 0 添加到数字的开头,直到它们都是最大大小 + 1 和 removeZeros,这将删除前导零:

void changeSize(std::string &a, std::string &b) {
    size_t exp_size = std::max(a.size(), b.size()) + 2;
    while (a.size() != exp_size) {
        a = '0' + a;
    }

    while (b.size() != exp_size) {
        b = '0' + b;
    }
}

void removeZeros(std::string &a) {
    int i = 0;    
    for (; i < a.size(); i++) {
        if (a[i] != '0') {
            break;
        }
    }
    a.erase(0, i);
    if (a.size() == 0) {
        a = "0";
    }
}

毕竟,我创建了主要的 add() 函数:

std::string add(std::string &a, std::string &b) {
    bool neg[2] = {false, false};
    bool out_negative = false;
    if (a[0] == '-') {
        neg[0] = true;
        a.erase(0, 1);
    }
    if (b[0] == '-') {
        neg[1] = true;
        b.erase(0, 1);
    }

    changeSize(a, b);

    if (neg[0] && !(neg[1] && neg[0])) {
        toMinus(a);
    }
    if(neg[1] && !(neg[1] && neg[0])) {
        toMinus(b);
    }

    if (neg[1] && neg[0]) {
        out_negative = true;
    }

    // Addition
    for (int i = a.size() - 1; i > 0; i--) {
        int _a = ti(a[i]);
        int _b = ti(b[i]);
        int out = _a + _b;
        if (out >= 10) {
            a[i - 1] += out / 10;
        } else if (out < 0) {
            if (abs(out) < 10) {
                a[i - 1]--;
            } else {
                a[i - 1] += abs(out) / 10;
            }
            if (i != 1)
                out += 10;
        }

        a[i] = tc(abs(out % 10));
    }

    if (ti(a[0]) == -1) { // Overflow
        out_negative = true;
        a[0] = '0';
        a[1]--;

        for (int i = 2; i < a.size(); i++) {
            if (i == a.size() - 1) {
                a[i] = tc(10 - ti(a[i]));
            } else {
                a[i] = tc(9 - ti(a[i]));
            }
        }
    }

    if (neg[0] && neg[1]) {
        out_negative = true;
    }

    removeZeros(a);

    if (out_negative) {
        a = '-' + a;
    }

    return a;
}

该程序在大多数情况下都有效,尽管我们的学校检查员发现它不起作用 - 而不是

-4400547114413430129608370706728634555709161366260921095898099024156859909714382493551072616612065064

它回来了

-4400547114413430129608370706728634555709161366260921095698099024156859909714382493551072616612065064

我找不到问题是什么。 请帮助并提前感谢您。

pastebin 上的完整代码

虽然我认为您的整体方法对于这个问题是完全合理的,但您的实现似乎有点太复杂了。 试图自己解决这个问题,我想出了这个:

#include <iostream>
#include <limits>
#include <random>
#include <string>

bool greater(const std::string& a, const std::string& b)
{
    if (a.length() == b.length()) return a > b;
    return a.length() > b.length();
}

std::string add(std::string a, std::string b)
{
    std::string out;

    bool aNeg = a[0] == '-';
    if (aNeg) a.erase(0, 1);
    bool bNeg = b[0] == '-';
    if (bNeg) b.erase(0, 1);

    bool resNeg = aNeg && bNeg;
    if (aNeg ^ bNeg && (aNeg && greater(a, b) || bNeg && greater(b, a)))
    {
        resNeg = true;
        std::swap(a, b);
    }

    int i = a.length() - 1;
    int j = b.length() - 1;
    int carry = 0;
    while (i >= 0 || j >= 0)
    {
        const int digitA = (i >= 0) ? a[i] - '0' : 0;
        const int digitB = (j >= 0) ? b[j] - '0' : 0;
        const int sum = (aNeg == bNeg ? digitA + digitB : (bNeg ? digitA - digitB : digitB - digitA)) + carry;
        carry = 0;
        if (sum >= 10) carry = 1;
        else if (sum < 0) carry = -1;

        out = std::to_string((sum + 20) % 10) + out;

        i--;
        j--;
    }
    if (carry) out = '1' + out;
    while (out[0] == '0') out.erase(0, 1);
    if (resNeg) out = '-' + out;
    return out;
}


void test()
{
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> dis(-std::numeric_limits<int32_t>::max(), std::numeric_limits<int32_t>::max());
    for (int i = 0; i < 1000000; ++i)
    {
        const int64_t a = dis(gen);
        const int64_t b = dis(gen);
        const auto expected = std::to_string(a + b);
        const auto actual = add(std::to_string(a), std::to_string(b));
        if (actual != expected) {
            std::cout << "mismatch expected: " << expected << std::endl;
            std::cout << "mismatch actual  : " << actual << std::endl;
            std::cout << "                a: " << a << std::endl;
            std::cout << "                b: " << b << std::endl;
        }
    }
}

int main()
{
    test();
}

它可以进一步优化,但要点是:

  • 如果两个数字的符号相同,我们可以做简单的书面加法。 如果两者都是负数,我们只需在末尾加上-
  • 如果符号不同,我们做书面减法。 如果被减数大于被减数,则没有问题,我们知道结果将为正。 然而,如果被减数更大,我们就必须重新表述这个问题。 例如, 123 - 234我们将公式化为-(234 - 123) 我们可以使用常规的书面减法来解决内部部分,然后我们在前面加上-

我用随机数测试这个,我们可以使用常规整数算法计算出正确的结果。 因为它不会失败,所以我非常有信心它也可以正确地用于更大的输入。 这样的方法还可以帮助您发现实施失败的情况。

除此之外,我认为您应该使用带有调试器的已知失败案例或简单地打印中间步骤的语句以查看失败的位置。 您发布的失败示例中唯一的小差异可能指向处理结转的一些问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM