十进制乘数中的错误

时间:2011-01-30 20:33:28

标签: c++ vector math multiplication

我在C ++中写一个十进制乘法器。乘法器是通过将两个整数表示为数字向量来实现的。每个向量以相反的顺序存储其数字,以便以十的幂表示。例如,3497 = 3 * 10 ^ 3 + 4 * 10 ^ 2 + 9 * 10 ^ 1 + 7 * 10 ^ 0并且作为{7,9,4,3}存储在矢量中。因此,向量的每个索引表示该数字在整数中的相应幂10。

但是,我的乘法中有一些错误。 1位x 1位和2位x 1位乘法工作完美,但它以2位x 2位数分解。我相当肯定修复这个bug会修复所有其他错误的n位x m-digit。我的乘法和加法方法的代码如下。

vector<int> multiply(vector<int> &a, vector<int> &b) {
    // check for emptiness
    if(a.size() == 0)
        return b;
    else if(b.size() == 0)
        return a;

    unsigned int i; // increment counter

    // compensate for size differences
    if(a.size() > b.size())
        for(i = 0; i < a.size()-b.size(); ++i)
            b.push_back(0);
    else
        for(i = 0; i < b.size()-a.size(); ++i)
            a.push_back(0);

    int c = 0; // carry value
    int temp; // temporary integer
    vector<int> p; // product vector
    vector<int> s; // sum vector
    s.push_back(0); // initialize to 0

    // multiply each digit of a by an index of b
    for(i = 0; i < b.size(); ++i) {
        // skip digits of 0
        if(b[i] == 0)
            continue;

        p.resize(i,0); // resize p and add 0s

        // multiply b[i] by each digit of a
        for(unsigned int j = 0; j < a.size(); ++j) {
            temp = c + b[i] * a[j]; // calculate temporary value

            // two cases
            if(temp > 9) {
                c = temp / 10; // new carry
                p.push_back(temp % 10);
            }
            else {
                c = 0;
                p.push_back(temp);
            }
        }

        // append carry if relevant
        if(c != 0)
            p.push_back(c);

        // sum p and s
        s = add(p, s);
        p.clear(); // empty p
    }

    return s; // return summed vector (total product)
}

vector<int> add(vector<int> &a, vector<int> &b) {
    // check for emptiness
    if(a.size() == 0)
        return b;
    else if(b.size() == 0)
        return a;

    unsigned int i; // increment counter

    // compensate size differences
    if(a.size() > b.size())
        for(i = 0; i < a.size()-b.size(); ++i)
            b.push_back(0);
    else
        for(i = 0; i < b.size()-a.size(); ++i)
            a.push_back(0);

    int c = 0; // carry value
    vector<int> s; // sum vector
    int temp; // temporary value

    // iterate through decimal vectors
    for(i = 0; i < a.size(); ++i) {
        temp = c + a[i] + b[i]; // sum three terms

        // two cases
        if(temp > 9) {
            c = temp / 10; // new carry
            s.push_back(temp % 10); // push back remainder
        }
        else {
            c = 0;
            s.push_back(temp);
        }
    }

    // append carry if relevant
    if(c != 0)
        s.push_back(c);

    return s; // return sum
}

一些测试用例:

45 * 16返回740(应为720) 34 * 18返回542(应为532) 67 * 29回归2003年(应该是1943年) 28 * 12返回336(正确)

我能想到的唯一问题是携带的问题,但是当我浏览代码时,一切似乎都会检查出来。谁能看到错误?或者我完全采取了错误的方法吗?

2 个答案:

答案 0 :(得分:4)

你没有在内循环之前(或之后)清除进位。

    // iterate through decimal vectors
    for(i = 0; i < a.size(); ++i) {
        ...
    }

    // append carry if relevant
    if(c != 0) {
        p.push_back(c);
        // add this line
        c=0;
    }

另外,在add你可能想要:

// compensate size differences
while (a.size() > b.size())
    b.push_back(0);
while (b.size() > a.size())
    a.push_back(0);

按原样,你只会推动一些零,等于(我相信)数组之间初始差异的一半。尝试一个交互式调试器(正如James建议的那样),你可能会发现其他错误(尽管还有一些东西可以说是为了简单地手动运行代码)。

答案 1 :(得分:1)

其他一些风格评论:

  1. 不要测试空向量然后返回你要返回的内容,因为空向量的存在表明其他地方存在编程错误 - 要么引发异常,要么(可能更好)使用断言。当然,除非你使用空向量来表示0 - 在这种情况下,逻辑仍然是错误的; 0 *某事= 0,不是什么。

  2. 当您测试矢量空虚时,请使用.empty()

  3. 不需要用零填充向量到相同的长度。你没有成对地考虑元素;你把每个人相乘一个。你有没有注意到目前你可以用零填充b,但是你有逻辑在循环内完全跳过那些零?

  4. 但无论如何,填充可以更整齐地完成:

    size_t len = std::max(a.size(), b.size());
    a.resize(len); b.resize(len); // Note that 0 is the default "fill"
    
  5. 特殊情况不够特别。处理temp > 9 的情况的逻辑在temp <= 9 时非常有效。不要过早优化。保持代码简单。 (通过在现代处理器上添加分支,很容易丢失通过避免div / mod工作而获得的性能。)

  6. 不要通过反复清除和调整矢量来“重复使用”矢量。相反,在该范围内构建它(同样,不要过早地优化)。通常,尽可能限制变量范围。类似的评论适用于其他变量。特别是对于循环计数器( ugh )。

  7. 但是执行缓存诸如迭代的矢量大小(当大小不变时)。这对编译器来说更难以优化。

  8. 更好的是,当不需要索引时,使用迭代器迭代向量。

  9. 输入向量不会改变,所以做广告。

  10. 在有合理的完整名称的地方使用全名。

  11. 你真的不需要评论那么多。很明显发生了什么。

  12. 你正在做的跑步总数('积累')是一个很好的候选人(少数很好的理由之一)来利用非常规参考。

  13. 我会写(不考虑算法的重大变化)(警告,未经测试!):

    // I give the function a noun-type name because it returns a value. Just a convention.
    vector<int> product(const vector<int> &a, const vector<int> &b) {
        assert !a.empty();
        assert !b.empty();
    
        vector<int> result(1); // initialized to hold 1 digit with value 0.
    
        vector<int>::const_iterator a_begin = b.begin(), a_end = b.end();
        size_t b_len = b.size();
    
        for (unsigned int i = 0; i < b_len; ++i) {
            vector<int> row(i);
            int carry = 0; // notice that proper scoping auto-fixes the bug.
    
            for (vector<int>::const_iterator it = a_begin; it != a_end; ++it) {
                int value = carry + b[i] * *it;
                carry = value / 10;
                row.push_back(value % 10);
            }
            if (carry != 0) value.push_back(carry);
            add(result, row);
        }
        return result;
    }
    
    // Similarly, if I were to return the sum, I would call this 'sum'.
    void add(vector<int> &target, const vector<int> &to_add) {
        assert !target.empty();
        assert !to_add.empty();
    
        int count = to_add.size();
    
        // Make sure 'target' is long enough
        target.resize(std::max(target.size(), count));
        int size = target.size(); // after resizing.
    
        int carry = 0;
    
        // iterate through decimal vectors
        for (int i = 0; i < size; ++i) {
            int value = carry + target[i];
            if (i < count) { value += to_add[i]; }
            carry = value / 10;
            target[i] = value % 10;
        }
    
        if (carry != 0) { target.push_back(carry); }
    }