简体   繁体   English

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

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

I've been trying to solve this problem (from school) for just about a week now.我一直在尝试解决这个问题(从学校开始)大约一个星期了。 We're given two numbers, from -(10^100000) to +that.我们得到了两个数字,从 -(10^100000) 到 +that。

Of course the simplest solution is to implement written addition, so that's what I did.当然最简单的解决方案是实现书面加法,所以我就是这样做的。 I decided, that I would store the numbers as strings, using two functions:我决定使用两个函数将数字存储为字符串:

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;
}

This way I can store negative digits, like -2.这样我就可以存储负数,比如-2。 With that in mind I implemented a toMinus function:考虑到这一点,我实现了一个 toMinus 函数:

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

I also created a changeSize function, which adds 0 to the beginning of the number until they are both their max size + 1 and removeZeros, which removes leading zeros:我还创建了一个 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";
    }
}

After all that, I created the main add() function:毕竟,我创建了主要的 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;
}

This program works in most cases, although our school checker found that it doesn't - like instead of该程序在大多数情况下都有效,尽管我们的学校检查员发现它不起作用 - 而不是

-4400547114413430129608370706728634555709161366260921095898099024156859909714382493551072616612065064

it returned它回来了

-4400547114413430129608370706728634555709161366260921095698099024156859909714382493551072616612065064

I can't find what the problem is.我找不到问题是什么。 Please help and thank you in advance.请帮助并提前感谢您。

Full code on pastebin pastebin 上的完整代码

While I think your overall approach is totally reasonable for this problem, your implementation seems a bit too complicated.虽然我认为您的整体方法对于这个问题是完全合理的,但您的实现似乎有点太复杂了。 Trying to solve this myself, I came up with this:试图自己解决这个问题,我想出了这个:

#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();
}

It can potentially be further optimized, but the main points are:它可以进一步优化,但要点是:

  • If the sign of both numbers is the same, we can do simple written addition.如果两个数字的符号相同,我们可以做简单的书面加法。 If both are negative, we simply prepend - at the end.如果两者都是负数,我们只需在末尾加上-
  • If the signs are different, we do written subtraction.如果符号不同,我们做书面减法。 If the minuend is greater than the subtrahend, there's no issue, we know that the result will be positive.如果被减数大于被减数,则没有问题,我们知道结果将为正。 If, however, the subtrahend is greater, we have to reformulate the problem.然而,如果被减数更大,我们就必须重新表述这个问题。 For example, 123 - 234 we would formulate as -(234 - 123) .例如, 123 - 234我们将公式化为-(234 - 123) The inner part we can solve using regular written subtraction, after which we prepend - .我们可以使用常规的书面减法来解决内部部分,然后我们在前面加上-

I test this with random numbers for which we can calculate the correct result using regular integer arithmetic.我用随机数测试这个,我们可以使用常规整数算法计算出正确的结果。 Since it doesn't fail for those, I'm pretty confident it also works correctly for larger inputs.因为它不会失败,所以我非常有信心它也可以正确地用于更大的输入。 An approach like this could also help you uncover cases where your implementation fails.这样的方法还可以帮助您发现实施失败的情况。

Other than that, I think you should use a known failing case with a debugger or simply print statements for the intermediate steps to see where it fails.除此之外,我认为您应该使用带有调试器的已知失败案例或简单地打印中间步骤的语句以查看失败的位置。 The only small differences in the failing example you posted could point at some issue with handling a carry-over.您发布的失败示例中唯一的小差异可能指向处理结转的一些问题。

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

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