这是对this thread的跟进,主要问题是迭代数组的所有排列,给定["a","b","c"]
,使用迭代器获得["bca","acb".. etc]
。
由于Martin R的见解以及他在另一个thread中的投入,我为使用迭代器的“基于序列的排列枚举”问题提出了另一种可能的解决方案。问题是,尽管有充分的迹象表明它们都在那里,但我不确定我是否具有所有排列。该算法保证提供n!排列最多,没有重复。
这种方法背后的想法是,假设有一个数组a=["a","b","c"...]
,大小为n
。列出所有排列可以看作是从袋子中挑选元素:
■
■
■
■ ■
■ ■ ■
■ ... ■ ■ ■
0 ... n-3 n-2 n-1
因此,该算法采用初始数组,并删除一行,然后递归传递该行,直到没有剩余行为止。在这一点上,如果可以找到迭代器,则可以单独处理所有单个排列。迭代器隐藏在下面的FactorialSequence
中,其中方法next()
允许从相邻点移动。
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 ;
}
permute()
函数被称为传递通过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)!)")
}
并给出(以扁平化的字符串格式):
[-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
关于“无重复项”的说明:由于使用数组(迭代器)访问置换,因此如果两个迭代器的一个元素不同,则它们指向两个不同的置换。尽管有些薄,但我将其作为没有重复项的理由。
剩下的唯一问题是“它们都在那里吗?”。可以说有n!指向给定排列的不同数组,但是我不太确定该参数的有效性,因为它来自“绘图” ...欢迎指针。
我没有彻底检查SO是否已通过这种方式或类似方式进行了表述(尽管原始线程中的链接使用了其他方法)。抱歉。
答案 0 :(得分:3)
对于给定大小的N
,FactorialSequence
会生成所有数组的序列
[ i.0, i.1, ..., i.(N-1) ]
如此
0 <= i.0 < N, 0 <= i.1 < N-1, ..., 0 <= i.(N-1) < 1
完全是
N * (N-1) * ... * 1 = N!
元素。然后,permute()
函数选择索引为i.0
的元素
从具有N
元素的给定数组中选择,然后从
其余i.1
个元素,依此类推。
是的,这确实会产生数组的所有可能排列。
但是,代码可以简化一点。首先,N-1
not 是否返回初始数组FactorialSequence
,对应于
身份排列。似乎还有单独的[ 0, ..., 0 ]
方法
如果我们将代码更改为
__next()
然后返回所有排列(包括初始标识),并且
public struct FactorialSequence : Sequence, IteratorProtocol {
private var current: [Int]
private var firstIteration = true
public init(size:Int) {
self.current = Array(repeating: 0, count: size)
}
public mutating func next() -> [Int]? {
if firstIteration {
firstIteration = false
return current
}
for i in self.current.indices.reversed() {
if current[i] < current.count - i - 1 {
current[i] += 1
return current;
}
current[i] = 0
}
return nil
}
}
语句不再需要。
defer
函数可以稍微简化一下,并使其通用:
permute()
现在
func permute<E>(array: [E], at: [Int]) -> [E] {
if at.isEmpty { return [] }
var at = at
var array = array
let firstIndex = at.remove(at: 0)
let firstElement = array.remove(at: firstIndex)
return [firstElement] + permute(array: array, at: at)
}
产生预期的输出。
请注意,let array = ["a", "b", "c"]
let fs = FactorialSequence(size: 3)
for p in fs {
print(permute(array: array, at: p).joined())
}
会产生很多中间数组,
因此我假设它的效率不如其他方法
您引用的。
另一种选择是将选择的元素交换 位置,这避免了递归和临时数组:
permute()
(不过,它会以不同的顺序产生排列。)