在计算数学中,有效地乘以大数是从密码学到科学计算等各种应用的基石。 Karatsuba 乘法算法 是一种分而治之的方法,与传统的大数长乘法相比,它显着提高了性能。在本文中,我们将探索这种强大算法的 JavaScript 实现,该算法旨在处理表示为字符串的任意大数字。
标准的“教科书”乘法方法的时间复杂度为 (O(n2)) , 在哪里 (n) 是被相乘的数字的位数。随着数字变大,这种二次增长在计算上变得昂贵。 Anatolii Karatsuba 于 1960 年提出的 Karatsuba 算法将这种复杂性降低到大约 (O(n1.585)) ,使其成为大输入的更快选择。
该算法依赖于分治策略:
这种方法将递归乘法的次数从 4 次减少到 3 次,从而提高了效率。
下面是 JavaScript 中 Karasuba 算法的稳健实现。此版本通过将任意大整数表示为字符串来支持它们。
乘法.js
/** * Karatsuba multiplication algorithm for large numbers. * @param {string} num1 - First large number as a string. * @param {string} num2 - Second large number as a string. * @returns {string} - Product of the two numbers as a string. */ function karatsubaMultiply(num1, num2) { // Remove leading zeros num1 = num1.replace(/^0+/, "") || "0"; num2 = num2.replace(/^0+/, "") || "0"; // If either number is zero, return "0" if (num1 === "0" || num2 === "0") return "0"; // Base case for small numbers (12), use Number for safe multiplication if (num1.length <= 12 && num2.length <= 12) { return (Number(num1) * Number(num2)).toString(); } // Ensure even length by padding const maxLen = Math.max(num1.length, num2.length); const paddedLen = Math.ceil(maxLen / 2) * 2; num1 = num1.padStart(paddedLen, "0"); num2 = num2.padStart(paddedLen, "0"); const mid = paddedLen / 2; // Split the numbers into two halves const high1 = num1.slice(0, -mid); const low1 = num1.slice(-mid); const high2 = num2.slice(0, -mid); const low2 = num2.slice(-mid); // Helper function for adding large numbers as strings function addLargeNumbers(a, b) { const maxLength = Math.max(a.length, b.length); a = a.padStart(maxLength, "0"); b = b.padStart(maxLength, "0"); let result = ""; let carry = 0; for (let i = maxLength - 1; i >= 0; i--) { const sum = parseInt(a[i]) + parseInt(b[i]) + carry; result = (sum % 10) + result; carry = Math.floor(sum / 10); } if (carry > 0) { result = carry + result; } return result.replace(/^0+/, "") || "0"; } // Helper function to multiply by 10^n function multiplyByPowerOf10(num, power) { return num === "0" ? "0" : num + "0".repeat(power); } // Helper function for subtracting large numbers function subtractLargeNumbers(a, b) { const maxLength = Math.max(a.length, b.length); a = a.padStart(maxLength, "0"); b = b.padStart(maxLength, "0"); let result = ""; let borrow = 0; for (let i = maxLength - 1; i >= 0; i--) { let diff = parseInt(a[i]) - parseInt(b[i]) - borrow; if (diff < 0) { diff += 10; borrow = 1; } else { borrow = 0; } result = diff + result; } return result.replace(/^0+/, "") || "0"; } // Recursive steps const z0 = karatsubaMultiply(low1, low2); const z1 = karatsubaMultiply( addLargeNumbers(low1, high1), addLargeNumbers(low2, high2) ); const z2 = karatsubaMultiply(high1, high2); // Compute the result using Karatsuba formula const z1MinusZ2MinusZ0 = subtractLargeNumbers( subtractLargeNumbers(z1, z2), z0 ); const powerMidTerm = multiplyByPowerOf10(z1MinusZ2MinusZ0, mid); const z2Term = multiplyByPowerOf10(z2, 2 * mid); // Add all terms const term1 = addLargeNumbers(z2Term, powerMidTerm); const result = addLargeNumbers(term1, z0); return result; } // Example Usage const num1 = "1234567890123456789023454353453454354345435345435435"; const num2 = "98765432109876543210"; console.log("Product:", karatsubaMultiply(num1, num2));
node multiply.js
基础案例优化:
任意精度的字符串操作:
辅助功能:
递归设计:
Karatsuba 算法减少了递归乘法的次数 (O(n2)) 到大约 (O(n1.585)) 。这使得它比大输入的传统方法要快得多。然而,字符串操作的开销可能会影响较小输入的性能,这就是为什么基本情况优化至关重要。
对于:
/** * Karatsuba multiplication algorithm for large numbers. * @param {string} num1 - First large number as a string. * @param {string} num2 - Second large number as a string. * @returns {string} - Product of the two numbers as a string. */ function karatsubaMultiply(num1, num2) { // Remove leading zeros num1 = num1.replace(/^0+/, "") || "0"; num2 = num2.replace(/^0+/, "") || "0"; // If either number is zero, return "0" if (num1 === "0" || num2 === "0") return "0"; // Base case for small numbers (12), use Number for safe multiplication if (num1.length <= 12 && num2.length <= 12) { return (Number(num1) * Number(num2)).toString(); } // Ensure even length by padding const maxLen = Math.max(num1.length, num2.length); const paddedLen = Math.ceil(maxLen / 2) * 2; num1 = num1.padStart(paddedLen, "0"); num2 = num2.padStart(paddedLen, "0"); const mid = paddedLen / 2; // Split the numbers into two halves const high1 = num1.slice(0, -mid); const low1 = num1.slice(-mid); const high2 = num2.slice(0, -mid); const low2 = num2.slice(-mid); // Helper function for adding large numbers as strings function addLargeNumbers(a, b) { const maxLength = Math.max(a.length, b.length); a = a.padStart(maxLength, "0"); b = b.padStart(maxLength, "0"); let result = ""; let carry = 0; for (let i = maxLength - 1; i >= 0; i--) { const sum = parseInt(a[i]) + parseInt(b[i]) + carry; result = (sum % 10) + result; carry = Math.floor(sum / 10); } if (carry > 0) { result = carry + result; } return result.replace(/^0+/, "") || "0"; } // Helper function to multiply by 10^n function multiplyByPowerOf10(num, power) { return num === "0" ? "0" : num + "0".repeat(power); } // Helper function for subtracting large numbers function subtractLargeNumbers(a, b) { const maxLength = Math.max(a.length, b.length); a = a.padStart(maxLength, "0"); b = b.padStart(maxLength, "0"); let result = ""; let borrow = 0; for (let i = maxLength - 1; i >= 0; i--) { let diff = parseInt(a[i]) - parseInt(b[i]) - borrow; if (diff < 0) { diff += 10; borrow = 1; } else { borrow = 0; } result = diff + result; } return result.replace(/^0+/, "") || "0"; } // Recursive steps const z0 = karatsubaMultiply(low1, low2); const z1 = karatsubaMultiply( addLargeNumbers(low1, high1), addLargeNumbers(low2, high2) ); const z2 = karatsubaMultiply(high1, high2); // Compute the result using Karatsuba formula const z1MinusZ2MinusZ0 = subtractLargeNumbers( subtractLargeNumbers(z1, z2), z0 ); const powerMidTerm = multiplyByPowerOf10(z1MinusZ2MinusZ0, mid); const z2Term = multiplyByPowerOf10(z2, 2 * mid); // Add all terms const term1 = addLargeNumbers(z2Term, powerMidTerm); const result = addLargeNumbers(term1, z0); return result; } // Example Usage const num1 = "1234567890123456789023454353453454354345435345435435"; const num2 = "98765432109876543210"; console.log("Product:", karatsubaMultiply(num1, num2));
结果是:
node multiply.js
Karatsuba 乘法算法是一种实用且高效的大数乘法解决方案。此实现在处理 JavaScript 中的任意大输入时展示了其强大功能和灵活性。随着对高精度运算的需求不断增长,掌握此类算法可以大大增强各种应用中的计算能力。
以上是理解并实现大数的 Karatsuba 乘法算法的详细内容。更多信息请关注PHP中文网其他相关文章!