计算Swift中字符串的所有排列

时间:2016-01-23 20:27:42

标签: ios swift permutation

对于字符串"ABC",下面的代码段会计算6个总排列中的5个。我的策略是在每个索引可能索引处插入每个字符。但是函数永远不会被"CBA"作为可能的排列。我错过了什么?

var permutationArray:[String] = [];
let string: String = "ABC"

func permute(input: String) -> Array<String>
{
    var permutations: Array<String> = []

    /*   Convert the input string into characters      */
    var inputArray: Array<String>
    inputArray = input.characters.map { String($0) }
    print(inputArray)

    /*   For each character in the input string...     */
    for var i = 0; i < inputArray.count; i++
    {

        /*       Insert it at every index              */
        let characterInArray: String = inputArray[i]
        var inputArrayCopy: Array<String> = []
        for var y = 0; y < inputArray.count; y++
        {

            inputArrayCopy = inputArray
            inputArrayCopy.removeAtIndex(i)
            inputArrayCopy.insert(characterInArray, atIndex:y)

            let joiner = ""
            let permutation = inputArrayCopy.joinWithSeparator(joiner)
            if !permutations.contains(permutation) {
                permutations.insert(permutation, atIndex: 0)
            }
        }
    }

    return permutations
}

var permutations = permute(string)
print(permutations)

9 个答案:

答案 0 :(得分:20)

虽然Stefan和Matt对使用Heap的算法提出了一个很好的观点,但我认为你有一个重要的问题,为什么你的代码不起作用以及如何调试它。

在这种情况下,算法完全不正确,发现它的最佳方法是使用铅笔和纸张IMO。你正在做的是挑选每个元素,将其从数组中删除,然后将其注入每个可能的位置。您的代码执行您要求它执行的操作。但是不可能以这种方式进入“CBA”。您一次只移动一个元素,但“CBA”有两个元素乱序。如果你扩展到ABCD,你会发现更多的遗漏排列(它只产生24个中的10个)。

虽然Heap的算法非常有效,但更重要的是它遍历整个数组并交换每个可能的对,而不是仅仅通过数组移动单个元素。您选择的任何算法都必须具有该属性。

只是把我的帽子扔进戒指,我会用这种方式扩展Matt的实现:

// Takes any collection of T and returns an array of permutations
func permute<C: Collection>(items: C) -> [[C.Iterator.Element]] {
    var scratch = Array(items) // This is a scratch space for Heap's algorithm
    var result: [[C.Iterator.Element]] = [] // This will accumulate our result

    // Heap's algorithm
    func heap(_ n: Int) {
        if n == 1 {
            result.append(scratch)
            return
        }

        for i in 0..<n-1 {
            heap(n-1)
            let j = (n%2 == 1) ? 0 : i
            scratch.swapAt(j, n-1)
        }
        heap(n-1)
    }

    // Let's get started
    heap(scratch.count)

    // And return the result we built up
    return result
}

// We could make an overload for permute() that handles strings if we wanted
// But it's often good to be very explicit with strings, and make it clear
// that we're permuting Characters rather than something else.

let string = "ABCD"
let perms = permute(string.characters) // Get the character permutations
let permStrings = perms.map() { String($0) } // Turn them back into strings
print(permStrings) // output if you like

答案 1 :(得分:12)

这是Swift中Heap(Sedgewick's?)算法的表达式。它是有效的,因为数组是通过引用传递而不是通过值传递(当然这意味着你必须准备好让数组被篡改)。通过使用内置的swapAt(_:_:)函数:

有效地表达交换
func permutations(_ n:Int, _ a: inout Array<Character>) {
    if n == 1 {print(a); return}
    for i in 0..<n-1 {
        permutations(n-1,&a)
        a.swapAt(n-1, (n%2 == 1) ? 0 : i)
    }
    permutations(n-1,&a)
}

我们试一试:

var arr = Array("ABC".characters)
permutations(arr.count,&arr)

输出:

["A", "B", "C"]
["B", "A", "C"]
["C", "A", "B"]
["A", "C", "B"]
["B", "C", "A"]
["C", "B", "A"]

如果您想要对每个排列做什么不仅仅是打印它,请用其他内容替换print(a)。例如,您可以将每个排列附加到数组,将字符数组合成一个字符串,无论如何。

答案 2 :(得分:3)

func generate(n: Int, var a: [String]){
    if n == 1 {
        print(a.joinWithSeparator(""))
    } else {
        for var i = 0; i < n - 1; i++ {
            generate(n - 1, a: a)
            if n % 2 == 0 {
                let temp = a[i]
                a[i] = a[n-1]
                a[n-1] = temp
            }
            else {
                let temp = a[0]
                a[0] = a[n-1]
                a[n-1] = temp
            }
        }
        generate(n - 1, a: a)
    }
}


func testExample() {
    var str = "123456"
    var strArray = str.characters.map { String($0) }
    generate(str.characters.count, a: strArray)
}

不要重新发明轮子。这是Heap's algorithm的简单端口。

答案 3 :(得分:3)

这是我的解决方案。

import Foundation

class Permutator {
    class func permutation(_ str: String) -> Set<String> {
         var set = Set<String>()

         permutation(str, prefix: "", set: &set)

         return set
    }

    private class func permutation(_ str: String, prefix: String, set: inout Set<String>) {
        if str.characters.count == 0 {
            set.insert(prefix)
        }

        for i in str.characters.indices {

            let left    = str.substring(to: i)
            let right   = str.substring(from: str.index(after: i))

            let rem = left + right
            permutation(rem, prefix: prefix + String(str[i]), set: &set)
        }
    }
}


let startTime = Date()
let permutation = Permutator.permutation("abcdefgh")


print("\(permutation) \n")
print("COMBINAISON: \(permutation.count)")
print("TIME: \(String(format: "%.3f", Date().timeIntervalSince(startTime)))s")

您可以将其复制/粘贴到文件中,然后使用命令行swift binary执行它。

对于7个所有唯一字符的排列,此算法大约需要0.06秒才能执行。

答案 4 :(得分:1)

Swift编码挑战中也提出了一种非常简单的方法。

 func permutation(string: String, current: String = "") {
        let length = string.characters.count
        let strArray = Array(string.characters)
        if (length == 0) {
            // there's nothing left to re-arrange; print the result
            print(current)
            print("******")
        } else {
            print(current)
            // loop through every character
            for i in 0 ..< length {
                // get the letters before me
                let left = String(strArray[0 ..< i])
                // get the letters after me
                let right = String(strArray[i+1 ..< length])
                // put those two together and carry on
                permutation(string: left + right, current: current +
                    String(strArray[i]))
            }

        }
    }

答案 5 :(得分:1)

Apple今天发布了位于以下位置的算法包:

https://github.com/apple/swift-algorithms

此软件包包括一个permutations函数,其功能如下:

let string = "abc"
string.permutations()
/*
["a", "b", "c"]
["a", "c", "b"]
["b", "a", "c"]
["b", "c", "a"]
["c", "a", "b"]
["c", "b", "a"]
*/

答案 6 :(得分:0)

我一直在寻找解决同一问题的方法,但是我想要一个适用于通用数据类型的解决方案,因此我通过查看scala代码(http://vkostyukov.ru/posts/combinatorial-algorithms-in-scala/)来编写了一个

https://gist.github.com/psksvp/8fb5c6fbfd6a2207e95638db95f55ae1

 /**
  translate from Scala by psksvp@gmail.com
 http://vkostyukov.ru/posts/combinatorial-algorithms-in-scala/
 */
extension Array
{
  func combinations(_ n: Int) -> [[Element]]
  {
    guard self.count > 0 else {return [[Element]]()}
    guard n <= self.count else {return [[Element]]()}
    
    if 1 == n
    {
      return self.map {[$0]}
    }
    else
    {
      let head = self.first! // at this point head should be valid
      let tail = Array(self.dropFirst())
      let car = tail.combinations(n - 1).map {[head] + $0} // build first comb
      let cdr = tail.combinations(n)  // do the rest
      return car + cdr
    }
  }
  
  func variations(_ n:Int) -> [[Element]]
  {
    func mixone(_ i: Int, _ x: Element, _ ll: [Element]) -> [Element]
    {
      return Array( ll[0 ..< i] + ([x] + ll[i ..< ll.count]) )
    }
    
    func foldone(_ x: Element, _ ll: [Element]) -> [[Element]]
    {
      let r:[[Element]] = (1 ... ll.count).reduce([[x] + ll])
                          {
                            a, i in
                            [mixone(i, x, ll)] + a
                          }
      return r
    }
    
    func mixmany(_ x: Element, _ ll: [[Element]]) -> [[Element]]
    {
      guard ll.count > 0 else {return [[Element]]()}
      let head = ll.first!
      let tail = Array<Array<Element>>(ll.dropFirst())
      return foldone(x, head) + mixmany(x, tail)
    }
    
    guard self.count > 0 else {return [[Element]]()}
    guard n <= self.count else {return [[Element]]()}
    
    if 1 == n
    {
      return self.map {[$0]}
    }
    else
    {
      let head = self.first! // at this point head should be valid
      let tail = Array(self.dropFirst())
      return mixmany(head, tail.variations(n - 1)) + tail.variations(n)
    }
  }
  
  var permutations: [[Element]]
  {
    variations(self.count)
  }
}


print([1, 2, 3, 4].combinations(2))
print([1, 2, 3, 4].variations(2))
print([1, 2, 3, 4].permutations)

print(Array("ABCD").permutations)

答案 7 :(得分:0)

100% 工作测试

 func permute(strInput:String,l:Int,r:Int){
        var inputCharacter = Array(strInput)
       
        if ( l==r){
            print(strInput)
        }else{
            for var i in l..<r{
          // Swapping done
                inputCharacter.swapAt(l, i);
          // Recursion called
                permute(strInput: String(inputCharacter), l: l+1, r: r);
          //backtrack
                inputCharacter.swapAt(l, i);
            }
        }
    }

这样你就可以调用方法了:

 permute(strInput: "ABC", l: 0, r: 3)

输出:

ABC
ACB
BAC
BCA
CBA
CAB

答案 8 :(得分:-1)

您可以使用此框架的功能来计算重复和不重复的排列和组合。您可以调查源代码并与您自己的源代码进行比较。

https://github.com/amirrezaeghtedari/AECounting

该库根据词典顺序计算结果。例如,5个项目中3个项目的排列结果如下:

let result = Permutation.permute(n: 5, r: 3)

//result
//[
// [1, 2, 3],
// [1, 2, 4],
// [1, 2, 5],
//  ...,
// 5, 4, 3]
//].

您可以轻松地将问题项分配给结果数组中的1到n个数字。

如果遇到问题,您应该致电:

let result = Permutation.permute(n: 3, r: 3)