如何在 javascript 中实现 Karatsuba 乘法?
How to implement Karatsuba Multiplication in javascript?
我尝试使用以下代码实现 karatsuba algorithm。当 x 和 y(参数)中的位数不匹配时,问题就开始了,因为 recursive call
在这种情况下使用以下逻辑不起作用。截至目前,当 x 和 y 中的位数相同时,我得到了正确的输出。
更准确地说,我认为问题始于 z1 和 z3 的计算,因为这是 x 和 y 的位数经常不匹配的地方。
我也对推导出关于如何定义 m
的逻辑感到有点困惑,这是这里基础的力量。我相信我的问题已经说清楚了吗?
(任何关于更多优化的建议都会更有帮助,因为我刚刚开始我的 java 脚本之旅)。
function karatSuba(x,y)
{
var x1,x0,y1,y0,base,m;
var dummy_x = x.toString();
var dummy_y = y.toString();
var n = (dummy_x.length>dummy_y.length) ? dummy_y.length : dummy_x.length;
m = Math.round(n/2);
base = 10;
//base case
if((x<base)||(y<base))
return x * y;
//base case
var bm = Math.pow(base ,m);
var dummy_x1 = dummy_x.substring(0,n/2);
var x1 = parseInt(dummy_x1);
dummy_x1 = null;
var dummy_x1 = dummy_x.substring(n/2,n);
var x0 = parseInt(dummy_x1);
dummy_x1 = null;
var dummy_y1 = dummy_y.substring(0,n/2);
var y1 = parseInt(dummy_y1);
dummy_y1 = null;
var dummy_y0 = dummy_y.substring(n/2,n);
var y0 = parseInt(dummy_y0);
dummy_y = null;
var p = x1 + x0;
var q = y1 + y0;
var a = karatSuba(x1,y1);
var b = karatSuba(x0,y0);
var z1 = karatSuba(a,Math.pow(bm,2));
var z2 = b;
//var z3 = karatSmul(bm ,((karatSmul(p,q) - a - b)));
var z3 = bm * ((p*q) - (a) - (b));
var z = z1 + z2 + z3;
return z;
}
console.log(karatSuba(344,100));
您编写算法的方式有几个错误。下面的代码应该可以工作。
function karatSuba(x,y)
{
var x1,x0,y1,y0,base,m;
base = 10;
if((x<base)||(y<base)){
console.log( " X - y = " , x,y, x*y)
return x * y;
}
var dummy_x = x.toString();
var dummy_y = y.toString();
var n = (dummy_x.length > dummy_y.length) ? dummy_y.length : dummy_x.length;
m = Math.round(n/2);
var high1 = parseInt(dummy_x.substring(0,dummy_x.length-m));
var low1 = parseInt(dummy_x.substring(dummy_x.length-m,dummy_x.length )) ;
var high2 = parseInt(dummy_y.substring(0,dummy_y.length-m));
var low2 = parseInt(dummy_y.substring(dummy_y.length-m,dummy_y.length));
var z0 = karatSuba( low1, low2);
var z1 = karatSuba(low1+high1, low2+high2);
var z2 = karatSuba(high1,high2);
var res = (z2 * Math.pow(10, 2 * m ) ) + ( (z1-z2-z0) * Math.pow(10, m )) + z0;
return res;
}
var a = 12345;
var b = 6789;
console.log(karatSuba(a,b));
console.log(a * b);
function range(start, stop, step) {
if (typeof stop == 'undefined') {
// one param defined
stop = start;
start = 0;
}
if (typeof step == 'undefined') {
step = 1;
}
if ((step > 0 && start >= stop) || (step < 0 && start <= stop)) {
return [];
}
var result = [];
for (var i = start; step > 0 ? i < stop : i > stop; i += step) {
result.push(i);
}
return result;
};
function zeroPad(numberString, zeros, left = true) {
//Return the string with zeros added to the left or right.
for (var i in range(zeros)) {
if (left)
numberString = '0' + numberString
else
numberString = numberString + '0'
}
return numberString
}
function largeMultiplcation(x, y) {
x = x.toString();
y = y.toString();
if (x.length == 1 && y.length == 1)
return int(x) * int(y)
if (x.length() < y.length)
x = zeroPad(x, y.length - x.length);
else
y = zeroPad(y, x.length - y.length);
n = x.length
j = Math.floor(n/2);
//for odd digit integers
if ( n % 2 != 0)
j += 1
var BZeroPadding = n - j
var AZeroPadding = BZeroPadding * 2
a = parseInt(x.substring(0,j));
b = parseInt(x.substring(j));
c = parseInt(y.substring(0,j));
d = parseInt(y.substring(j));
//recursively calculate
ac = LargeMultiplication(a, c)
bd = LargeMultiplication(b, d)
k = LargeMultiplication(a + b, c + d)
A = int(zeroPad(str(ac), AZeroPadding, false))
B = int(zeroPad(str(k - ac - bd), BZeroPadding, false))
return A + B + bd
}
如果你的输入是数字而不是字符串,你会这样做:
function karatsuba(x, y) {
if (x<10 && y<10) {
return x*y;
}
let maxLength = Math.max(x.toString().length, y.toString().length);
let m = Math.round(maxLength/2);
let xHigh = Math.floor(x/ Math.pow(10,m));
let yHigh = Math.floor(y/ Math.pow(10,m));
let xLow = x % Math.pow(10,m);
let yLow = y % Math.pow(10,m);
let a = karatsuba(xHigh, yHigh);
let d = karatsuba(xLow, yLow);
let e = karatsuba(xLow+xHigh, yLow+yHigh)-a-d;
return a * Math.pow(10, m*2) + e * Math.pow(10,m) + d;
}
当然,如果你使用这种方法,当你将更大的数字相乘时,比如两个 64 位数字,你会损失一些精度。 (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER)
解决方法是使用 BigInt 原语 (https://github.com/tc39/proposal-bigint) or ditch JS for Python. (https://www.geeksforgeeks.org/what-is-maximum-possible-value-of-an-integer-in-python/)
我尝试使用以下代码实现 karatsuba algorithm。当 x 和 y(参数)中的位数不匹配时,问题就开始了,因为 recursive call
在这种情况下使用以下逻辑不起作用。截至目前,当 x 和 y 中的位数相同时,我得到了正确的输出。
更准确地说,我认为问题始于 z1 和 z3 的计算,因为这是 x 和 y 的位数经常不匹配的地方。
我也对推导出关于如何定义 m
的逻辑感到有点困惑,这是这里基础的力量。我相信我的问题已经说清楚了吗?
(任何关于更多优化的建议都会更有帮助,因为我刚刚开始我的 java 脚本之旅)。
function karatSuba(x,y)
{
var x1,x0,y1,y0,base,m;
var dummy_x = x.toString();
var dummy_y = y.toString();
var n = (dummy_x.length>dummy_y.length) ? dummy_y.length : dummy_x.length;
m = Math.round(n/2);
base = 10;
//base case
if((x<base)||(y<base))
return x * y;
//base case
var bm = Math.pow(base ,m);
var dummy_x1 = dummy_x.substring(0,n/2);
var x1 = parseInt(dummy_x1);
dummy_x1 = null;
var dummy_x1 = dummy_x.substring(n/2,n);
var x0 = parseInt(dummy_x1);
dummy_x1 = null;
var dummy_y1 = dummy_y.substring(0,n/2);
var y1 = parseInt(dummy_y1);
dummy_y1 = null;
var dummy_y0 = dummy_y.substring(n/2,n);
var y0 = parseInt(dummy_y0);
dummy_y = null;
var p = x1 + x0;
var q = y1 + y0;
var a = karatSuba(x1,y1);
var b = karatSuba(x0,y0);
var z1 = karatSuba(a,Math.pow(bm,2));
var z2 = b;
//var z3 = karatSmul(bm ,((karatSmul(p,q) - a - b)));
var z3 = bm * ((p*q) - (a) - (b));
var z = z1 + z2 + z3;
return z;
}
console.log(karatSuba(344,100));
您编写算法的方式有几个错误。下面的代码应该可以工作。
function karatSuba(x,y)
{
var x1,x0,y1,y0,base,m;
base = 10;
if((x<base)||(y<base)){
console.log( " X - y = " , x,y, x*y)
return x * y;
}
var dummy_x = x.toString();
var dummy_y = y.toString();
var n = (dummy_x.length > dummy_y.length) ? dummy_y.length : dummy_x.length;
m = Math.round(n/2);
var high1 = parseInt(dummy_x.substring(0,dummy_x.length-m));
var low1 = parseInt(dummy_x.substring(dummy_x.length-m,dummy_x.length )) ;
var high2 = parseInt(dummy_y.substring(0,dummy_y.length-m));
var low2 = parseInt(dummy_y.substring(dummy_y.length-m,dummy_y.length));
var z0 = karatSuba( low1, low2);
var z1 = karatSuba(low1+high1, low2+high2);
var z2 = karatSuba(high1,high2);
var res = (z2 * Math.pow(10, 2 * m ) ) + ( (z1-z2-z0) * Math.pow(10, m )) + z0;
return res;
}
var a = 12345;
var b = 6789;
console.log(karatSuba(a,b));
console.log(a * b);
function range(start, stop, step) {
if (typeof stop == 'undefined') {
// one param defined
stop = start;
start = 0;
}
if (typeof step == 'undefined') {
step = 1;
}
if ((step > 0 && start >= stop) || (step < 0 && start <= stop)) {
return [];
}
var result = [];
for (var i = start; step > 0 ? i < stop : i > stop; i += step) {
result.push(i);
}
return result;
};
function zeroPad(numberString, zeros, left = true) {
//Return the string with zeros added to the left or right.
for (var i in range(zeros)) {
if (left)
numberString = '0' + numberString
else
numberString = numberString + '0'
}
return numberString
}
function largeMultiplcation(x, y) {
x = x.toString();
y = y.toString();
if (x.length == 1 && y.length == 1)
return int(x) * int(y)
if (x.length() < y.length)
x = zeroPad(x, y.length - x.length);
else
y = zeroPad(y, x.length - y.length);
n = x.length
j = Math.floor(n/2);
//for odd digit integers
if ( n % 2 != 0)
j += 1
var BZeroPadding = n - j
var AZeroPadding = BZeroPadding * 2
a = parseInt(x.substring(0,j));
b = parseInt(x.substring(j));
c = parseInt(y.substring(0,j));
d = parseInt(y.substring(j));
//recursively calculate
ac = LargeMultiplication(a, c)
bd = LargeMultiplication(b, d)
k = LargeMultiplication(a + b, c + d)
A = int(zeroPad(str(ac), AZeroPadding, false))
B = int(zeroPad(str(k - ac - bd), BZeroPadding, false))
return A + B + bd
}
如果你的输入是数字而不是字符串,你会这样做:
function karatsuba(x, y) {
if (x<10 && y<10) {
return x*y;
}
let maxLength = Math.max(x.toString().length, y.toString().length);
let m = Math.round(maxLength/2);
let xHigh = Math.floor(x/ Math.pow(10,m));
let yHigh = Math.floor(y/ Math.pow(10,m));
let xLow = x % Math.pow(10,m);
let yLow = y % Math.pow(10,m);
let a = karatsuba(xHigh, yHigh);
let d = karatsuba(xLow, yLow);
let e = karatsuba(xLow+xHigh, yLow+yHigh)-a-d;
return a * Math.pow(10, m*2) + e * Math.pow(10,m) + d;
}
当然,如果你使用这种方法,当你将更大的数字相乘时,比如两个 64 位数字,你会损失一些精度。 (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER)
解决方法是使用 BigInt 原语 (https://github.com/tc39/proposal-bigint) or ditch JS for Python. (https://www.geeksforgeeks.org/what-is-maximum-possible-value-of-an-integer-in-python/)