Heap's algorithm is an algorithm to generate all permutations of a given array. It
... generates each permutation from the previous one by interchanging a single pair of elements; the other n−2 elements are not disturbed.
Here is my attempt to implement this algorithm in Swift 4 as a Sequence
, so that all permutations of an array can be enumerated with for .. in
loops and related techniques:
/* * A sequence of all permutations of a given array. Based on the * non-recursive version of Heap's algorithm as described in * https://en.wikipedia.org/wiki/Heap%27s_algorithm */ struct HeapPermutationSequence<Element>: Sequence, IteratorProtocol { private var current: [Element] private var c: [Int] private var i = 0 private var firstIteration = true init(elements: [Element]) { self.current = elements self.c = Array(repeating: 0, count: elements.count) } mutating func next() -> [Element]? { if firstIteration { firstIteration = false } else { while i < c.count { if c[i] < i { if i % 2 == 0 { current.swapAt(0, i) } else { current.swapAt(c[i], i) } c[i] += 1 i = 0 break } else { c[i] = 0 i += 1 } } } return i < c.count ? current : nil } }
Example usage:
for p in HeapPermutationSequence(elements: [1, 2, 3]) { print(p, terminator: ", ") } print() // Output: // [1, 2, 3], [2, 1, 3], [3, 1, 2], [1, 3, 2], [2, 3, 1], [3, 2, 1],
Several techniques from @Hamish's answer Sequence-based enumeration of permutations in lexicographic order could be applied here as well to improve the performance:
- Avoid the cost of copy-on-write by not mutating the
current
array after it is returned as the next element. - Have a single exit point in the
next()
method.
Benchmark:
let N = 10 var count = 0 let start = Date() for _ in HeapPermutationSequence(elements: Array(1...N)) { count += 1 } let end = Date() print(count, end.timeIntervalSince(start))
which takes about 0.03 seconds (on a 1.2 GHz Intel Core m5 MacBook, compiled in Release mode).
All feedback is welcome, such as (but of course not restricted to):
- Performance improvements.
- Better variable names (
c
andi
are simply copied from the Wikipedia pseudo-code). - Simplifying the code, or making it better readable/understandable.