基于序列的排列枚举的变体

时间:2018-09-01 07:02:53

标签: arrays swift algorithm permutation

这是对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是否已通过这种方式或类似方式进行了表述(尽管原始线程中的链接使用了其他方法)。抱歉。

1 个答案:

答案 0 :(得分:3)

对于给定大小的NFactorialSequence会生成所有数组的序列

[ 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()

(不过,它会以不同的顺序产生排列。)