Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 16 additions & 16 deletions Combinatorics/Combinatorics.playground/Contents.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//: Playground - noun: a place where people can play

/* Calculates n! */
func factorial(n: Int) -> Int {
func factorial(_ n: Int) -> Int {
var n = n
var result = 1
while n > 1 {
Expand All @@ -20,7 +20,7 @@ factorial(20)
Calculates P(n, k), the number of permutations of n distinct symbols
in groups of size k.
*/
func permutations(n: Int, _ k: Int) -> Int {
func permutations(_ n: Int, _ k: Int) -> Int {
var n = n
var answer = n
for _ in 1..<k {
Expand All @@ -41,7 +41,7 @@ permutations(9, 4)
Original algorithm by Niklaus Wirth.
See also Dr.Dobb's Magazine June 1993, Algorithm Alley
*/
func permuteWirth<T>(a: [T], _ n: Int) {
func permuteWirth<T>(_ a: [T], _ n: Int) {
if n == 0 {
print(a) // display the current permutation
} else {
Expand Down Expand Up @@ -75,7 +75,7 @@ permuteWirth(xyz, 2)
Original algorithm by Robert Sedgewick.
See also Dr.Dobb's Magazine June 1993, Algorithm Alley
*/
func permuteSedgewick(a: [Int], _ n: Int, inout _ pos: Int) {
func permuteSedgewick(_ a: [Int], _ n: Int, _ pos: inout Int) {
var a = a
pos += 1
a[n] = pos
Expand Down Expand Up @@ -103,16 +103,16 @@ permuteSedgewick(numbers, 0, &pos)
Calculates C(n, k), or "n-choose-k", i.e. how many different selections
of size k out of a total number of distinct elements (n) you can make.
*/
func combinations(n: Int, _ k: Int) -> Int {
func combinations(_ n: Int, choose k: Int) -> Int {
return permutations(n, k) / factorial(k)
}

combinations(3, 2)
combinations(28, 5)
combinations(3, choose: 2)
combinations(28, choose: 5)

print("\nCombinations:")
for i in 1...20 {
print("\(20)-choose-\(i) = \(combinations(20, i))")
print("\(20)-choose-\(i) = \(combinations(20, choose: i))")
}


Expand All @@ -121,7 +121,7 @@ for i in 1...20 {
Calculates C(n, k), or "n-choose-k", i.e. the number of ways to choose
k things out of n possibilities.
*/
func quickBinomialCoefficient(n: Int, _ k: Int) -> Int {
func quickBinomialCoefficient(_ n: Int, choose k: Int) -> Int {
var result = 1

for i in 0..<k {
Expand All @@ -131,8 +131,8 @@ func quickBinomialCoefficient(n: Int, _ k: Int) -> Int {
return result
}

quickBinomialCoefficient(8, 2)
quickBinomialCoefficient(30, 15)
quickBinomialCoefficient(8, choose: 2)
quickBinomialCoefficient(30, choose: 15)



Expand All @@ -145,7 +145,7 @@ struct Array2D<T> {
init(columns: Int, rows: Int, initialValue: T) {
self.columns = columns
self.rows = rows
array = .init(count: rows*columns, repeatedValue: initialValue)
array = Array(repeating: initialValue, count: rows*columns)
}

subscript(column: Int, row: Int) -> T {
Expand All @@ -163,8 +163,8 @@ struct Array2D<T> {
space for the cached values.
*/

func binomialCoefficient(n: Int, _ k: Int) -> Int {
var bc = Array(count: n + 1, repeatedValue: Array(count: n + 1, repeatedValue: 0))
func binomialCoefficient(_ n: Int, choose k: Int) -> Int {
var bc = Array(repeating: Array(repeating: 0, count: n + 1), count: n + 1)

for i in 0...n {
bc[i][0] = 1
Expand All @@ -182,5 +182,5 @@ func binomialCoefficient(n: Int, _ k: Int) -> Int {
return bc[n][k]
}

binomialCoefficient(30, 15)
binomialCoefficient(66, 33)
binomialCoefficient(30, choose: 15)
binomialCoefficient(66, choose: 33)
6 changes: 6 additions & 0 deletions Combinatorics/Combinatorics.playground/timeline.xctimeline
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Timeline
version = "3.0">
<TimelineItems>
</TimelineItems>
</Timeline>
121 changes: 0 additions & 121 deletions Combinatorics/Combinatorics.swift

This file was deleted.

36 changes: 18 additions & 18 deletions Combinatorics/README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
A *permutation* is a certain arrangement of the objects from a collection. For example, if we have the first five letters from the alphabet, then this is a permutation:

a, b, c, d, e

This is another permutation:

b, e, d, a, c
Expand All @@ -28,7 +28,7 @@ After picking the second letter, there are only three letters left to choose fro
To calculate the factorial in Swift:

```swift
func factorial(n: Int) -> Int {
func factorial(_ n: Int) -> Int {
var n = n
var result = 1
while n > 1 {
Expand Down Expand Up @@ -62,7 +62,7 @@ You could implement this in terms of the `factorial()` function from earlier, bu
Here is an algorithm that can deal with larger numbers:

```swift
func permutations(n: Int, _ k: Int) -> Int {
func permutations(_ n: Int, _ k: Int) -> Int {
var n = n
var answer = n
for _ in 1..<k {
Expand Down Expand Up @@ -98,7 +98,7 @@ So far we've counted how many permutations exist for a given collection, but how
Here's a recursive algorithm by Niklaus Wirth:

```swift
func permuteWirth<T>(a: [T], _ n: Int) {
func permuteWirth<T>(_ a: [T], _ n: Int) {
if n == 0 {
print(a) // display the current permutation
} else {
Expand Down Expand Up @@ -216,7 +216,7 @@ If the above is still not entirely clear, then I suggest you give it a go in the
For fun, here is an alternative algorithm, by Robert Sedgewick:

```swift
func permuteSedgewick(a: [Int], _ n: Int, inout _ pos: Int) {
func permuteSedgewick(_ a: [Int], _ n: Int, _ pos: inout Int) {
var a = a
pos += 1
a[n] = pos
Expand Down Expand Up @@ -282,7 +282,7 @@ The formula for `C(n, k)` is:

As you can see, you can derive it from the formula for `P(n, k)`. There are always more permutations than combinations. You divide the number of permutations by `k!` because a total of `k!` of these permutations give the same combination.

Above I showed that the number of permutations of `k` `l` `m` is 6, but if you pick only two of those letters the number of combinations is 3. If we use the formula we should get the same answer. We want to calculate `C(3, 2)` because we choose 2 letters out of a collection of 3.
Above I showed that the number of permutations of `k` `l` `m` is 6, but if you pick only two of those letters the number of combinations is 3. If we use the formula we should get the same answer. We want to calculate `C(3, 2)` because we choose 2 letters out of a collection of 3.

3 * 2 * 1 6
C(3, 2) = --------- = --- = 3
Expand All @@ -291,15 +291,15 @@ Above I showed that the number of permutations of `k` `l` `m` is 6, but if you p
Here's a simple function to calculate `C(n, k)`:

```swift
func combinations(n: Int, _ k: Int) -> Int {
func combinations(_ n: Int, choose k: Int) -> Int {
return permutations(n, k) / factorial(k)
}
```

Use it like this:

```swift
combinations(28, 5) // prints 98280
combinations(28, choose: 5) // prints 98280
```

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.
Expand All @@ -319,7 +319,7 @@ After the reduction of fractions, we get the following formula:
We can implement this formula as follows:

```swift
func quickBinomialCoefficient(n: Int, _ k: Int) -> Int {
func quickBinomialCoefficient(_ n: Int, choose k: Int) -> Int {
var result = 1
for i in 0..<k {
result *= (n - i)
Expand All @@ -334,8 +334,8 @@ This algorithm can create larger numbers than the previous method. Instead of ca
Here's how you can use this improved algorithm:

```swift
quickBinomialCoefficient(8, 2) // prints 28
quickBinomialCoefficient(30, 15) // prints 155117520
quickBinomialCoefficient(8, choose: 2) // prints 28
quickBinomialCoefficient(30, choose: 15) // prints 155117520
```

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.
Expand All @@ -345,10 +345,10 @@ Here is an algorithm that uses dynamic programming to overcome the need for calc
0: 1
1: 1 1
2: 1 2 1
3: 1 3 3 1
4: 1 4 6 4 1
5: 1 5 10 10 5 1
6: 1 6 15 20 15 6 1
3: 1 3 3 1
4: 1 4 6 4 1
5: 1 5 10 10 5 1
6: 1 6 15 20 15 6 1

Each number in the next row is made up by adding two numbers from the previous row. For example in row 6, the number 15 is made by adding the 5 and 10 from row 5. These numbers are called the binomial coefficients and as it happens they are the same as `C(n, k)`.

Expand All @@ -365,8 +365,8 @@ For example, for row 6:
The following code calculates Pascal's triangle in order to find the `C(n, k)` you're looking for:

```swift
func binomialCoefficient(n: Int, _ k: Int) -> Int {
var bc = Array(count: n + 1, repeatedValue: Array(count: n + 1, repeatedValue: 0))
func binomialCoefficient(_ n: Int, choose k: Int) -> Int {
var bc = Array(repeating: Array(repeating: 0, count: n + 1), count: n + 1)

for i in 0...n {
bc[i][0] = 1
Expand All @@ -390,7 +390,7 @@ The algorithm itself is quite simple: the first loop fills in the 1s at the oute
Now you can calculate `C(66, 33)` without any problems:

```swift
binomialCoefficient(66, 33) // prints a very large number
binomialCoefficient(66, choose: 33) // prints a very large number
```

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!
Expand Down