我正在尝试在javascript中实现自己的LargeInteger类(如果成功,以后将移植到Java和Swift)。但是,运行时间不能令人满意。我想看看我可以做些什么来改善实施。
我已经实现了Kanatusba乘法,并使用牛顿法进行除法。但是,它们仍然需要花费很长时间,尤其是数字较长时。
class LargeInteger {
//positive, digits
//constructor
constructor(s) {
if (s == "") {
return new LargeInteger("0");
}
//trim leading zeros
var i = 0;
while (s[i] == "0") {
i++;
}
s = s.substring(i);
if (s == "") {
s = "0";
}
this.positive = !(s[0] == "-");
this.digits = [];
if (this.positive) {
for (var i = 0; i < s.length; i++) {
this.digits.push(parseInt(s[i]));
}
} else {
for (var i = 1; i < s.length; i++) {
this.digits.push(parseInt(s[i]));
}
}
}
static init(digits, positive) {
if (positive) {
return new LargeInteger(digits.join(""));
} else {
return new LargeInteger("-" + digits.join(""));
}
}
get length() {
return this.digits.length;
}
isZero() {
return ((this.length == 1) && (this.digit(0) == 0))
}
isOne() {
return ((this.length == 1) && (this.digit(0) == 1))
}
//getter - nth digit
digit(n) {
return this.digits[n];
}
//print
print() {
if (this.positive) {
return this.digits.join("");
} else {
return "-" + this.digits.join("");
}
}
//negation
negate() {
return LargeInteger.init(this.digits, !this.positive);
}
//addition
add(q) {
if ((this.positive) && (!q.positive)) {
return this.subtract(q.negate());
} else if ((!this.positive) && (q.positive)) {
return q.subtract(this.negate());
}
var m = this.length;
var n = q.length;
if (m >= n) {
var larger = this;
var smaller = q;
} else {
var larger = q;
var smaller = this;
m = q.length;
n = this.length;
}
//longer (length m), shorter (length n)
var sumDigits = [];
var carry = 0;
var reverseDigitCount = 0;
for (reverseDigitCount = 0; reverseDigitCount < n; reverseDigitCount++) {
var sumDigit = larger.digit(m - reverseDigitCount - 1) + smaller.digit(n - reverseDigitCount - 1) + carry;
if (sumDigit >= 10) {
carry = 1;
sumDigit -= 10;
} else {
carry = 0;
}
if (reverseDigitCount == 0) {
sumDigits[0] = sumDigit;
} else {
sumDigits.unshift(sumDigit);
}
}
for (reverseDigitCount = reverseDigitCount; reverseDigitCount < m; reverseDigitCount++) {
if (larger.digit(m - reverseDigitCount - 1) + carry >= 10) {
sumDigits.unshift(larger.digit(m - reverseDigitCount - 1) + carry - 10);
carry = 1;
} else {
sumDigits.unshift(larger.digit(m - reverseDigitCount - 1) + carry);
carry = 0;
}
}
if (reverseDigitCount == m) {
if (carry == 1) {
sumDigits.unshift(1);
}
reverseDigitCount++;
}
var sum = new LargeInteger(sumDigits.join(""));
if ((this.positive) && (q.positive)) {
return sum;
} else {
return sum.negate();
}
}
//comparison
compare(q) {
if ((this.positive) && (!q.positive)) {
return 1;
}
if ((!this.positive) && (q.positive)) {
return -1;
}
if (this.length > q.length) {
if ((this.positive) && (q.positive)) {
return 1;
} else {
return -1;
}
}
if (q.length > this.length) {
if ((this.positive) && (q.positive)) {
return -1;
} else {
return 1;
}
}
for (var i = 0; i < this.length; i++) {
if (this.digit(i) > q.digit(i)) {
if ((this.positive) && (q.positive)) {
return 1;
} else {
return -1;
}
} else if (this.digit(i) < q.digit(i)) {
if ((this.positive) && (q.positive)) {
return -1;
} else {
return 1;
}
}
}
return 0;
}
//subtraction
subtract(q) {
if (this.compare(q) == 0) {
return new LargeInteger("0");
}
if (!q.positive) {
return this.add(q.negate());
}
if (this.compare(q) == -1) {
return q.subtract(this).negate();
}
//this larger than q
var m = this.length;
var n = q.length;
var diffDigits = [];
var carry = 0;
var reverseDigitCount = 0;
for (reverseDigitCount = 0; reverseDigitCount < n; reverseDigitCount++) {
var diffDigit = this.digit(m - reverseDigitCount - 1) - q.digit(n - reverseDigitCount - 1) + carry;
if (diffDigit < 0) {
carry = -1;
diffDigit += 10;
} else {
carry = 0;
}
if (reverseDigitCount == 0) {
diffDigits[0] = diffDigit;
} else {
diffDigits.unshift(diffDigit);
}
}
for (reverseDigitCount = reverseDigitCount; reverseDigitCount < m; reverseDigitCount++) {
if (this.digit(m - reverseDigitCount - 1) + carry < 0) {
diffDigits.unshift(this.digit(m - reverseDigitCount - 1) + carry + 10);
carry = -1;
} else {
diffDigits.unshift(this.digit(m - reverseDigitCount - 1) + carry);
carry = 0;
}
}
var difference = new LargeInteger(diffDigits.join(""));
return difference;
}
//multiplication
multiply(q) {
if ((this.isZero()) || (q.isZero())) {
return new LargeInteger("0");
}
if ((this.positive) && (!q.positive)) {
return this.multiply(q.negate()).negate();
} else if ((!this.positive) && (q.positive)) {
return this.negate().multiply(q).negate();
} else if ((!this.positive) && (!q.positive)) {
return this.negate().multiply(q.negate());
}
if ((this.length <= 1) || (q.length <= 1)) {
if (this.length == 1) {
var singleDigit = this;
var theOtherNumber = q;
} else {
var singleDigit = q;
var theOtherNumber = this;
}
var carry = 0;
var productString = "";
for (var i = 0; i < theOtherNumber.length; i++) {
var productTemp = singleDigit.digit(0) * theOtherNumber.digit(theOtherNumber.length - i - 1) + carry;
if (productTemp >= 10) {
productString = (productTemp % 10).toString() + productString;
carry = Math.floor(productTemp / 10);
} else {
productString = productTemp.toString() + productString;
carry = 0;
}
}
if (carry > 0) {
productString = carry.toString() + productString;
}
return new LargeInteger(productString);
}
var m = this.length;
var n = q.length;
var k = (m >= n) ? Math.round(m/2) : Math.round(n/2); //must use the larger
if (this.length >= k + 1) {
var high1 = LargeInteger.init(this.digits.slice(0, m - k), true);
var low1 = LargeInteger.init(this.digits.slice(m - k), true);
} else {
var high1 = new LargeInteger("0");
var low1 = this;
}
if (q.length >= k + 1) {
var high2 = LargeInteger.init(q.digits.slice(0, n - k), true);
var low2 = LargeInteger.init(q.digits.slice(n - k), true);
} else {
var high2 = new LargeInteger("0");
var low2 = q;
}
var x = low1.multiply(low2);
var w = low2.add(high2);
var y = low1.add(high1).multiply(w);
var z = high1.multiply(high2);
var prod1S = z.digits.slice(0);
if (!z.isZero()) {
for (var i = 0; i < 2 * k; i++) {
prod1S.push(0);
}
}
var prod1 = LargeInteger.init(prod1S, true);
var prod2Temp = y.subtract(z).subtract(x);
var prod2S = prod2Temp.digits.slice(0);
if (!prod2Temp.isZero()) {
for (var i = 0; i < k; i++) {
prod2S.push(0);
}
}
var prod2 = LargeInteger.init(prod2S, prod2Temp.positive);
var product = prod1.add(prod2).add(x);
return product;
}
//division by q
divide(q) {
if (this.isZero()) {
return new Array(new LargeInteger("0"), new LargeInteger("0"));
}
if (q.isOne()) {
return new Array(this, new LargeInteger("0"));
}
if (!q.positive) {
var tempResult = this.divide(q.negate());
return new Array(tempResult[0].negate(), tempResult[1]);
}
if (!this.positive) {
var tempResult = this.negate().divide(q);
if (tempResult[1].positive()) {
return new Array(tempResult[0].negate().subtract(new LargeInteger("1")), q.subtract(tempResult[1]));
} else {
return new Array(tempResult[0].negate(), new LargeInteger("0"));
}
}
var m = this.length;
var n = q.length;
if (m < n) {
return new Array(new LargeInteger("0"), this);
}
if (m == n) {
var quotient = new LargeInteger("0");
var compareResult = quotient.multiply(q).compare(this);
for (var i = 1; i <= 9; i++) {
quotient = new LargeInteger(i.toString());
if (quotient.multiply(q).compare(this) != compareResult) {
compareResult = quotient.multiply(q).compare(this);
break;
}
}
if (compareResult != 0) {
quotient = quotient.subtract(new LargeInteger("1"));
}
return new Array(quotient, this.subtract(q.multiply(quotient)));
}
if (m > n) {
//pown = 10^n
//a = floor(10^n/q)
var pownString = "1";
for (var i = 0; i < n; i++) {
pownString += "0";
}
var pown = new LargeInteger(pownString);
var a = new LargeInteger("1");
var compareResult = a.multiply(q).compare(pown);
for (var i = 2; i <= 10; i++) {
a = new LargeInteger(i.toString());
if (a.multiply(q).compare(pown) != compareResult) {
break;
}
}
a = a.subtract(new LargeInteger("1"));
//set y0
var powString = "1";
for (var i = 0; i < m - n; i++) {
powString += "0";
}
var pow = new LargeInteger(powString);
var leastIterate = 1;
if (pown.multiply(new LargeInteger("2")).subtract(q.multiply(new LargeInteger("2")).multiply(a)).compare(q) <= 0) {
var y = [pow.multiply(a)];
} else {
var y = [pow.multiply(a.add(new LargeInteger("1")))];
leastIterate = 2;
}
//y0 prepared
//start iterating
var i = 0;
for (i = 0; i < leastIterate; i++) { //prepared up to y1 or y2
var comp1 = y[i].multiply(y[i]).digits.join(""); //string
var comp2 = new LargeInteger(comp1.substring(0, comp1.length - m + n));
var comp3 = comp2.multiply(q).digits.join(""); //string
var comp = new LargeInteger(comp3.substring(0, comp3.length - n));
y.push(y[i].multiply(new LargeInteger("2")).subtract(comp));
}
while (y[i].compare(y[i-1]) == 1) {
comp1 = y[i].multiply(y[i]).digits.join(""); //string
comp2 = new LargeInteger(comp1.substring(0, comp1.length - m + n));
comp3 = comp2.multiply(q).digits.join(""); //string
comp = new LargeInteger(comp3.substring(0, comp3.length - n));
y.push(y[i].multiply(new LargeInteger("2")).subtract(comp));
i++;
}
var l = y[i];
var powmString = "1";
for (var j = 0; j < m; j++) {
powmString += "0";
}
var powm = new LargeInteger(powmString);
var compareResult = l.multiply(q).compare(powm);
while ((compareResult > 0) && (l.positive)) {
l = l.subtract(new LargeInteger("1"));
compareResult = l.multiply(q).compare(powm);
}
var k = l;
var tString = this.multiply(k).digits.join("");
var t = new LargeInteger(tString.substring(0, tString.length - m));
var tPlusOne = t.add(new LargeInteger("1"));
var compareResult1 = t.multiply(q).compare(this);
var compareResult2 = tPlusOne.multiply(q).compare(this);
if ((compareResult1 <= 0) && (compareResult2 <= 0)) {
var quotient = tPlusOne;
} else if (compareResult1 <= 0) {
var quotient = t;
} else {
var quotient = tPlusOne;
}
var remainder = this.subtract(q.multiply(quotient));
return new Array(quotient, remainder);
}
}
}
使用我的实现,乘法与运行时间一起显示:
45762386904386908439068459078903489028389086098908439058239048290380383082308409 * 355353636389024839058320985423098569024386902385492 = 16261830596315377465025794335398860058130415744953953987538577499938393893814539400045882476069301296744527463216854780249875202228p
65.8499999990454毫秒
但是,使用其他一些库可以在1毫秒内完成乘法。