This is a follow up on this thread, which main issue was to iterate over all permutations of an array, that is given ["a","b","c"]
, obtain ["bca","acb".. etc]
, using an iterator.
Thanks to Martin R's insights, and also his inputs in another thread, I came up with another possible solution for the 'Sequence-Based enumeration of permutations' problem using iterators. The issue is that I'm not sure I have all permutations although there are good indications they're all there. The algorithm is guaranteed to provide n! permutations at the most, with no duplicates.
The idea behind this approach is the following, say there's an array a=["a","b","c"...]
, of size n
. Listing all permutations can be viewed as picking elements from bags:
■
■
■
■ ■
■ ■ ■
■ ... ■ ■ ■
0 ... n-3 n-2 n-1
So the algorithm takes the initial array, and removes a row, pass it recursively until there is no row left. At this point, if an iterator can be found, all individual permutations can be addressed independently. The iterator is hidden in FactorialSequence
below, where a method next()
allows to move from adjacents points.
public struct FactorialSequence : Sequence, IteratorProtocol {
private var current: [Int]
public init(size:Int) {
self.current = Array(repeating: 0, count: size)
}
public mutating func next() -> [Int]? {
return self.__next();
}
private mutating func __next() -> [Int]? {
var next = current
defer {
current = next;
}
for i in self.current.indices.reversed() {
if next[i] < current.count - i - 1 {
next[i] += 1
return next;
}
next[i] = 0
}
return nil
}
}
func permute(seq:[String],at:[Int]) -> String? {
if seq.count > 0 {
var ss = seq;
let uu = seq[at.first!]
var cc = at;
_ = ss.remove(at: cc.first!)
_ = cc.remove(at: 0);
return uu + (permute(seq:ss,at:cc) ?? "")
}
return nil ;
}
the permute()
function is called passing the iterator (an array) calculated from FactorialSequence:
var fs = FactorialSequence(size: 3)
print("\(fs.current):\(permute(seq:["a","b","c"], at: fs.current)!)")
while let uu = fs.next() {
print("\(uu):\(permute(seq:["a","b","c"], at: uu)!)")
}
and gives (in flatten string format):
[-0.000][-0.000][171] [0, 0, 0]:abc
[0.0009][0.0009][174] [0, 1, 0]:acb
[0.0016][0.0007][174] [1, 0, 0]:bac
[0.0024][0.0008][174] [1, 1, 0]:bca
[0.0032][0.0008][174] [2, 0, 0]:cab
[0.0040][0.0008][174] [2, 1, 0]:cba
Note on 'no duplicates': Since permutations are accessed using an array (the iterator), if two iterators differ by one elements, they point to two different permutations. Although a little thin, I take this as an argument for not having duplicates.
The only question remaining is 'are they all there?'. One could say that there are n! distinct arrays pointing at a given permutation, but I'm not too sure about the validity of that argument, since it comes from a 'drawing'... Pointers welcome.
I didn't thoroughly scrub SO to check if this had been already formulated this way or in a similar way (although links in the original thread use other approaches). Apologies if it did.