对于字符串"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)
答案 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)