Skip to content

Commit 7a983d7

Browse files
authored
Merge pull request kodecocodes#211 from rwash8347/master
Karatsuba Multiplication
2 parents 5105cd0 + d0da5de commit 7a983d7

File tree

5 files changed

+241
-0
lines changed

5 files changed

+241
-0
lines changed
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
//: Playground - noun: a place where people can play
2+
3+
import Foundation
4+
5+
precedencegroup ExponentiativePrecedence {
6+
higherThan: MultiplicationPrecedence
7+
lowerThan: BitwiseShiftPrecedence
8+
associativity: left
9+
}
10+
11+
infix operator ^^: ExponentiativePrecedence
12+
func ^^ (radix: Int, power: Int) -> Int {
13+
return Int(pow(Double(radix), Double(power)))
14+
}
15+
16+
// Long Multiplication - O(n^2)
17+
func multiply(_ num1: Int, by num2: Int, base: Int = 10) -> Int {
18+
let num1Array = String(num1).characters.reversed().map{ Int(String($0))! }
19+
let num2Array = String(num2).characters.reversed().map{ Int(String($0))! }
20+
21+
var product = Array(repeating: 0, count: num1Array.count + num2Array.count)
22+
23+
for i in num1Array.indices {
24+
var carry = 0
25+
for j in num2Array.indices {
26+
product[i + j] += carry + num1Array[i] * num2Array[j]
27+
carry = product[i + j] / base
28+
product[i + j] %= base
29+
}
30+
product[i + num2Array.count] += carry
31+
}
32+
33+
return Int(product.reversed().map{ String($0) }.reduce("", +))!
34+
}
35+
36+
// Karatsuba Multiplication - O(n^log2(3))
37+
func karatsuba(_ num1: Int, by num2: Int) -> Int {
38+
let num1Array = String(num1).characters
39+
let num2Array = String(num2).characters
40+
41+
guard num1Array.count > 1 && num2Array.count > 1 else {
42+
return multiply(num1, by: num2)
43+
}
44+
45+
let n = max(num1Array.count, num2Array.count)
46+
let nBy2 = n / 2
47+
48+
let a = num1 / 10^^nBy2
49+
let b = num1 % 10^^nBy2
50+
let c = num2 / 10^^nBy2
51+
let d = num2 % 10^^nBy2
52+
53+
let ac = karatsuba(a, by: c)
54+
let bd = karatsuba(b, by: d)
55+
let adPlusbc = karatsuba(a+b, by: c+d) - ac - bd
56+
57+
let product = ac * 10^^(2 * nBy2) + adPlusbc * 10^^nBy2 + bd
58+
59+
return product
60+
}
61+
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2+
<playground version='5.0' target-platform='ios' executeOnSourceChanges='false'>
3+
<timeline fileName='timeline.xctimeline'/>
4+
</playground>

Karatsuba Multiplication/KaratsubaMultiplication.playground/playground.xcworkspace/contents.xcworkspacedata

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//
2+
// KaratsubaMultiplication.swift
3+
//
4+
//
5+
// Created by Richard Ash on 9/12/16.
6+
//
7+
//
8+
9+
import Foundation
10+
11+
precedencegroup ExponentiativePrecedence {
12+
higherThan: MultiplicationPrecedence
13+
lowerThan: BitwiseShiftPrecedence
14+
associativity: left
15+
}
16+
17+
infix operator ^^: ExponentiativePrecedence
18+
func ^^ (radix: Int, power: Int) -> Int {
19+
return Int(pow(Double(radix), Double(power)))
20+
}
21+
22+
func karatsuba(_ num1: Int, by num2: Int) -> Int {
23+
let num1Array = String(num1).characters
24+
let num2Array = String(num2).characters
25+
26+
guard num1Array.count > 1 && num2Array.count > 1 else {
27+
return num1 * num2
28+
}
29+
30+
let n = max(num1Array.count, num2Array.count)
31+
let nBy2 = n / 2
32+
33+
let a = num1 / 10^^nBy2
34+
let b = num1 % 10^^nBy2
35+
let c = num2 / 10^^nBy2
36+
let d = num2 % 10^^nBy2
37+
38+
let ac = karatsuba(a, by: c)
39+
let bd = karatsuba(b, by: d)
40+
let adPlusbc = karatsuba(a+b, by: c+d) - ac - bd
41+
42+
let product = ac * 10^^(2 * nBy2) + adPlusbc * 10^^nBy2 + bd
43+
44+
return product
45+
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# Karatsuba Multiplication
2+
3+
Goal: To quickly multiply two numbers together
4+
5+
## Long Multiplication
6+
7+
In grade school we learned how to multiply two numbers together via Long Multiplication. Let's try that first!
8+
9+
### Example 1: Multiply 1234 by 5678 using Long Multiplication
10+
11+
5678
12+
*1234
13+
------
14+
22712
15+
17034-
16+
11356--
17+
5678---
18+
--------
19+
7006652
20+
21+
So what's the problem with Long Multiplication? Well remember the first part of our goal. To *quickly* multiply two numbers together. Long Multiplication is slow! (**O(n^2)**)
22+
23+
You can see where the **O(n^2)** comes from in the implementation of Long Multiplication:
24+
25+
```swift
26+
// Long Multiplication
27+
func multiply(_ num1: Int, by num2: Int, base: Int = 10) -> Int {
28+
let num1Array = String(num1).characters.reversed().map{ Int(String($0))! }
29+
let num2Array = String(num2).characters.reversed().map{ Int(String($0))! }
30+
31+
var product = Array(repeating: 0, count: num1Array.count + num2Array.count)
32+
33+
for i in num1Array.indices {
34+
var carry = 0
35+
for j in num2Array.indices {
36+
product[i + j] += carry + num1Array[i] * num2Array[j]
37+
carry = product[i + j] / base
38+
product[i + j] %= base
39+
}
40+
product[i + num2Array.count] += carry
41+
}
42+
43+
return Int(product.reversed().map{ String($0) }.reduce("", +))!
44+
}
45+
```
46+
47+
The double for loop is the culprit! By comparing each of the digits (as is necessary!) we set ourselves up for an **O(n^2)** running time. So Long Multiplication might not be the best algorithm after all. Can we do better?
48+
49+
## Karatsuba Multiplication
50+
51+
The Karatsuba Algorithm was discovered by Anatoly Karatsuba and published in 1962. Karatsuba discovered that you could compute the product of two large numbers using three smaller products and some addition and subtraction.
52+
53+
For two numbers x, y, where m <= n:
54+
55+
x = a*10^m + b
56+
y = c*10^m + d
57+
58+
Now, we can say:
59+
60+
x*y = (a*10^m + b) * (c*10^m + d)
61+
= a*c*10^(2m) + (a*d + b*c)*10^(m) + b*d
62+
63+
This had been know since the 19th century. The problem is that the method requires 4 multiplications (`a*c`, `a*d`, `b*c`, `b*d`). Karatsuba's insight was that you only need three! (`a*c`, `b*d`, `(a+b)*(c+d)`). Now a perfectly valid question right now would be "How is that possible!?!" Here's the math:
64+
65+
(a+b)*(c+d) - a*c - b*c = (a*c + a*d + b*c + b*d) - a*c - b*c
66+
= (a*d + b*c)
67+
68+
Pretty cool, huh?
69+
70+
Here's the full implementation. Note that the recursive algorithm is most efficient at m = n/2.
71+
72+
```swift
73+
// Karatsuba Multiplication
74+
func karatsuba(_ num1: Int, by num2: Int) -> Int {
75+
let num1Array = String(num1).characters
76+
let num2Array = String(num2).characters
77+
78+
guard num1Array.count > 1 && num2Array.count > 1 else {
79+
return num1*num2
80+
}
81+
82+
let n = max(num1Array.count, num2Array.count)
83+
let nBy2 = n / 2
84+
85+
let a = num1 / 10^^nBy2
86+
let b = num1 % 10^^nBy2
87+
let c = num2 / 10^^nBy2
88+
let d = num2 % 10^^nBy2
89+
90+
let ac = karatsuba(a, by: c)
91+
let bd = karatsuba(b, by: d)
92+
let adPlusbc = karatsuba(a+b, by: c+d) - ac - bd
93+
94+
let product = ac * 10^^(2 * nBy2) + adPlusbc * 10^^nBy2 + bd
95+
96+
return product
97+
}
98+
```
99+
100+
What about the running time of this algorithm? Is all this extra work worth it? We can use the Master Theorem to answer this question. This leads us to `T(n) = 3*T(n/2) + c*n + d` where c & d are some constants. It follows (because 3 > 2^1) that the running time is **O(n^log2(3))** which is roughly **O(n^1.56)**. Much better!
101+
102+
### Example 2: Multiply 1234 by 5678 using Karatsuba Multiplication
103+
104+
m = 2
105+
x = 1234 = a*10^2 + b = 12*10^2 + 34
106+
y = 5678 = c*10^2 + d = 56*10^2 + 78
107+
108+
a*c = 672
109+
b*d = 2652
110+
(a*d + b*c) = 2840
111+
112+
x*y = 672*10^4 + 2840*10^2 + 2652
113+
= 6720000 + 284000 + 2652
114+
= 7006652
115+
116+
## Resources
117+
118+
[Wikipedia] (https://en.wikipedia.org/wiki/Karatsuba_algorithm)
119+
120+
[WolframMathWorld] (http://mathworld.wolfram.com/KaratsubaMultiplication.html)
121+
122+
[Master Theorem] (https://en.wikipedia.org/wiki/Master_theorem)
123+
124+
*Written for Swift Algorithm Club by Richard Ash*

0 commit comments

Comments
 (0)