Skip to content

Commit 0871bc9

Browse files
committed
Text tweaks
1 parent 57e43f9 commit 0871bc9

File tree

4 files changed

+71
-65
lines changed

4 files changed

+71
-65
lines changed

Combinatorics/Combinatorics.playground/Contents.swift

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,24 @@ for i in 1...20 {
117117

118118

119119

120+
/*
121+
Calculates C(n, k), or "n-choose-k", i.e. the number of ways to choose
122+
k things out of n possibilities.
123+
*/
124+
func quickBinomialCoefficient(n: Int, _ k: Int) -> Int {
125+
var result = 1
126+
127+
for i in 0..<k {
128+
result *= (n - i)
129+
result /= (i + 1)
130+
}
131+
return result
132+
}
133+
134+
quickBinomialCoefficient(8, 2)
135+
quickBinomialCoefficient(30, 15)
136+
137+
120138

121139
/* Supporting code because Swift doesn't have a built-in 2D array. */
122140
struct Array2D<T> {
@@ -166,21 +184,3 @@ func binomialCoefficient(n: Int, _ k: Int) -> Int {
166184
binomialCoefficient(30, 15)
167185
binomialCoefficient(66, 33)
168186

169-
170-
171-
/*
172-
Calculates C(n, k), or "n-choose-k", i.e. the number of ways to choose
173-
k things out of n possibilities.
174-
*/
175-
func quickBinomialCoefficient(n: Int, _ k: Int) -> Int {
176-
var result = 1
177-
178-
for i in 0..<k {
179-
result *= (n - i)
180-
result /= (i + 1)
181-
}
182-
return result
183-
}
184-
185-
quickBinomialCoefficient(30, 15)
186-
quickBinomialCoefficient(8, 2)

Combinatorics/Combinatorics.playground/timeline.xctimeline

Lines changed: 0 additions & 6 deletions
This file was deleted.

Combinatorics/Combinatorics.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,20 @@ func combinations(n: Int, _ k: Int) -> Int {
7878
return permutations(n, k) / factorial(k)
7979
}
8080

81+
/*
82+
Calculates C(n, k), or "n-choose-k", i.e. the number of ways to choose
83+
k things out of n possibilities.
84+
*/
85+
func quickBinomialCoefficient(n: Int, _ k: Int) -> Int {
86+
var result = 1
87+
88+
for i in 0..<k {
89+
result *= (n - i)
90+
result /= (i + 1)
91+
}
92+
return result
93+
}
94+
8195
/*
8296
Calculates C(n, k), or "n-choose-k", i.e. the number of ways to choose
8397
k things out of n possibilities.

Combinatorics/README.markdown

Lines changed: 39 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,43 @@ combinations(28, 5) // prints 98280
304304

305305
Because this uses the `permutations()` and `factorial()` functions under the hood, you're still limited by how large these numbers can get. For example, `combinations(30, 15)` is "only" `155,117,520` but because the intermediate results don't fit into a 64-bit integer, you can't calculate it with the given function.
306306

307-
Here is an algorithm that uses dynamic programming to overcome the need for calculating factorials. It is based on Pascal's triangle:
307+
There's a faster approach to calculate `C(n, k)` in **O(k)** time and **O(1)** extra space. The idea behind it is that the formula for `C(n, k)` is:
308+
309+
n! n * (n - 1) * ... * 1
310+
C(n, k) = ------------- = ------------------------------------------
311+
(n - k)! * k! (n - k) * (n - k - 1) * ... * 1 * k!
312+
313+
After the reduction of fractions, we get the following formula:
314+
315+
n * (n - 1) * ... * (n - k + 1) (n - 0) * (n - 1) * ... * (n - k + 1)
316+
C(n, k) = --------------------------------------- = -----------------------------------------
317+
k! (0 + 1) * (1 + 1) * ... * (k - 1 + 1)
318+
319+
We can implement this formula as follows:
320+
321+
```swift
322+
func quickBinomialCoefficient(n: Int, _ k: Int) -> Int {
323+
var result = 1
324+
for i in 0..<k {
325+
result *= (n - i)
326+
result /= (i + 1)
327+
}
328+
return result
329+
}
330+
```
331+
332+
This algorithm can create larger numbers than the previous method. Instead of calculating the entire numerator (a potentially huge number) and then dividing it by the factorial (also a very large number), here we already divide in each step. That causes the temporary results to grow much less quickly.
333+
334+
Here's how you can use this improved algorithm:
335+
336+
```swift
337+
quickBinomialCoefficient(8, 2) // prints 28
338+
quickBinomialCoefficient(30, 15) // prints 155117520
339+
```
340+
341+
This new method is quite fast but you're still limited in how large the numbers can get. You can calculate `C(30, 15)` without any problems, but something like `C(66, 33)` will still cause integer overflow in the numerator.
342+
343+
Here is an algorithm that uses dynamic programming to overcome the need for calculating factorials and doing divisions. It is based on Pascal's triangle:
308344

309345
0: 1
310346
1: 1 1
@@ -351,54 +387,16 @@ func binomialCoefficient(n: Int, _ k: Int) -> Int {
351387

352388
This uses [Array2D](../Array2D/) as helper code because Swift doesn't have a built-in two-dimensional array. The algorithm itself is quite simple: the first loop fills in the 1s at the outer edges of the triangle. The other loops calculate each number in the triangle by adding up the two numbers from the previous row.
353389

354-
Now you can calculate `C(30, 15)` without any problems:
390+
Now you can calculate `C(66, 33)` without any problems:
355391

356392
```swift
357-
binomialCoefficient(30, 15) // prints 155117520
358393
binomialCoefficient(66, 33) // prints a very large number
359394
```
360395

361-
There's a faster approach to calculate `C(n, k)` in `O(k)` time and `O(1)` extra space.
362-
363-
The idea behind it is that the formula for `C(n, k)` is:
364-
365-
n! n * (n - 1) * ... * 1
366-
C(n, k) = ------------- = ------------------------------------------
367-
(n - k)! * k! (n - k) * (n - k - 1) * ... * 1 * k!
368-
369-
After the reduction of fractions, we get the following formula:
370-
371-
n * (n - 1) * ... * (n - k + 1) (n - 0) * (n - 1) * ... * (n - k + 1)
372-
C(n, k) = --------------------------------------- = -----------------------------------------
373-
k! (0 + 1) * (1 + 1) * ... * (k - 1 + 1)
374-
375-
So the following code calculates the `C(n, k)` you're looking for:
376-
377-
```swift
378-
func quickBinomialCoefficient(n: Int, _ k: Int) -> Int {
379-
var result = 1
380-
381-
for i in 0..<k {
382-
result *= (n - i)
383-
result /= (i + 1)
384-
}
385-
return result
386-
}
387-
```
388-
389-
As there're the divisors for numbers from `1...k` among `k` consecutive numbers, what is exactly we've got in the numerator of our fast formula every time we devide `result` by `(i + 1)` we won't have a real number.
390-
391-
Here's how you can use it:
392-
393-
```swift
394-
quickBinomialCoefficient(30, 15) // prints 155117520
395-
quickBinomialCoefficient(8, 2) // prints 28
396-
```
397-
398396
You may wonder what the point is in calculating these permutations and combinations, but many algorithm problems are really combinatorics problems in disguise. Often you may need to look at all possible combinations of your data to see which one gives the right solution. If that means you need to search through `n!` potential solutions, you may want to consider a different approach -- as you've seen, these numbers become huge very quickly!
399397

400398
## References
401399

402400
Wirth's and Sedgewick's permutation algorithms and the code for counting permutations and combinations are based on the Algorithm Alley column from Dr.Dobb's Magazine, June 1993. The dynamic programming binomial coefficient algorithm is from The Algorithm Design Manual by Skiena.
403401

404-
*Written for Swift Algorithm Club by Matthijs Hollemans*
402+
*Written for Swift Algorithm Club by Matthijs Hollemans and [Kanstantsin Linou](https://github.com/nuts23)*

0 commit comments

Comments
 (0)