kiaraRobles
kiaraRobles

Reputation: 607

Calculate all permutations of a string in Swift

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. But the function never gets "CBA" as a possible permutation. What am I missing?

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)

Upvotes: 18

Views: 18192

Answers (10)

david_adler
david_adler

Reputation: 10902

For those looking to calculate all permutations of an array:

func permutations<T>(_ arr: [T]) -> [[T]] {
    if arr.count < 2 {
        return [arr]
    }
    var ret: [[T]] = []
    let rest = Array(arr[1...])
    for p in permutations(rest) {
        for i in 0...p.count {
            ret.append(Array(p[0..<i]) + [arr[0]] + Array(p[i...]))
        }
    }
    return ret
}

🚨 Update: just use array.permuations() as noted by @Caleb

Upvotes: -1

Mr.Javed Multani
Mr.Javed Multani

Reputation: 13236

100% working tested

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

This way you can call method:

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

Output:

ABC
ACB
BAC
BCA
CBA
CAB

Upvotes: 0

Ezekiel
Ezekiel

Reputation: 2515

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"]
*/

Upvotes: 6

Justin
Justin

Reputation: 2046

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)

Upvotes: -1

psksvp
psksvp

Reputation: 139

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 [email protected]
 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)

Upvotes: 0

Rizwan Mehboob
Rizwan Mehboob

Reputation: 1373

A very straightforward approach as also suggested in Swift coding challenges.

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

        }
    }

Upvotes: 5

matt
matt

Reputation: 534885

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.

Upvotes: 24

Rob Napier
Rob Napier

Reputation: 299265

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

Upvotes: 29

Steven
Steven

Reputation: 1247

Here is my solution.

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

You can copy/paste it in a file and execute it with the command line swift binary.

For a permutation of 7 all unique characters, this algorithm take around 0,06 second to execute.

Upvotes: 3

Stefan Kendall
Stefan Kendall

Reputation: 67802

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.

Upvotes: 3

Related Questions