Calculate all permutations of a string in Swift

后端 未结 8 1313
余生分开走
余生分开走 2020-12-03 03:28

For the string \"ABC\" the code snippet below calculates 5 of the 6 total permutations. My strategy was to insert each character at each index possible index. B

相关标签:
8条回答
  • 2020-12-03 03:58

    Apple today released an Algorithms package available at:

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

    This package includes a permutations function that works like so:

    let string = "abc"
    string.permutations()
    /*
    ["a", "b", "c"]
    ["a", "c", "b"]
    ["b", "a", "c"]
    ["b", "c", "a"]
    ["c", "a", "b"]
    ["c", "b", "a"]
    */
    
    0 讨论(0)
  • 2020-12-03 03:58

    You can use the functions of this framework to calculate permutations and combinations both with repetition and without repetition. You can investigate the source code and compare with your own.

    https://github.com/amirrezaeghtedari/AECounting

    This library calculates the results based on lexicographic order. For example the result of permutation 3 items out of 5 items are same as below:

    let result = Permutation.permute(n: 5, r: 3)
    
    //result
    //[
    // [1, 2, 3],
    // [1, 2, 4],
    // [1, 2, 5],
    //  ...,
    // 5, 4, 3]
    //].
    

    You can easily assign your problem items to 1 to n numbers in the result array.

    In case of your problem, you should call:

    let result = Permutation.permute(n: 3, r: 3)
    
    0 讨论(0)
  • 2020-12-03 03:59
    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)
    }
    

    Don't reinvent the wheel. Here's a simple port of Heap's algorithm.

    0 讨论(0)
  • 2020-12-03 04:02

    Here's an expression of Heap's (Sedgewick's?) algorithm in Swift. It is efficient because the array is passed by reference instead of being passed by value (though of course this means you must be prepared to have the array tampered with). Swapping is efficiently expressed through the use of the built-in swapAt(_:_:) function:

    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)
    }
    

    Let's try it:

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

    Output:

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

    If what you wanted to do with each permutation was not merely to print it, replace print(a) with something else. For example, you could append each permutation to an array, combine the array of characters into a string, whatever.

    0 讨论(0)
  • 2020-12-03 04:03

    I was searching to solve the same problem, but I wanted a solution that worked with Generic data type, so I wrote one by looking at a scala code (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)
    
    0 讨论(0)
  • 2020-12-03 04:13

    While Stefan and Matt make a good point about using Heap's algorithm, I think you have an important question about why your code doesn't work and how you would debug that.

    In this case, the algorithm is simply incorrect, and the best way to discover that is with pencil and paper IMO. What you are doing is picking each element, removing it from the array, and then injecting it into each possible location. Your code does what you have asked it to do. But it's not possible to get to "CBA" that way. You're only moving one element at a time, but "CBA" has two elements out of order. If you expanded to ABCD, you'd find many more missing permutations (it only generates 10 of the 24).

    While Heap's algorithm is nicely efficient, the deeper point is that it walks through the entire array and swaps every possible pair, rather than just moving a single element through the array. Any algorithm you choose must have that property.

    And just to throw my hat into the ring, I'd expand on Matt's implementation this way:

    // 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
    
    0 讨论(0)
提交回复
热议问题