我在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(正确)
我能想到的唯一问题是携带的问题,但是当我浏览代码时,一切似乎都会检查出来。谁能看到错误?或者我完全采取了错误的方法吗?
答案 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)
其他一些风格评论:
不要测试空向量然后返回你要返回的内容,因为空向量的存在表明其他地方存在编程错误 - 要么引发异常,要么(可能更好)使用断言。当然,除非你使用空向量来表示0 - 在这种情况下,逻辑仍然是错误的; 0 *某事= 0,不是什么。
当您测试矢量空虚时,请使用.empty()
。
不需要用零填充向量到相同的长度。你没有成对地考虑元素;你把每个人相乘一个。你有没有注意到目前你可以用零填充b
,但是你有逻辑在循环内完全跳过那些零?
但无论如何,填充可以更整齐地完成:
size_t len = std::max(a.size(), b.size());
a.resize(len); b.resize(len); // Note that 0 is the default "fill"
特殊情况不够特别。处理temp > 9
的情况的逻辑在temp <= 9
时非常有效。不要过早优化。保持代码简单。 (通过在现代处理器上添加分支,很容易丢失通过避免div / mod工作而获得的性能。)
不要通过反复清除和调整矢量来“重复使用”矢量。相反,在该范围内构建它(同样,不要过早地优化)。通常,尽可能限制变量范围。类似的评论适用于其他变量。特别是对于循环计数器( ugh )。
但是执行缓存诸如迭代的矢量大小(当大小不变时)。这对编译器来说更难以优化。
更好的是,当不需要索引时,使用迭代器迭代向量。
输入向量不会改变,所以做广告。
在有合理的完整名称的地方使用全名。
你真的不需要评论那么多。很明显发生了什么。
你正在做的跑步总数('积累')是一个很好的候选人(少数很好的理由之一)来利用非常规参考。
我会写(不考虑算法的重大变化)(警告,未经测试!):
// 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); }
}