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.