The Integer Multiplication
Problem: Integer Multiplication
Input: 两个n位非负整数,x和y
output: x和y的乘积(product)
The Grade-School Algorithm
为了唤起你的记忆,考虑x = 5678
和y = 1234
相乘的具体例子(所以n = 4)。参见图1.1。该算法首先计算第一个数字和第二个数字的最后一个数字的部分乘积5678 * 4 = 22712
。计算这个部分积归结为将第一个输入(x)的每位数字乘以4,并根据需要加上“进位”。接下来,当计算下一个部分积(5678 * 3 = 17034
)时,我们做同样的事情,将第二个输入(y)向左移动一个位数字,并乘以第一个输入(x)的每一位数,在结果的末尾有规则地添加“0”。(个位时零个0,十位时一个0,……以此类推)最后一步是把所有部分积加起来。
# !/usr/bin/python3
# -*- coding: utf-8 -*-
# @Time : 2021/2/9 22:24
# @Author : Yu LiXinQian
# @Email : yulixinqian805@gmail.com
# @File : NaiveSolution.py
# @Project : Algorithm_illuminated
print("Two n-digit number:")
print("Please enter 2 numbers:")
x = input("X : ")
x = int(x)
y = input("Y : ")
y_s = [int(y) // (10**i) % 10 for i in range(len(y))]
partial = [y_s[i] * x * 10**i for i in range(len(y_s))]
print("Naive Solution:")
print(y_s)
print(partial)
print(sum(partial))
Can we do better?
Recursive Algorithm
为了了解Karatsuba乘法,让我们重新使用前面的例子,x = 5678
,y = 1234
。我们将执行一系列步骤,与小学算法完全不同,最终得到x y。这些步骤的顺序应该给你留下非常神秘的印象,就像从帽子里拉出一只兔子。我们将解释什么是Karatsuba乘法,以及它的工作原理。现在要理解的关键点是,有一系列令人眼花缭乱的选项来解决像整数乘法这样的计算问题。
首先,为了把x的前半部分和后半部分看作是独立的数字,我们给它们起了名字 a 和 b (所以a = 56,b = 78)。类似地,c和d分别表示12 和 34。
接下来,我们将执行一系列操作,只涉及两位数的数字a、b、c、a、n、d,最后以一种神奇的方式将所有项收集在一起,得到x和y的乘积。
-
a ∗ c = 56 ∗ 12 = 672 a * c = 56 *12 = 672 a∗c=56∗12=672
-
b ∗ d = 78 ∗ 34 = 2652 b * d = 78 * 34 = 2652 b∗d=78∗34=2652
-
( a + b ) ∗ ( c + d ) = 134 ∗ 46 = 6164 (a + b) * (c + d)= 134 * 46 = 6164 (a+b)∗(c+d)=134∗46=6164
-
6164 − 672 − 2652 = 2840 6164 - 672 - 2652 = 2840 6164−672−2652=2840
-
1 0 4 ∗ 672 + 1 0 2 ∗ 2840 + 2652 = 70066552 10^4 * 672 + 10^2 * 2840 + 2652 = 70066552 104∗672+102∗2840+2652=70066552
一般来说,一个偶数 n 位数的数 x 可以用两个n/2位数来表示,其前半部分和后半部分 a 和 b : x = 10 n/2 * a + b ,同样,我们可以写成 y = 10 n/2 * a + b。
表达式提出了一种递归方法来相乘两个数字。为了计算乘积x * y
,我们计算表达式。四个相关的乘积(a * c,a * d,b * c, b * d) 涉及少于n位数的数,所以我们可以递归地计算每一个。一旦我们的四个递归调用带着它们的答案返回给我们,我们就可以以显而易见的方式计算表达式。我们在下面的伪代码中总结了这个算法,我们将称之为RecIntMult。
RecIntMult
Input: 两个n位正整数 x 和 y
Output: x和y的乘积
Assumption: n是2的倍数
if n = 1 then // base case compute x · y in one step and return the result else // recursive case a, b := first and second halves of x c, d := first and second halves of y recursively compute ac := a · c, ad := a · d, bc := b · c, a n d bd := b · d compute 10^n · ac + 10^(n/2)· (ad + bc) + bd using grade-school addition and return the result
# !/usr/bin/python3
# -*- coding: utf-8 -*-
# @Time : 2021/2/10 10:39
# @Author : Yu LiXinQian
# @Email : yulixinqian805@gmail.com
# @File : RecursiveSolution.py
# @Project : Algorithm_illuminated
print("Two n-digit positive integers x and y.")
print("Assumption : x,y is the power of 2.")
x = int(input("X : "))
y = int(input("Y : "))
def RecIntMult(x1, y1):
if x1/10 < 1 and y1/10 < 1:
return x1 * y1
else:
n = len(str(x1))
n2 = n//2
a, b = divmod(x1, 10**n2)
c, d = divmod(y1, 10**n2)
ac = RecIntMult(a, c)
ad = RecIntMult(a, d)
bd = RecIntMult(b, d)
bc = RecIntMult(b, c)
return ac*10**n+(ad+bc)*10**n2+bd
print(RecIntMult(x, y))
karatsuba Algorithm
是对上述Recursive的改进。保a * c 和 b * d 的递归计算,另一部分原为( a * d + b * c),我们改为:
老方法进行加总。
Karatsuba
Input: 两个n位正整数 x 和 y
Output: x和y的乘积
Assumption: n是2的倍数
if n = 1 then // base case compute x · y in one step and return the result else // recursive case a, b := first and second halves of x c, d := first and second halves of y compute p := a + b and q := c + d using grade-school addition recursively compute ac := a · c, bd := b · d, a n d pq := p · q compute adbc := pq - ac - bd using grade-school addition compute 10^n· ac + 10^(n/2)· adbc + bd using grade-school addition and return the result
# !/usr/bin/python3
# -*- coding: utf-8 -*-
# @Time : 2021/2/9 23:01
# @Author : Yu LiXinQian
# @Email : yulixinqian805@gmail.com
# @File : KaratsubaSolution.py
# @Project : Algorithm_illuminated
print("Two n-digit positive integers x and y.")
print("Assumption : x,y is the power of 2.")
x = int(input("X : "))
y = int(input("Y : "))
def KaraIntMult(x1, y1):
if x1/10 < 1 and y1/10 < 1:
return x1 * y1
else:
n = len(str(x1))
n2 = n//2
a, b = divmod(x1, 10**n2)
c, d = divmod(y1, 10**n2)
ac = KaraIntMult(a, c)
bd = KaraIntMult(b, d)
p = a+b
q = c+d
pq = p*q
adbc = pq-ac-bd
return ac*10**n+adbc*10**n2+bd
print(KaraIntMult(x, y))
ntMult(a, c)
bd = KaraIntMult(b, d)
p = a+b
q = c+d
pq = pq
adbc = pq-ac-bd
return ac10n+adbc*10n2+bd
print(KaraIntMult(x, y))