Combinatorics in Swift
import Combinatorics
for chars in Permutation(of:"swift") {
print(String(chars))
}The following are random-accessible iterators with each element obtained via [_:Int]. Bounds are checked and if they are out it fatalError()s.
let p = Permutation(0, 1, 2, 3)
p.count // 24
p[0] // [0, 1, 2, 3]
p[p.count - 1] // [3, 2, 1, 0]They all support initializer of forms below.
Permutation(seed:[Element], size:Int=default)
Permutation(of:Sequence, size:Int=default)
Permutation(_ source:Element…)The first one is the canonical initializer. size specifies the size of array that the iterator returns, which defaults to seed.count. The second form is for convenience in which case any Sequence is converted to [Element].
Returns an iterator that returns the permuted array.
var p = Permutation(of:"abcd")
p.count // 24
p[p.count-1] // ["d","c","b","a"]
p.map { $0 } // [["a","b","c","d"]...["d","c","b","a"]]
p = Permutation(of:"abcd", size:2)
p.count // 12
p[p.count-1] // ["d", "c"]
p.map { $0 } // [["a","b"] ... ["d","c"]]Returns an iterator that returns the permuted array but arrays with same elements are treated as the same, regardless of the order. Therefore you should not ommit size or you get only one result.
var c = Combination(of:"abcd")
c.count // 1
c[c.count-1] // ["a","b","c","d"]
c.map { $0 } // [["a","b","c","d"]]
c = Combination(of:"abcd", size:2)
c.count // 6
c[c.count-1] // ["c","d"]
c.map { $0 } // [["a","b"],["a","c"],["a","d"],["b","c"], ["b","d"], ["c","d"]]Returns an iterator that returns the corresponding "digits".
var d = BaseN(of:0...3)
d.count // 4 ** 4 == 256
d[d.count-1] // [3,3,3,3]
d.map { $0 } // [[0,0,0,0]...[3,3,3,3]]
d = BaseN(of:0...3, size:2)
d.count // 16
d[d.count-1] // [3,3]
d.map { $0 } // [[0,0]...[3,3]]Returns an iterator that returns the element of the power set for each iteration. size is fixed to seed.count where seed is the source sequence.
let s = PowerSet(of:0...3)
s.count // 2 ** 4 == 16
s.map { $0 } // [[],[0],[1],[0,1]...[0,1,2,3]]Returns an iterator that returns the element of the cartesian product for each iteration.
let suits = "♠️♦️❤️♣️"
let ranks = 1..13
let cp = CartesianProduct(suits, ranks)
cp.count // 52
cp.map { $0 } //[("♠️",1)...("♣️",13)]Unlike other iterators CartesianProduct takes two Collections and returns their Cartesian product in tuples. The type of their .Element do not have to match.
The iterator itself is also a collection so you can build multidimensional Cartesian products by succesively applying multiplicands.
let cp = CartesianProduct("01", "abc")
let cpcp = CartesianProduct(cp, "ATCG")
cp.count // 24
cpcp.map{ $0 } // [(("0","a"),"A")...(("1","c"),"G")]As you see CartesianProduct returns a tuple. This is mathmatically correct but harder to work with. But in Swift (T,T) is a different type from (T,T,T) so you cannot write a function that returns tuples of different lengths.
To mitigate this, Combinatorics offers ProductSet. The type of all elements must be identical but you get an array instead of tuple.
let ps = ProductSet([0,1],[2,4,6],[3,6,9,12],[4,8,12,16,20])
ps.count // 2 * 3 * 4 * 5 == 120
ps.map{ $0 } // [[0, 2, 3, 4] ... [1, 6, 12, 20]]This module also comes with followings arithmetic functions that are bundled in Combinatorics as static functions.
// T:SignedInteger
Combinatorics.factorial<T>(_ n:T)->T
Combinatorics.permutation<T>(_ n:T, _ k:T)->T
Combinatorics.combination<T>(_ n:T, _ k:T)->TAs you see they are generically defined so you can use not only Int but also BigInt where available.
Under the hood, iterators above are defined as follows:
public typealias Permutation = CombinatoricsIndex<Int>.Permutation
public typealias Combination = CombinatoricsIndex<Int>.Combination
// …
public typealias ProductSet = CombinatoricsIndex<Int>.ProductSetWhy? Because Int is often big enough for combinatorics. Fortunately Swift allows you to generically define subscript its index does not have to be Int. See BigCombinatorics to see how to use BigInt indices.
$ git clone https://github.com/dankogai/swift-combinatorics.git
$ cd swift-combinatorics # the following assumes your $PWD is here
$ swift buildSimply
$ scripts/run-repl.shor
$ swift build && swift -I.build/debug -L.build/debug -lCombinatorics
and in your repl,
1> import Combinatorics
2> Permutation(of:"swift").map{ String($0) }
$R0: [String] = 120 values {
[0] = "swift"
[1] = "switf"
[2] = "swfit"
// ...
[119] = "tfiws"
}Xcode project is deliberately excluded from the repository because it should be generated via swift package generate-xcodeproj . For convenience, you can
$ scripts/prep-xcodeAnd the Workspace opens up for you with Playground on top. The playground is written as a manual.
Unfortunately Swift Package Manager does not support iOS. To make matters worse Swift Playgrounds does not support modules. But don't worry. This module is so compact all you need is copy Combinatorics.swift.
In case of Swift Playgrounds just copy it under Sources folder. If you are too lazy just run:
$ scripts/ios-prep.shand iOS/Combinatorics.playground is all set. You do not have to import Combinatorics therein.
Add the following to the dependencies section:
.package(
url: "https://github.com/dankogai/swift-combinatorics.git", from: "0.0.1"
)and the following to the .target argument:
.target(
name: "YourSwiftyPackage",
dependencies: ["Combinatorics"])Now all you have to do is:
import Combinatoricsin your code. Enjoy!
Swift 4.1 or better, OS X or Linux to build.
