Karatsuba multiplication
$begingroup$
My implementation of Karatsuba multiplication from Tim Roughgarden's Algorithms course. I've made a Integer class that holds an integer in string format. I've added operations to this class to add, subtract and multiply numbers. Karatsuba.cpp performs Gauss' trick (complex multiplication algorithm).
Integer.hpp
#pragma once
#include <string>
#include <functional>
class Integer{
public:
Integer();
Integer(const std::string& input);
~Integer();
Integer operator+(const Integer& other) const;
Integer operator-(const Integer& other) const;
Integer operator*(const Integer& other) const;
size_t size() const{return fString.size();}
void padRight(size_t num);
void padLeft(size_t num);
Integer substr(size_t pos, size_t length) const;
void print() const;
void stripLeadingZeros();
private:
std::string fString;
};
void equalizeLengthsPadLeft(Integer& first,
Integer& second);
void equalizeLengthsPadRight(std::string& first, std::string& second);
Integer.cpp
#include "Integer.hpp"
#include <assert.h>
#include <string>
#include <algorithm>
#include <iostream>
namespace {
int char2int(char c){
return c - '0';
}
}
Integer::Integer()
:
fString()
{
}
Integer::Integer(const std::string& input)
:
fString(input)
{
}
Integer::~Integer(){}
Integer Integer::substr(size_t pos, size_t len) const{
return fString.substr(pos, len);
}
void equalizeLengthsPadLeft(Integer& first,
Integer& second){
if (first.size() < second.size()){
first.padLeft(second.size()-first.size());
}
else if(first.size() > second.size()){
second.padLeft(first.size() - second.size());
}
}
void equalizeLengthsPadRight(std::string& first, std::string& second){
if (first.size() < second.size()){
first += std::string(second.size()-first.size(), '0');
}
else if(first.size() > second.size()){
second += std::string(first.size() - second.size(), '0');
}
}
Integer Integer::operator+(const Integer& other) const{
// For the time being, just conver to integer and return
std::string first = fString;
std::reverse(first.begin(), first.end());
std::string second = other.fString;
std::reverse(second.begin(), second.end());
equalizeLengthsPadRight(first,second);
std::string::iterator first_it = first.begin();
std::string::iterator second_it = second.begin();
std::string resultStr;
int carry = 0;
while(first_it != first.end()){
int sum = char2int(*first_it) + char2int(*second_it) + carry;
carry = 0;
if (sum >= 10){
sum = sum%10;
carry = 1;
}
resultStr += std::to_string(sum);
first_it++;second_it++;
}
if (carry){
resultStr += '1';
}
std::reverse(resultStr.begin(), resultStr.end());
return resultStr;
}
Integer Integer::operator-(const Integer& other) const{
std::string first = fString;
std::reverse(first.begin(), first.end());
std::string second = other.fString;
std::reverse(second.begin(), second.end());
// Equalize
equalizeLengthsPadRight(first,second);
std::string::iterator first_it = first.begin();
std::string::iterator second_it = second.begin();
std::string resultStr;
while(first_it != first.end()){
int up = char2int(*first_it);
int down = char2int(*second_it);
int localResult;
if (up >= down){
localResult = up-down;
}
else{
// Keep searching forward until you get a non-zero value
auto next_it = first_it+1;
while(true){
if (char2int(*next_it) > 0){
// Found the first non-zero number
break;
}
next_it++;
}
*next_it = std::to_string(char2int(*next_it)-1)[0];
// Now chase back to first_it setting 9's
// on the way. Make sure everything was 0
// beforehand
next_it--;
while(next_it != first_it){
assert(char2int(*next_it) == 0);
*next_it = std::to_string(9)[0];
next_it--;
}
localResult = up+10 -down;
}
assert(localResult>=0);
resultStr += std::to_string(localResult);
first_it++;
second_it++;
}
std::reverse(resultStr.begin(), resultStr.end());
return resultStr;
}
Integer Integer::operator*(const Integer& other) const {
// Only allow multiplication when size is 1
assert(size() == other.size() == 1);
return std::to_string(std::stoi(fString)*std::stoi(other.fString));
}
void Integer::padRight(size_t num){
fString += std::string(num, '0');
}
void Integer::padLeft(size_t num){
fString.insert(0,num,'0');
}
void Integer::print() const{
std::cout << fString << std::endl;
}
void Integer::stripLeadingZeros(){
// Don't strip if all are zeros - this will lead to an empty string
if (std::all_of(fString.cbegin(), fString.cend(), (char c){return ('0'== c); })){
return;
}
fString.erase(0, fString.find_first_not_of('0'));
}
Karatsuba multiplication
#include <string>
#include <assert.h>
#include <cmath>
#include "Integer.hpp"
Integer multiply(const Integer& inp1, const Integer& inp2){
Integer first = inp1;
Integer second = inp2;
equalizeLengthsPadLeft(first, second);
assert(first.size()==second.size());
size_t sz = first.size();
// Base case
if (sz == 1){
return first*second;
}
int n = sz/2;
Integer A = first.substr(0,n);
Integer B = first.substr(n, sz-n);
Integer C = second.substr(0,n);
Integer D = second.substr(n, sz-n);
Integer AC = multiply(A, C);
Integer BD = multiply(B, D);
Integer A_plus_B = A+B;
Integer C_plus_D = C+D;
Integer sum = multiply(A_plus_B, C_plus_D);
Integer AD_plus_BC = sum - AC - BD;
AC.padRight(2*(sz-n));
AD_plus_BC.padRight(sz-n);
Integer result = AC+ AD_plus_BC + BD;
result.stripLeadingZeros();
return result;
}
int main(){
Integer first("3141592653589793238462643383279502884197169399375105820974944592");
Integer second("2718281828459045235360287471352662497757247093699959574966967627");
Integer ans = multiply(first, second);
ans.print();
}
c++ algorithm
$endgroup$
add a comment |
$begingroup$
My implementation of Karatsuba multiplication from Tim Roughgarden's Algorithms course. I've made a Integer class that holds an integer in string format. I've added operations to this class to add, subtract and multiply numbers. Karatsuba.cpp performs Gauss' trick (complex multiplication algorithm).
Integer.hpp
#pragma once
#include <string>
#include <functional>
class Integer{
public:
Integer();
Integer(const std::string& input);
~Integer();
Integer operator+(const Integer& other) const;
Integer operator-(const Integer& other) const;
Integer operator*(const Integer& other) const;
size_t size() const{return fString.size();}
void padRight(size_t num);
void padLeft(size_t num);
Integer substr(size_t pos, size_t length) const;
void print() const;
void stripLeadingZeros();
private:
std::string fString;
};
void equalizeLengthsPadLeft(Integer& first,
Integer& second);
void equalizeLengthsPadRight(std::string& first, std::string& second);
Integer.cpp
#include "Integer.hpp"
#include <assert.h>
#include <string>
#include <algorithm>
#include <iostream>
namespace {
int char2int(char c){
return c - '0';
}
}
Integer::Integer()
:
fString()
{
}
Integer::Integer(const std::string& input)
:
fString(input)
{
}
Integer::~Integer(){}
Integer Integer::substr(size_t pos, size_t len) const{
return fString.substr(pos, len);
}
void equalizeLengthsPadLeft(Integer& first,
Integer& second){
if (first.size() < second.size()){
first.padLeft(second.size()-first.size());
}
else if(first.size() > second.size()){
second.padLeft(first.size() - second.size());
}
}
void equalizeLengthsPadRight(std::string& first, std::string& second){
if (first.size() < second.size()){
first += std::string(second.size()-first.size(), '0');
}
else if(first.size() > second.size()){
second += std::string(first.size() - second.size(), '0');
}
}
Integer Integer::operator+(const Integer& other) const{
// For the time being, just conver to integer and return
std::string first = fString;
std::reverse(first.begin(), first.end());
std::string second = other.fString;
std::reverse(second.begin(), second.end());
equalizeLengthsPadRight(first,second);
std::string::iterator first_it = first.begin();
std::string::iterator second_it = second.begin();
std::string resultStr;
int carry = 0;
while(first_it != first.end()){
int sum = char2int(*first_it) + char2int(*second_it) + carry;
carry = 0;
if (sum >= 10){
sum = sum%10;
carry = 1;
}
resultStr += std::to_string(sum);
first_it++;second_it++;
}
if (carry){
resultStr += '1';
}
std::reverse(resultStr.begin(), resultStr.end());
return resultStr;
}
Integer Integer::operator-(const Integer& other) const{
std::string first = fString;
std::reverse(first.begin(), first.end());
std::string second = other.fString;
std::reverse(second.begin(), second.end());
// Equalize
equalizeLengthsPadRight(first,second);
std::string::iterator first_it = first.begin();
std::string::iterator second_it = second.begin();
std::string resultStr;
while(first_it != first.end()){
int up = char2int(*first_it);
int down = char2int(*second_it);
int localResult;
if (up >= down){
localResult = up-down;
}
else{
// Keep searching forward until you get a non-zero value
auto next_it = first_it+1;
while(true){
if (char2int(*next_it) > 0){
// Found the first non-zero number
break;
}
next_it++;
}
*next_it = std::to_string(char2int(*next_it)-1)[0];
// Now chase back to first_it setting 9's
// on the way. Make sure everything was 0
// beforehand
next_it--;
while(next_it != first_it){
assert(char2int(*next_it) == 0);
*next_it = std::to_string(9)[0];
next_it--;
}
localResult = up+10 -down;
}
assert(localResult>=0);
resultStr += std::to_string(localResult);
first_it++;
second_it++;
}
std::reverse(resultStr.begin(), resultStr.end());
return resultStr;
}
Integer Integer::operator*(const Integer& other) const {
// Only allow multiplication when size is 1
assert(size() == other.size() == 1);
return std::to_string(std::stoi(fString)*std::stoi(other.fString));
}
void Integer::padRight(size_t num){
fString += std::string(num, '0');
}
void Integer::padLeft(size_t num){
fString.insert(0,num,'0');
}
void Integer::print() const{
std::cout << fString << std::endl;
}
void Integer::stripLeadingZeros(){
// Don't strip if all are zeros - this will lead to an empty string
if (std::all_of(fString.cbegin(), fString.cend(), (char c){return ('0'== c); })){
return;
}
fString.erase(0, fString.find_first_not_of('0'));
}
Karatsuba multiplication
#include <string>
#include <assert.h>
#include <cmath>
#include "Integer.hpp"
Integer multiply(const Integer& inp1, const Integer& inp2){
Integer first = inp1;
Integer second = inp2;
equalizeLengthsPadLeft(first, second);
assert(first.size()==second.size());
size_t sz = first.size();
// Base case
if (sz == 1){
return first*second;
}
int n = sz/2;
Integer A = first.substr(0,n);
Integer B = first.substr(n, sz-n);
Integer C = second.substr(0,n);
Integer D = second.substr(n, sz-n);
Integer AC = multiply(A, C);
Integer BD = multiply(B, D);
Integer A_plus_B = A+B;
Integer C_plus_D = C+D;
Integer sum = multiply(A_plus_B, C_plus_D);
Integer AD_plus_BC = sum - AC - BD;
AC.padRight(2*(sz-n));
AD_plus_BC.padRight(sz-n);
Integer result = AC+ AD_plus_BC + BD;
result.stripLeadingZeros();
return result;
}
int main(){
Integer first("3141592653589793238462643383279502884197169399375105820974944592");
Integer second("2718281828459045235360287471352662497757247093699959574966967627");
Integer ans = multiply(first, second);
ans.print();
}
c++ algorithm
$endgroup$
$begingroup$
What is a Gauss trick, and how Karatsuba.cpp performs it?
$endgroup$
– vnp
17 hours ago
$begingroup$
Karatsuba is div-conq multiplication. If $x$ and $y$ are $n$-digit strings in some base $B$, then for any positive integer $m < n$, you can write $x = aB^m + b, y = cB^m + d$ where $b$ and $d$ are less than $B^m$. The product $x cdot y = ldots =acB^{2m} + (ad + bc)B^m + bd$. Applying Gauss' complex multiplication algorithm, reuse the results of $ac$ and $bd$ to find $(ad + bc)$. Thus, $(ad + bc) = (a + b)(c + d) - ac - bd$, reducing the no. of recursive multiplications at the cost of extra additions. This is performed whenAD_plus_BC
is introduced in the code.
$endgroup$
– Snowhawk
16 hours ago
add a comment |
$begingroup$
My implementation of Karatsuba multiplication from Tim Roughgarden's Algorithms course. I've made a Integer class that holds an integer in string format. I've added operations to this class to add, subtract and multiply numbers. Karatsuba.cpp performs Gauss' trick (complex multiplication algorithm).
Integer.hpp
#pragma once
#include <string>
#include <functional>
class Integer{
public:
Integer();
Integer(const std::string& input);
~Integer();
Integer operator+(const Integer& other) const;
Integer operator-(const Integer& other) const;
Integer operator*(const Integer& other) const;
size_t size() const{return fString.size();}
void padRight(size_t num);
void padLeft(size_t num);
Integer substr(size_t pos, size_t length) const;
void print() const;
void stripLeadingZeros();
private:
std::string fString;
};
void equalizeLengthsPadLeft(Integer& first,
Integer& second);
void equalizeLengthsPadRight(std::string& first, std::string& second);
Integer.cpp
#include "Integer.hpp"
#include <assert.h>
#include <string>
#include <algorithm>
#include <iostream>
namespace {
int char2int(char c){
return c - '0';
}
}
Integer::Integer()
:
fString()
{
}
Integer::Integer(const std::string& input)
:
fString(input)
{
}
Integer::~Integer(){}
Integer Integer::substr(size_t pos, size_t len) const{
return fString.substr(pos, len);
}
void equalizeLengthsPadLeft(Integer& first,
Integer& second){
if (first.size() < second.size()){
first.padLeft(second.size()-first.size());
}
else if(first.size() > second.size()){
second.padLeft(first.size() - second.size());
}
}
void equalizeLengthsPadRight(std::string& first, std::string& second){
if (first.size() < second.size()){
first += std::string(second.size()-first.size(), '0');
}
else if(first.size() > second.size()){
second += std::string(first.size() - second.size(), '0');
}
}
Integer Integer::operator+(const Integer& other) const{
// For the time being, just conver to integer and return
std::string first = fString;
std::reverse(first.begin(), first.end());
std::string second = other.fString;
std::reverse(second.begin(), second.end());
equalizeLengthsPadRight(first,second);
std::string::iterator first_it = first.begin();
std::string::iterator second_it = second.begin();
std::string resultStr;
int carry = 0;
while(first_it != first.end()){
int sum = char2int(*first_it) + char2int(*second_it) + carry;
carry = 0;
if (sum >= 10){
sum = sum%10;
carry = 1;
}
resultStr += std::to_string(sum);
first_it++;second_it++;
}
if (carry){
resultStr += '1';
}
std::reverse(resultStr.begin(), resultStr.end());
return resultStr;
}
Integer Integer::operator-(const Integer& other) const{
std::string first = fString;
std::reverse(first.begin(), first.end());
std::string second = other.fString;
std::reverse(second.begin(), second.end());
// Equalize
equalizeLengthsPadRight(first,second);
std::string::iterator first_it = first.begin();
std::string::iterator second_it = second.begin();
std::string resultStr;
while(first_it != first.end()){
int up = char2int(*first_it);
int down = char2int(*second_it);
int localResult;
if (up >= down){
localResult = up-down;
}
else{
// Keep searching forward until you get a non-zero value
auto next_it = first_it+1;
while(true){
if (char2int(*next_it) > 0){
// Found the first non-zero number
break;
}
next_it++;
}
*next_it = std::to_string(char2int(*next_it)-1)[0];
// Now chase back to first_it setting 9's
// on the way. Make sure everything was 0
// beforehand
next_it--;
while(next_it != first_it){
assert(char2int(*next_it) == 0);
*next_it = std::to_string(9)[0];
next_it--;
}
localResult = up+10 -down;
}
assert(localResult>=0);
resultStr += std::to_string(localResult);
first_it++;
second_it++;
}
std::reverse(resultStr.begin(), resultStr.end());
return resultStr;
}
Integer Integer::operator*(const Integer& other) const {
// Only allow multiplication when size is 1
assert(size() == other.size() == 1);
return std::to_string(std::stoi(fString)*std::stoi(other.fString));
}
void Integer::padRight(size_t num){
fString += std::string(num, '0');
}
void Integer::padLeft(size_t num){
fString.insert(0,num,'0');
}
void Integer::print() const{
std::cout << fString << std::endl;
}
void Integer::stripLeadingZeros(){
// Don't strip if all are zeros - this will lead to an empty string
if (std::all_of(fString.cbegin(), fString.cend(), (char c){return ('0'== c); })){
return;
}
fString.erase(0, fString.find_first_not_of('0'));
}
Karatsuba multiplication
#include <string>
#include <assert.h>
#include <cmath>
#include "Integer.hpp"
Integer multiply(const Integer& inp1, const Integer& inp2){
Integer first = inp1;
Integer second = inp2;
equalizeLengthsPadLeft(first, second);
assert(first.size()==second.size());
size_t sz = first.size();
// Base case
if (sz == 1){
return first*second;
}
int n = sz/2;
Integer A = first.substr(0,n);
Integer B = first.substr(n, sz-n);
Integer C = second.substr(0,n);
Integer D = second.substr(n, sz-n);
Integer AC = multiply(A, C);
Integer BD = multiply(B, D);
Integer A_plus_B = A+B;
Integer C_plus_D = C+D;
Integer sum = multiply(A_plus_B, C_plus_D);
Integer AD_plus_BC = sum - AC - BD;
AC.padRight(2*(sz-n));
AD_plus_BC.padRight(sz-n);
Integer result = AC+ AD_plus_BC + BD;
result.stripLeadingZeros();
return result;
}
int main(){
Integer first("3141592653589793238462643383279502884197169399375105820974944592");
Integer second("2718281828459045235360287471352662497757247093699959574966967627");
Integer ans = multiply(first, second);
ans.print();
}
c++ algorithm
$endgroup$
My implementation of Karatsuba multiplication from Tim Roughgarden's Algorithms course. I've made a Integer class that holds an integer in string format. I've added operations to this class to add, subtract and multiply numbers. Karatsuba.cpp performs Gauss' trick (complex multiplication algorithm).
Integer.hpp
#pragma once
#include <string>
#include <functional>
class Integer{
public:
Integer();
Integer(const std::string& input);
~Integer();
Integer operator+(const Integer& other) const;
Integer operator-(const Integer& other) const;
Integer operator*(const Integer& other) const;
size_t size() const{return fString.size();}
void padRight(size_t num);
void padLeft(size_t num);
Integer substr(size_t pos, size_t length) const;
void print() const;
void stripLeadingZeros();
private:
std::string fString;
};
void equalizeLengthsPadLeft(Integer& first,
Integer& second);
void equalizeLengthsPadRight(std::string& first, std::string& second);
Integer.cpp
#include "Integer.hpp"
#include <assert.h>
#include <string>
#include <algorithm>
#include <iostream>
namespace {
int char2int(char c){
return c - '0';
}
}
Integer::Integer()
:
fString()
{
}
Integer::Integer(const std::string& input)
:
fString(input)
{
}
Integer::~Integer(){}
Integer Integer::substr(size_t pos, size_t len) const{
return fString.substr(pos, len);
}
void equalizeLengthsPadLeft(Integer& first,
Integer& second){
if (first.size() < second.size()){
first.padLeft(second.size()-first.size());
}
else if(first.size() > second.size()){
second.padLeft(first.size() - second.size());
}
}
void equalizeLengthsPadRight(std::string& first, std::string& second){
if (first.size() < second.size()){
first += std::string(second.size()-first.size(), '0');
}
else if(first.size() > second.size()){
second += std::string(first.size() - second.size(), '0');
}
}
Integer Integer::operator+(const Integer& other) const{
// For the time being, just conver to integer and return
std::string first = fString;
std::reverse(first.begin(), first.end());
std::string second = other.fString;
std::reverse(second.begin(), second.end());
equalizeLengthsPadRight(first,second);
std::string::iterator first_it = first.begin();
std::string::iterator second_it = second.begin();
std::string resultStr;
int carry = 0;
while(first_it != first.end()){
int sum = char2int(*first_it) + char2int(*second_it) + carry;
carry = 0;
if (sum >= 10){
sum = sum%10;
carry = 1;
}
resultStr += std::to_string(sum);
first_it++;second_it++;
}
if (carry){
resultStr += '1';
}
std::reverse(resultStr.begin(), resultStr.end());
return resultStr;
}
Integer Integer::operator-(const Integer& other) const{
std::string first = fString;
std::reverse(first.begin(), first.end());
std::string second = other.fString;
std::reverse(second.begin(), second.end());
// Equalize
equalizeLengthsPadRight(first,second);
std::string::iterator first_it = first.begin();
std::string::iterator second_it = second.begin();
std::string resultStr;
while(first_it != first.end()){
int up = char2int(*first_it);
int down = char2int(*second_it);
int localResult;
if (up >= down){
localResult = up-down;
}
else{
// Keep searching forward until you get a non-zero value
auto next_it = first_it+1;
while(true){
if (char2int(*next_it) > 0){
// Found the first non-zero number
break;
}
next_it++;
}
*next_it = std::to_string(char2int(*next_it)-1)[0];
// Now chase back to first_it setting 9's
// on the way. Make sure everything was 0
// beforehand
next_it--;
while(next_it != first_it){
assert(char2int(*next_it) == 0);
*next_it = std::to_string(9)[0];
next_it--;
}
localResult = up+10 -down;
}
assert(localResult>=0);
resultStr += std::to_string(localResult);
first_it++;
second_it++;
}
std::reverse(resultStr.begin(), resultStr.end());
return resultStr;
}
Integer Integer::operator*(const Integer& other) const {
// Only allow multiplication when size is 1
assert(size() == other.size() == 1);
return std::to_string(std::stoi(fString)*std::stoi(other.fString));
}
void Integer::padRight(size_t num){
fString += std::string(num, '0');
}
void Integer::padLeft(size_t num){
fString.insert(0,num,'0');
}
void Integer::print() const{
std::cout << fString << std::endl;
}
void Integer::stripLeadingZeros(){
// Don't strip if all are zeros - this will lead to an empty string
if (std::all_of(fString.cbegin(), fString.cend(), (char c){return ('0'== c); })){
return;
}
fString.erase(0, fString.find_first_not_of('0'));
}
Karatsuba multiplication
#include <string>
#include <assert.h>
#include <cmath>
#include "Integer.hpp"
Integer multiply(const Integer& inp1, const Integer& inp2){
Integer first = inp1;
Integer second = inp2;
equalizeLengthsPadLeft(first, second);
assert(first.size()==second.size());
size_t sz = first.size();
// Base case
if (sz == 1){
return first*second;
}
int n = sz/2;
Integer A = first.substr(0,n);
Integer B = first.substr(n, sz-n);
Integer C = second.substr(0,n);
Integer D = second.substr(n, sz-n);
Integer AC = multiply(A, C);
Integer BD = multiply(B, D);
Integer A_plus_B = A+B;
Integer C_plus_D = C+D;
Integer sum = multiply(A_plus_B, C_plus_D);
Integer AD_plus_BC = sum - AC - BD;
AC.padRight(2*(sz-n));
AD_plus_BC.padRight(sz-n);
Integer result = AC+ AD_plus_BC + BD;
result.stripLeadingZeros();
return result;
}
int main(){
Integer first("3141592653589793238462643383279502884197169399375105820974944592");
Integer second("2718281828459045235360287471352662497757247093699959574966967627");
Integer ans = multiply(first, second);
ans.print();
}
c++ algorithm
c++ algorithm
edited 16 hours ago
Snowhawk
5,22411128
5,22411128
asked 19 hours ago
cppprogrammercppprogrammer
302110
302110
$begingroup$
What is a Gauss trick, and how Karatsuba.cpp performs it?
$endgroup$
– vnp
17 hours ago
$begingroup$
Karatsuba is div-conq multiplication. If $x$ and $y$ are $n$-digit strings in some base $B$, then for any positive integer $m < n$, you can write $x = aB^m + b, y = cB^m + d$ where $b$ and $d$ are less than $B^m$. The product $x cdot y = ldots =acB^{2m} + (ad + bc)B^m + bd$. Applying Gauss' complex multiplication algorithm, reuse the results of $ac$ and $bd$ to find $(ad + bc)$. Thus, $(ad + bc) = (a + b)(c + d) - ac - bd$, reducing the no. of recursive multiplications at the cost of extra additions. This is performed whenAD_plus_BC
is introduced in the code.
$endgroup$
– Snowhawk
16 hours ago
add a comment |
$begingroup$
What is a Gauss trick, and how Karatsuba.cpp performs it?
$endgroup$
– vnp
17 hours ago
$begingroup$
Karatsuba is div-conq multiplication. If $x$ and $y$ are $n$-digit strings in some base $B$, then for any positive integer $m < n$, you can write $x = aB^m + b, y = cB^m + d$ where $b$ and $d$ are less than $B^m$. The product $x cdot y = ldots =acB^{2m} + (ad + bc)B^m + bd$. Applying Gauss' complex multiplication algorithm, reuse the results of $ac$ and $bd$ to find $(ad + bc)$. Thus, $(ad + bc) = (a + b)(c + d) - ac - bd$, reducing the no. of recursive multiplications at the cost of extra additions. This is performed whenAD_plus_BC
is introduced in the code.
$endgroup$
– Snowhawk
16 hours ago
$begingroup$
What is a Gauss trick, and how Karatsuba.cpp performs it?
$endgroup$
– vnp
17 hours ago
$begingroup$
What is a Gauss trick, and how Karatsuba.cpp performs it?
$endgroup$
– vnp
17 hours ago
$begingroup$
Karatsuba is div-conq multiplication. If $x$ and $y$ are $n$-digit strings in some base $B$, then for any positive integer $m < n$, you can write $x = aB^m + b, y = cB^m + d$ where $b$ and $d$ are less than $B^m$. The product $x cdot y = ldots =acB^{2m} + (ad + bc)B^m + bd$. Applying Gauss' complex multiplication algorithm, reuse the results of $ac$ and $bd$ to find $(ad + bc)$. Thus, $(ad + bc) = (a + b)(c + d) - ac - bd$, reducing the no. of recursive multiplications at the cost of extra additions. This is performed when
AD_plus_BC
is introduced in the code.$endgroup$
– Snowhawk
16 hours ago
$begingroup$
Karatsuba is div-conq multiplication. If $x$ and $y$ are $n$-digit strings in some base $B$, then for any positive integer $m < n$, you can write $x = aB^m + b, y = cB^m + d$ where $b$ and $d$ are less than $B^m$. The product $x cdot y = ldots =acB^{2m} + (ad + bc)B^m + bd$. Applying Gauss' complex multiplication algorithm, reuse the results of $ac$ and $bd$ to find $(ad + bc)$. Thus, $(ad + bc) = (a + b)(c + d) - ac - bd$, reducing the no. of recursive multiplications at the cost of extra additions. This is performed when
AD_plus_BC
is introduced in the code.$endgroup$
– Snowhawk
16 hours ago
add a comment |
3 Answers
3
active
oldest
votes
$begingroup$
I could be wrong, but looking at Integer
I think it is really NaturalNumber
. It doesn't seem to support negative numbers.
There are a lot of reversals going on. It would almost certainly make more sense to use the reversed (little-endian) number internally, and only actually call std::reverse
when converting between strings and Integer
s.
padLeft
and stripLeadingZeros
do not belong in the public API of the class (and I'm not even sure they should be required in the private API). An Integer
should be an integer, not a string. padRight
would be more appropriately named something like multiplyByPowerOfTen
or shiftLeftDecimal
.
If the Karatsuba multiplication is intended to be practical rather than purely an academic exercise, the base case should be when the multiplication can be done in a native type: probably when both numbers are less than 10 ** 9, so they fit in a (signed) 32-bit type and the result in a (signed) 64-bit type.
$endgroup$
$begingroup$
Absolutely right about renaming Integer to NaturalNumber. The assignment assumed positive numbers and that doesn't get implied anywhere in the code. Extending the class a little bit will open it to negative numbers too. I could store the number in a reverse manner but it makes it harder to reason about the code. When constructing an integer from a raw string, I could perhaps just reverse the input and store it infString
. On further thought, only 2 operations -operator+
andoperator-
need the string in reverse order, maybe I can store another reversed string as a member
$endgroup$
– cppprogrammer
8 hours ago
$begingroup$
Onlyoperator+
andoperator-
explicitly reverse, but the Karatsuba multiplication uses those operators...
$endgroup$
– Peter Taylor
8 hours ago
add a comment |
$begingroup$
I miss comments and use of a documentation tool like doxygen.
- I did not expect separate digit handling loops in
operator+()
andoperator-()
(obviously, I expected handling of signed integers). Nor the asymmetry between the procedures.
In multiply()
,
(I don't quite like sum
for what it names (still undecided about sz
):)
sz-n
occurs just often enough to ponder introducing an abstraction, a name, a variable for it.- "Padding before adding" leads to two 2×sz additions: an alternative would be to exclude the least significant part (say,
bd
) ofBD
from addition, "appending" its most significant part toAC
for a single sz+n addition and just appendbd
.
(The remaining remarks may lead to more code, code more difficult to maintain/read:
revisiting the test harness seems a good idea before coding any.)
- The next step in avoiding complex digit operations would seem to be not padding in
operator±()
and usingincrement()
/decrement()
instead.) - With factors that differ widely in order of magnitude, there may be a lot of padding & stripping: consider an inner procedure doing just *three multiplications and five additions" (possibly with operand lengths passed in) and an outer one taking care of interface (like stripping leading zeroes).
(On second thought, I'm not sure where "tactics" should go: passing the shorter factor where it terminates recursion sooner, extra strategy ("long multiplication") when one factor reaches the base case, handling 0 and powers of "the base" separately (0th power = 1 being a prominent and trivial case).)
$endgroup$
$begingroup$
"Nor the asymmetry between the procedures." - How can this be improved? Aren't addition and subtraction fundamentally different? Inside the subtraction routine, trying to code the borrowing from the next power of 10 gave me a headache. Addition was a breeze
$endgroup$
– cppprogrammer
8 hours ago
add a comment |
$begingroup$
Your code is formatted inconsistently. Sometimes you placed spaces around operators, sometimes not, with no apparent patterns or rules. Just let your IDE format the code automatically.
Sometimes you use snake_case, sometimes camelCase. You should only use one of them, for consistency.
Instead of sum = sum%10
you can write sum -= 10
since the sum can never be greater than 19.
Having operator*
restricted to a single digit is unexpected. That function should better be an internal implementation detail, and multiply
should become the new operator*
.
Instead of Integer::print
, you should define the usual os << Integer
operator. Don't use std::endl
unless you really know you need it. A simple 'n'
performs better.
$endgroup$
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e) {
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom)) {
StackExchange.using('gps', function() { StackExchange.gps.track('embedded_signup_form.view', { location: 'question_page' }); });
$window.unbind('scroll', onScroll);
}
};
$window.on('scroll', onScroll);
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f213686%2fkaratsuba-multiplication%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
$begingroup$
I could be wrong, but looking at Integer
I think it is really NaturalNumber
. It doesn't seem to support negative numbers.
There are a lot of reversals going on. It would almost certainly make more sense to use the reversed (little-endian) number internally, and only actually call std::reverse
when converting between strings and Integer
s.
padLeft
and stripLeadingZeros
do not belong in the public API of the class (and I'm not even sure they should be required in the private API). An Integer
should be an integer, not a string. padRight
would be more appropriately named something like multiplyByPowerOfTen
or shiftLeftDecimal
.
If the Karatsuba multiplication is intended to be practical rather than purely an academic exercise, the base case should be when the multiplication can be done in a native type: probably when both numbers are less than 10 ** 9, so they fit in a (signed) 32-bit type and the result in a (signed) 64-bit type.
$endgroup$
$begingroup$
Absolutely right about renaming Integer to NaturalNumber. The assignment assumed positive numbers and that doesn't get implied anywhere in the code. Extending the class a little bit will open it to negative numbers too. I could store the number in a reverse manner but it makes it harder to reason about the code. When constructing an integer from a raw string, I could perhaps just reverse the input and store it infString
. On further thought, only 2 operations -operator+
andoperator-
need the string in reverse order, maybe I can store another reversed string as a member
$endgroup$
– cppprogrammer
8 hours ago
$begingroup$
Onlyoperator+
andoperator-
explicitly reverse, but the Karatsuba multiplication uses those operators...
$endgroup$
– Peter Taylor
8 hours ago
add a comment |
$begingroup$
I could be wrong, but looking at Integer
I think it is really NaturalNumber
. It doesn't seem to support negative numbers.
There are a lot of reversals going on. It would almost certainly make more sense to use the reversed (little-endian) number internally, and only actually call std::reverse
when converting between strings and Integer
s.
padLeft
and stripLeadingZeros
do not belong in the public API of the class (and I'm not even sure they should be required in the private API). An Integer
should be an integer, not a string. padRight
would be more appropriately named something like multiplyByPowerOfTen
or shiftLeftDecimal
.
If the Karatsuba multiplication is intended to be practical rather than purely an academic exercise, the base case should be when the multiplication can be done in a native type: probably when both numbers are less than 10 ** 9, so they fit in a (signed) 32-bit type and the result in a (signed) 64-bit type.
$endgroup$
$begingroup$
Absolutely right about renaming Integer to NaturalNumber. The assignment assumed positive numbers and that doesn't get implied anywhere in the code. Extending the class a little bit will open it to negative numbers too. I could store the number in a reverse manner but it makes it harder to reason about the code. When constructing an integer from a raw string, I could perhaps just reverse the input and store it infString
. On further thought, only 2 operations -operator+
andoperator-
need the string in reverse order, maybe I can store another reversed string as a member
$endgroup$
– cppprogrammer
8 hours ago
$begingroup$
Onlyoperator+
andoperator-
explicitly reverse, but the Karatsuba multiplication uses those operators...
$endgroup$
– Peter Taylor
8 hours ago
add a comment |
$begingroup$
I could be wrong, but looking at Integer
I think it is really NaturalNumber
. It doesn't seem to support negative numbers.
There are a lot of reversals going on. It would almost certainly make more sense to use the reversed (little-endian) number internally, and only actually call std::reverse
when converting between strings and Integer
s.
padLeft
and stripLeadingZeros
do not belong in the public API of the class (and I'm not even sure they should be required in the private API). An Integer
should be an integer, not a string. padRight
would be more appropriately named something like multiplyByPowerOfTen
or shiftLeftDecimal
.
If the Karatsuba multiplication is intended to be practical rather than purely an academic exercise, the base case should be when the multiplication can be done in a native type: probably when both numbers are less than 10 ** 9, so they fit in a (signed) 32-bit type and the result in a (signed) 64-bit type.
$endgroup$
I could be wrong, but looking at Integer
I think it is really NaturalNumber
. It doesn't seem to support negative numbers.
There are a lot of reversals going on. It would almost certainly make more sense to use the reversed (little-endian) number internally, and only actually call std::reverse
when converting between strings and Integer
s.
padLeft
and stripLeadingZeros
do not belong in the public API of the class (and I'm not even sure they should be required in the private API). An Integer
should be an integer, not a string. padRight
would be more appropriately named something like multiplyByPowerOfTen
or shiftLeftDecimal
.
If the Karatsuba multiplication is intended to be practical rather than purely an academic exercise, the base case should be when the multiplication can be done in a native type: probably when both numbers are less than 10 ** 9, so they fit in a (signed) 32-bit type and the result in a (signed) 64-bit type.
answered 13 hours ago
Peter TaylorPeter Taylor
16.9k2861
16.9k2861
$begingroup$
Absolutely right about renaming Integer to NaturalNumber. The assignment assumed positive numbers and that doesn't get implied anywhere in the code. Extending the class a little bit will open it to negative numbers too. I could store the number in a reverse manner but it makes it harder to reason about the code. When constructing an integer from a raw string, I could perhaps just reverse the input and store it infString
. On further thought, only 2 operations -operator+
andoperator-
need the string in reverse order, maybe I can store another reversed string as a member
$endgroup$
– cppprogrammer
8 hours ago
$begingroup$
Onlyoperator+
andoperator-
explicitly reverse, but the Karatsuba multiplication uses those operators...
$endgroup$
– Peter Taylor
8 hours ago
add a comment |
$begingroup$
Absolutely right about renaming Integer to NaturalNumber. The assignment assumed positive numbers and that doesn't get implied anywhere in the code. Extending the class a little bit will open it to negative numbers too. I could store the number in a reverse manner but it makes it harder to reason about the code. When constructing an integer from a raw string, I could perhaps just reverse the input and store it infString
. On further thought, only 2 operations -operator+
andoperator-
need the string in reverse order, maybe I can store another reversed string as a member
$endgroup$
– cppprogrammer
8 hours ago
$begingroup$
Onlyoperator+
andoperator-
explicitly reverse, but the Karatsuba multiplication uses those operators...
$endgroup$
– Peter Taylor
8 hours ago
$begingroup$
Absolutely right about renaming Integer to NaturalNumber. The assignment assumed positive numbers and that doesn't get implied anywhere in the code. Extending the class a little bit will open it to negative numbers too. I could store the number in a reverse manner but it makes it harder to reason about the code. When constructing an integer from a raw string, I could perhaps just reverse the input and store it in
fString
. On further thought, only 2 operations - operator+
and operator-
need the string in reverse order, maybe I can store another reversed string as a member$endgroup$
– cppprogrammer
8 hours ago
$begingroup$
Absolutely right about renaming Integer to NaturalNumber. The assignment assumed positive numbers and that doesn't get implied anywhere in the code. Extending the class a little bit will open it to negative numbers too. I could store the number in a reverse manner but it makes it harder to reason about the code. When constructing an integer from a raw string, I could perhaps just reverse the input and store it in
fString
. On further thought, only 2 operations - operator+
and operator-
need the string in reverse order, maybe I can store another reversed string as a member$endgroup$
– cppprogrammer
8 hours ago
$begingroup$
Only
operator+
and operator-
explicitly reverse, but the Karatsuba multiplication uses those operators...$endgroup$
– Peter Taylor
8 hours ago
$begingroup$
Only
operator+
and operator-
explicitly reverse, but the Karatsuba multiplication uses those operators...$endgroup$
– Peter Taylor
8 hours ago
add a comment |
$begingroup$
I miss comments and use of a documentation tool like doxygen.
- I did not expect separate digit handling loops in
operator+()
andoperator-()
(obviously, I expected handling of signed integers). Nor the asymmetry between the procedures.
In multiply()
,
(I don't quite like sum
for what it names (still undecided about sz
):)
sz-n
occurs just often enough to ponder introducing an abstraction, a name, a variable for it.- "Padding before adding" leads to two 2×sz additions: an alternative would be to exclude the least significant part (say,
bd
) ofBD
from addition, "appending" its most significant part toAC
for a single sz+n addition and just appendbd
.
(The remaining remarks may lead to more code, code more difficult to maintain/read:
revisiting the test harness seems a good idea before coding any.)
- The next step in avoiding complex digit operations would seem to be not padding in
operator±()
and usingincrement()
/decrement()
instead.) - With factors that differ widely in order of magnitude, there may be a lot of padding & stripping: consider an inner procedure doing just *three multiplications and five additions" (possibly with operand lengths passed in) and an outer one taking care of interface (like stripping leading zeroes).
(On second thought, I'm not sure where "tactics" should go: passing the shorter factor where it terminates recursion sooner, extra strategy ("long multiplication") when one factor reaches the base case, handling 0 and powers of "the base" separately (0th power = 1 being a prominent and trivial case).)
$endgroup$
$begingroup$
"Nor the asymmetry between the procedures." - How can this be improved? Aren't addition and subtraction fundamentally different? Inside the subtraction routine, trying to code the borrowing from the next power of 10 gave me a headache. Addition was a breeze
$endgroup$
– cppprogrammer
8 hours ago
add a comment |
$begingroup$
I miss comments and use of a documentation tool like doxygen.
- I did not expect separate digit handling loops in
operator+()
andoperator-()
(obviously, I expected handling of signed integers). Nor the asymmetry between the procedures.
In multiply()
,
(I don't quite like sum
for what it names (still undecided about sz
):)
sz-n
occurs just often enough to ponder introducing an abstraction, a name, a variable for it.- "Padding before adding" leads to two 2×sz additions: an alternative would be to exclude the least significant part (say,
bd
) ofBD
from addition, "appending" its most significant part toAC
for a single sz+n addition and just appendbd
.
(The remaining remarks may lead to more code, code more difficult to maintain/read:
revisiting the test harness seems a good idea before coding any.)
- The next step in avoiding complex digit operations would seem to be not padding in
operator±()
and usingincrement()
/decrement()
instead.) - With factors that differ widely in order of magnitude, there may be a lot of padding & stripping: consider an inner procedure doing just *three multiplications and five additions" (possibly with operand lengths passed in) and an outer one taking care of interface (like stripping leading zeroes).
(On second thought, I'm not sure where "tactics" should go: passing the shorter factor where it terminates recursion sooner, extra strategy ("long multiplication") when one factor reaches the base case, handling 0 and powers of "the base" separately (0th power = 1 being a prominent and trivial case).)
$endgroup$
$begingroup$
"Nor the asymmetry between the procedures." - How can this be improved? Aren't addition and subtraction fundamentally different? Inside the subtraction routine, trying to code the borrowing from the next power of 10 gave me a headache. Addition was a breeze
$endgroup$
– cppprogrammer
8 hours ago
add a comment |
$begingroup$
I miss comments and use of a documentation tool like doxygen.
- I did not expect separate digit handling loops in
operator+()
andoperator-()
(obviously, I expected handling of signed integers). Nor the asymmetry between the procedures.
In multiply()
,
(I don't quite like sum
for what it names (still undecided about sz
):)
sz-n
occurs just often enough to ponder introducing an abstraction, a name, a variable for it.- "Padding before adding" leads to two 2×sz additions: an alternative would be to exclude the least significant part (say,
bd
) ofBD
from addition, "appending" its most significant part toAC
for a single sz+n addition and just appendbd
.
(The remaining remarks may lead to more code, code more difficult to maintain/read:
revisiting the test harness seems a good idea before coding any.)
- The next step in avoiding complex digit operations would seem to be not padding in
operator±()
and usingincrement()
/decrement()
instead.) - With factors that differ widely in order of magnitude, there may be a lot of padding & stripping: consider an inner procedure doing just *three multiplications and five additions" (possibly with operand lengths passed in) and an outer one taking care of interface (like stripping leading zeroes).
(On second thought, I'm not sure where "tactics" should go: passing the shorter factor where it terminates recursion sooner, extra strategy ("long multiplication") when one factor reaches the base case, handling 0 and powers of "the base" separately (0th power = 1 being a prominent and trivial case).)
$endgroup$
I miss comments and use of a documentation tool like doxygen.
- I did not expect separate digit handling loops in
operator+()
andoperator-()
(obviously, I expected handling of signed integers). Nor the asymmetry between the procedures.
In multiply()
,
(I don't quite like sum
for what it names (still undecided about sz
):)
sz-n
occurs just often enough to ponder introducing an abstraction, a name, a variable for it.- "Padding before adding" leads to two 2×sz additions: an alternative would be to exclude the least significant part (say,
bd
) ofBD
from addition, "appending" its most significant part toAC
for a single sz+n addition and just appendbd
.
(The remaining remarks may lead to more code, code more difficult to maintain/read:
revisiting the test harness seems a good idea before coding any.)
- The next step in avoiding complex digit operations would seem to be not padding in
operator±()
and usingincrement()
/decrement()
instead.) - With factors that differ widely in order of magnitude, there may be a lot of padding & stripping: consider an inner procedure doing just *three multiplications and five additions" (possibly with operand lengths passed in) and an outer one taking care of interface (like stripping leading zeroes).
(On second thought, I'm not sure where "tactics" should go: passing the shorter factor where it terminates recursion sooner, extra strategy ("long multiplication") when one factor reaches the base case, handling 0 and powers of "the base" separately (0th power = 1 being a prominent and trivial case).)
edited 8 hours ago
Community♦
1
1
answered 13 hours ago
greybeardgreybeard
1,4961621
1,4961621
$begingroup$
"Nor the asymmetry between the procedures." - How can this be improved? Aren't addition and subtraction fundamentally different? Inside the subtraction routine, trying to code the borrowing from the next power of 10 gave me a headache. Addition was a breeze
$endgroup$
– cppprogrammer
8 hours ago
add a comment |
$begingroup$
"Nor the asymmetry between the procedures." - How can this be improved? Aren't addition and subtraction fundamentally different? Inside the subtraction routine, trying to code the borrowing from the next power of 10 gave me a headache. Addition was a breeze
$endgroup$
– cppprogrammer
8 hours ago
$begingroup$
"Nor the asymmetry between the procedures." - How can this be improved? Aren't addition and subtraction fundamentally different? Inside the subtraction routine, trying to code the borrowing from the next power of 10 gave me a headache. Addition was a breeze
$endgroup$
– cppprogrammer
8 hours ago
$begingroup$
"Nor the asymmetry between the procedures." - How can this be improved? Aren't addition and subtraction fundamentally different? Inside the subtraction routine, trying to code the borrowing from the next power of 10 gave me a headache. Addition was a breeze
$endgroup$
– cppprogrammer
8 hours ago
add a comment |
$begingroup$
Your code is formatted inconsistently. Sometimes you placed spaces around operators, sometimes not, with no apparent patterns or rules. Just let your IDE format the code automatically.
Sometimes you use snake_case, sometimes camelCase. You should only use one of them, for consistency.
Instead of sum = sum%10
you can write sum -= 10
since the sum can never be greater than 19.
Having operator*
restricted to a single digit is unexpected. That function should better be an internal implementation detail, and multiply
should become the new operator*
.
Instead of Integer::print
, you should define the usual os << Integer
operator. Don't use std::endl
unless you really know you need it. A simple 'n'
performs better.
$endgroup$
add a comment |
$begingroup$
Your code is formatted inconsistently. Sometimes you placed spaces around operators, sometimes not, with no apparent patterns or rules. Just let your IDE format the code automatically.
Sometimes you use snake_case, sometimes camelCase. You should only use one of them, for consistency.
Instead of sum = sum%10
you can write sum -= 10
since the sum can never be greater than 19.
Having operator*
restricted to a single digit is unexpected. That function should better be an internal implementation detail, and multiply
should become the new operator*
.
Instead of Integer::print
, you should define the usual os << Integer
operator. Don't use std::endl
unless you really know you need it. A simple 'n'
performs better.
$endgroup$
add a comment |
$begingroup$
Your code is formatted inconsistently. Sometimes you placed spaces around operators, sometimes not, with no apparent patterns or rules. Just let your IDE format the code automatically.
Sometimes you use snake_case, sometimes camelCase. You should only use one of them, for consistency.
Instead of sum = sum%10
you can write sum -= 10
since the sum can never be greater than 19.
Having operator*
restricted to a single digit is unexpected. That function should better be an internal implementation detail, and multiply
should become the new operator*
.
Instead of Integer::print
, you should define the usual os << Integer
operator. Don't use std::endl
unless you really know you need it. A simple 'n'
performs better.
$endgroup$
Your code is formatted inconsistently. Sometimes you placed spaces around operators, sometimes not, with no apparent patterns or rules. Just let your IDE format the code automatically.
Sometimes you use snake_case, sometimes camelCase. You should only use one of them, for consistency.
Instead of sum = sum%10
you can write sum -= 10
since the sum can never be greater than 19.
Having operator*
restricted to a single digit is unexpected. That function should better be an internal implementation detail, and multiply
should become the new operator*
.
Instead of Integer::print
, you should define the usual os << Integer
operator. Don't use std::endl
unless you really know you need it. A simple 'n'
performs better.
edited 13 hours ago
Toby Speight
24.5k740115
24.5k740115
answered 15 hours ago
Roland IlligRoland Illig
11.1k11844
11.1k11844
add a comment |
add a comment |
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e) {
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom)) {
StackExchange.using('gps', function() { StackExchange.gps.track('embedded_signup_form.view', { location: 'question_page' }); });
$window.unbind('scroll', onScroll);
}
};
$window.on('scroll', onScroll);
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f213686%2fkaratsuba-multiplication%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e) {
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom)) {
StackExchange.using('gps', function() { StackExchange.gps.track('embedded_signup_form.view', { location: 'question_page' }); });
$window.unbind('scroll', onScroll);
}
};
$window.on('scroll', onScroll);
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e) {
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom)) {
StackExchange.using('gps', function() { StackExchange.gps.track('embedded_signup_form.view', { location: 'question_page' }); });
$window.unbind('scroll', onScroll);
}
};
$window.on('scroll', onScroll);
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e) {
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom)) {
StackExchange.using('gps', function() { StackExchange.gps.track('embedded_signup_form.view', { location: 'question_page' }); });
$window.unbind('scroll', onScroll);
}
};
$window.on('scroll', onScroll);
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
$begingroup$
What is a Gauss trick, and how Karatsuba.cpp performs it?
$endgroup$
– vnp
17 hours ago
$begingroup$
Karatsuba is div-conq multiplication. If $x$ and $y$ are $n$-digit strings in some base $B$, then for any positive integer $m < n$, you can write $x = aB^m + b, y = cB^m + d$ where $b$ and $d$ are less than $B^m$. The product $x cdot y = ldots =acB^{2m} + (ad + bc)B^m + bd$. Applying Gauss' complex multiplication algorithm, reuse the results of $ac$ and $bd$ to find $(ad + bc)$. Thus, $(ad + bc) = (a + b)(c + d) - ac - bd$, reducing the no. of recursive multiplications at the cost of extra additions. This is performed when
AD_plus_BC
is introduced in the code.$endgroup$
– Snowhawk
16 hours ago