如何生成所有可能的组合?
How to generate all possible combinations?
我目前正在尝试从 Strings
的 Array
中创建所有可能组合的 Set
,每个元素只包含一个字母。
Array
本身可以包含相同的字母两次或更多次,它们只应在出现时使用。
Set
稍后应包含从最少 2 个字母到给定 Array
长度的所有组合。
我在 Whosebug 上进行了搜索,但只找到了忽略事实的排列函数,即每个字母只应在出现时使用。
这是我的第一个 Swift 2 项目,所以请原谅我的新手:)
我想要的
var array = ["A", "B", "C","D"]
var combinations: Set<String>
... <MAGIC> ...
print(combinations)
// "AB", "ABC", "ABD", "ABCD", "ABDC", "AC", "ACB", "ACD", "ACBD", "ACDB", and so on ...
我目前的做法
func permuation(arr: Array<String>) {
for (index, elementA) in arr.enumerate() {
//1..2..3..4
var tmpString = elementA
var tmpArray = arr
tmpArray.removeAtIndex(index)
for (index2, elementB) in tmpArray.enumerate() {
// 12..13..14
var tmpString2 = tmpString + elementB
var tmpArray2 = tmpArray
//[3,4]
tmpArray2.removeAtIndex(index2)
results.append(tmpString2)
}
}
}
permuation(array)
print(results)
// "["AB", "AC", "AD", "BA", "BC", "BD", "CA", "CB", "CD", "DA", "DB", "DC"]"
我知道,这在很多方面都是非常错误的,但我受困于这段代码,不知道如何添加递归功能。
试试这个。
一般算法是让一个 fromList
包含您尚未使用的字母,一个 toList
是您目前已建立的字符串。这使用递归来构建所有可能的字符串,并在长度为 2 或更大时将它们添加到集合中:
func permute(fromList: [String], toList: [String] = [String](), var set: Set<String> = Set<String>()) -> Set<String> {
if toList.count >= 2 {
set.insert(toList.joinWithSeparator(""))
}
if !fromList.isEmpty {
for (index, item) in fromList.enumerate() {
var newFrom = fromList
newFrom.removeAtIndex(index)
set = permute(newFrom, toList: toList + [item], set: set)
}
}
return set
}
permute(["A", "B", "C"])
// {"BA", "AC", "ABC", "AB", "BCA", "CB", "BC", "CAB", "ACB", "CA", "CBA", "BAC"}
permute(["A", "A", "B"])
// {"BA", "BAA", "AAB", "AB", "ABA", "AA"}
更快的回答:
正如@MartinR 在他的 post 中指出的那样,由于所有的集合创建和复制,上面的解决方案有点慢。我最初是使用一个 inout
变量来编写它的,但将其更改为功能更强大的接口以使其更易于调用。
这是我最初的(更快的)实现,另外我将它嵌入了一个 permute
中,它只需要一个 [String]
和 returns 一个 Set<String>
。它创建 set
和 toList
数组,然后调用 permute
的内部版本来完成真正的工作:
func permute(list: [String], minStringLen: Int = 2) -> Set<String> {
func permute(fromList: [String], toList: [String], minStringLen: Int, inout set: Set<String>) {
if toList.count >= minStringLen {
set.insert(toList.joinWithSeparator(""))
}
if !fromList.isEmpty {
for (index, item) in fromList.enumerate() {
var newFrom = fromList
newFrom.removeAtIndex(index)
permute(newFrom, toList: toList + [item], minStringLen: minStringLen, set: &set)
}
}
}
var set = Set<String>()
permute(list, toList:[], minStringLen: minStringLen, set: &set)
return set
}
permute(["A", "B", "C"])
// {"BA", "AC", "ABC", "AB", "BCA", "CB", "BC", "CAB", "ACB", "CA", "CBA", "BAC"}
permute(["A", "A", "B"])
// {"BA", "BAA", "AAB", "AB", "ABA", "AA"}
permute(["A", "A", "B"], minStringLen: 1)
// {"BA", "A", "BAA", "AB", "AA", "B", "AAB", "ABA"}
permute(["A", "A", "B"], minStringLen: 3)
// {"ABA", "BAA", "AAB"}
编辑:
我添加了一个 minStringLen
参数(默认值为 2
)而不是对该值进行硬编码。
有关性能比较,请参阅@MartinR 的回答。
Swift 3 和 Swift 4:
func permute(list: [String], minStringLen: Int = 2) -> Set<String> {
func permute(fromList: [String], toList: [String], minStringLen: Int, set: inout Set<String>) {
if toList.count >= minStringLen {
set.insert(toList.joined(separator: ""))
}
if !fromList.isEmpty {
for (index, item) in fromList.enumerated() {
var newFrom = fromList
newFrom.remove(at: index)
permute(fromList: newFrom, toList: toList + [item], minStringLen: minStringLen, set: &set)
}
}
}
var set = Set<String>()
permute(fromList: list, toList:[], minStringLen: minStringLen, set: &set)
return set
}
print(permute(list: ["A", "B", "C"]))
// ["ABC", "CA", "BAC", "ACB", "BA", "CAB", "BC", "CB", "BCA", "CBA", "AB", "AC"]
print(permute(list: ["A", "A", "B"]))
// ["AA", "AAB", "ABA", "AB", "BA", "BAA"]
print(permute(list: ["A", "A", "B"], minStringLen: 1))
// ["AAB", "ABA", "B", "BA", "A", "BAA", "AA", "AB"]
print(permute(list: ["A", "A", "B"], minStringLen: 3))
// ["AAB", "ABA", "BAA"]
这与@vacawama 的回答非常相似,但希望有所不同
足以值得一个单独的答案:)
这里构建了一个所有组合的数组(解释
内嵌评论):
func combinations(array : [String]) -> [String] {
// Recursion terminates here:
if array.count == 0 { return [] }
// Concatenate all combinations that can be built with element #i at the
// first place, where i runs through all array indices:
return array.indices.flatMap { i -> [String] in
// Pick element #i and remove it from the array:
var arrayMinusOne = array
let elem = arrayMinusOne.removeAtIndex(i)
// Prepend element to all combinations of the smaller array:
return [elem] + combinations(arrayMinusOne).map { elem + [=10=] }
}
}
然后就可以过滤至少两个字母的字符串,并且
将其转换为 Set
:
let c = Set(combinations(["A", "B", "C"]).filter { [=11=].characters.count >= 2 })
print(c)
// ["BA", "AC", "ABC", "AB", "BCA", "CB", "BC", "CAB", "ACB", "CA", "CBA", "BAC"]
我做了一个简单的性能对比(在Release模式下编译
在 Macbook Pro 上):
let array = ["A", "B", "C", "D", "E", "F", "G"]
let t1 = NSDate()
let c1 = Set(combinations(array).filter { [=12=].characters.count >= 2 })
let t2 = NSDate()
let c2 = permute(array)
let t3 = NSDate()
print(c1 == c2) // true
print(t2.timeIntervalSinceDate(t1))
print(t3.timeIntervalSinceDate(t2))
结果取决于输入数组的大小,
但是@vacawama 的更新方法是最快的:
# of array This vacawama's vacawama's
elements: method: 1st method: 2nd method:
2 0.00016 0.00005 0.00001
3 0.00043 0.00013 0.00004
4 0.00093 0.00062 0.00014
5 0.00335 0.00838 0.00071
6 0.01756 0.24399 0.00437
7 0.13625 11.90969 0.03692
在您的输出示例中,您也不清楚您真正想要的是什么:
它们的所有组合和排列:
["AB", "BA", "AC", "CA", "AD", "DA", ..., "ABCD", "ABDC", "ACBD", "ACDB", ...]
所有组合:
["AB", "AC", "AD", "BC", "BD", "CD", "ABC", "ABD", ...]
我可以推荐@oisdk 很棒的 Swift 库:SwiftSequence for both of them, it has lots of useful functions. In the Combinations section he even shows an example of its usage with the Power Set,这几乎正是您在案例 1 中寻找的。导入他的库文件后,您可以创建powerSet
在 CollectionType
上的功能如下:
extension CollectionType {
func powerSet() -> LazySequence<FlattenSequence<LazyMapSequence<Self, ComboSeq<Self.Generator.Element>>>>{
var i = 0
return lazy.flatMap{ _ in self.lazyCombos(++i) }
}
}
此方法延迟求值,这意味着它仅在真正需要时才求值。现在您提到您只想拥有至少 2 个元素的组合。使用 filter
方法可以轻松完成此操作:
let combinations = ["A", "B", "C", "D"].powerSet().filter{ [=13=].count >= 2 }
// As an array: [["A", "B"], ["A", "C"], ["A", "D"], ["B", "C"], ["B", "D"], ["C", "D"], ["A", "B", "C"], ["A", "B", "D"], ["A", "C", "D"], ["B", "C", "D"], ["A", "B", "C", "D"]]
对于情况 2,如果您还需要这些排列,您可以这样做:
let combPerms = combinations.flatMap{ [=14=].permutations() }
// As an array: [["A", "B"], ["B", "A"], ["A", "C"], ["C", "A"], ["A", "D"], ["D", "A"], ["B", "C"], ["C", "B"], ["B", "D"], ["D", "B"], ["C", "D"], ["D", "C"], ["A", "B", "C"], ["A", "C", "B"], ["B", "A", "C"], ["B", "C", "A"], ["C", "A", "B"], ["C", "B", "A"], ["A", "B", "D"], ["A", "D", "B"], ["B", "A", "D"], ["B", "D", "A"], ["D", "A", "B"], ["D", "B", "A"], ["A", "C", "D"], ["A", "D", "C"], ["C", "A", "D"], ["C", "D", "A"], ["D", "A", "C"], ["D", "C", "A"], ["B", "C", "D"], ["B", "D", "C"], ["C", "B", "D"], ["C", "D", "B"], ["D", "B", "C"], ["D", "C", "B"], ["A", "B", "C", "D"], ["A", "B", "D", "C"], ["A", "C", "B", "D"], ["A", "C", "D", "B"], ["A", "D", "B", "C"], ["A", "D", "C", "B"], ["B", "A", "C", "D"], ["B", "A", "D", "C"], ["B", "C", "A", "D"], ["B", "C", "D", "A"], ["B", "D", "A", "C"], ["B", "D", "C", "A"], ["C", "A", "B", "D"], ["C", "A", "D", "B"], ["C", "B", "A", "D"], ["C", "B", "D", "A"], ["C", "D", "A", "B"], ["C", "D", "B", "A"], ["D", "A", "B", "C"], ["D", "A", "C", "B"], ["D", "B", "A", "C"], ["D", "B", "C", "A"], ["D", "C", "A", "B"], ["D", "C", "B", "A"]]
您可以将它们转换为 String
的 Set
或 Array
:
let array = Array(combPerms)
let set = Set(combPerms)
但我强烈建议使用惰性版本 ;) 是的,要删除重复项,您可以只使用 Set(["A", "B", "C", "D"])
而不是 ["A", "B", "C", "D"]
你也可以像这样一次性完成案例2:
let x = ["A", "B", "C", "D"]
let result = Set(
x.indices
.flatMap{ x.lazyCombos([=16=] + 1) }
.filter{ [=16=].count >= 2 }
.flatMap{ [=16=].permutations() }
.map{ [=16=].joinWithSeparator("") })
这里有一个 Swift 3 函数,速度要快一些。它基于 Array 类型的扩展,可用于具有任何元素类型的数组。
public func allCombinations(_ array:[String], minLength:Int=2) -> [String]
{
var result:[String] = []
for n in minLength...array.count
{
result = result + array.combinations(of:n).map{ [=10=].joined(separator:"") }
}
return result
}
extension Array
{
public func combinations(of group:Int) -> [[Element]]
{
if group > count { return [] }
if group == count { return [self] }
var result:[[Element]] = []
var comboIndexes = (0..<group).map{[=10=]}
let fullCombo = group - 1
let indexLimit = count - fullCombo
var carry = fullCombo
while carry >= 0
{
if carry == fullCombo
{ result.append(comboIndexes.map{self[[=10=]]}) }
comboIndexes[carry] += 1
if comboIndexes[carry] == carry + indexLimit
{ carry -= 1 ; continue }
while carry < fullCombo
{
carry += 1
comboIndexes[carry] = comboIndexes[carry-1] + 1
}
}
return result
}
}
在我的测试中,它 运行 在 7 个字母上比 vacawama 的第二个版本快大约 40 倍。
[编辑] 后来我意识到这个函数会产生组合(按照 OP 中的要求),而 vacawama 的函数会产生排列。我测试了一个等效的排列算法,它只比 vacawama 的算法快 55%。
extension Array
{
public func permutations(of group:Int? = nil) -> [[Element]]
{
let group = group ?? count
var result : [[Element]] = []
var permutation : [Element] = []
func permute(from baseIndex:Int)
{
if baseIndex == permutation.count - 1
{
result.append(permutation)
return
}
permute(from:baseIndex+1)
for index in baseIndex+1..<permutation.count
{
swap(&permutation[baseIndex],&permutation[index])
permute(from:baseIndex+1)
}
let baseElement = permutation[baseIndex]
permutation.remove(at:baseIndex)
permutation.append(baseElement)
}
var comboIndexes = (0..<group).map{[=11=]}
let fullCombo = group - 1
let indexLimit = count - fullCombo
var carry = fullCombo
while carry >= 0
{
if carry == fullCombo
{
permutation = comboIndexes.map{self[[=11=]]}
permute(from:0)
}
comboIndexes[carry] += 1
if comboIndexes[carry] == carry + indexLimit
{ carry -= 1 ; continue }
while carry < fullCombo
{
carry += 1
comboIndexes[carry] = comboIndexes[carry-1] + 1
}
}
return result
}
}
我目前正在尝试从 Strings
的 Array
中创建所有可能组合的 Set
,每个元素只包含一个字母。
Array
本身可以包含相同的字母两次或更多次,它们只应在出现时使用。
Set
稍后应包含从最少 2 个字母到给定 Array
长度的所有组合。
我在 Whosebug 上进行了搜索,但只找到了忽略事实的排列函数,即每个字母只应在出现时使用。
这是我的第一个 Swift 2 项目,所以请原谅我的新手:)
我想要的
var array = ["A", "B", "C","D"]
var combinations: Set<String>
... <MAGIC> ...
print(combinations)
// "AB", "ABC", "ABD", "ABCD", "ABDC", "AC", "ACB", "ACD", "ACBD", "ACDB", and so on ...
我目前的做法
func permuation(arr: Array<String>) {
for (index, elementA) in arr.enumerate() {
//1..2..3..4
var tmpString = elementA
var tmpArray = arr
tmpArray.removeAtIndex(index)
for (index2, elementB) in tmpArray.enumerate() {
// 12..13..14
var tmpString2 = tmpString + elementB
var tmpArray2 = tmpArray
//[3,4]
tmpArray2.removeAtIndex(index2)
results.append(tmpString2)
}
}
}
permuation(array)
print(results)
// "["AB", "AC", "AD", "BA", "BC", "BD", "CA", "CB", "CD", "DA", "DB", "DC"]"
我知道,这在很多方面都是非常错误的,但我受困于这段代码,不知道如何添加递归功能。
试试这个。
一般算法是让一个 fromList
包含您尚未使用的字母,一个 toList
是您目前已建立的字符串。这使用递归来构建所有可能的字符串,并在长度为 2 或更大时将它们添加到集合中:
func permute(fromList: [String], toList: [String] = [String](), var set: Set<String> = Set<String>()) -> Set<String> {
if toList.count >= 2 {
set.insert(toList.joinWithSeparator(""))
}
if !fromList.isEmpty {
for (index, item) in fromList.enumerate() {
var newFrom = fromList
newFrom.removeAtIndex(index)
set = permute(newFrom, toList: toList + [item], set: set)
}
}
return set
}
permute(["A", "B", "C"])
// {"BA", "AC", "ABC", "AB", "BCA", "CB", "BC", "CAB", "ACB", "CA", "CBA", "BAC"}
permute(["A", "A", "B"])
// {"BA", "BAA", "AAB", "AB", "ABA", "AA"}
更快的回答:
正如@MartinR 在他的 post 中指出的那样,由于所有的集合创建和复制,上面的解决方案有点慢。我最初是使用一个 inout
变量来编写它的,但将其更改为功能更强大的接口以使其更易于调用。
这是我最初的(更快的)实现,另外我将它嵌入了一个 permute
中,它只需要一个 [String]
和 returns 一个 Set<String>
。它创建 set
和 toList
数组,然后调用 permute
的内部版本来完成真正的工作:
func permute(list: [String], minStringLen: Int = 2) -> Set<String> {
func permute(fromList: [String], toList: [String], minStringLen: Int, inout set: Set<String>) {
if toList.count >= minStringLen {
set.insert(toList.joinWithSeparator(""))
}
if !fromList.isEmpty {
for (index, item) in fromList.enumerate() {
var newFrom = fromList
newFrom.removeAtIndex(index)
permute(newFrom, toList: toList + [item], minStringLen: minStringLen, set: &set)
}
}
}
var set = Set<String>()
permute(list, toList:[], minStringLen: minStringLen, set: &set)
return set
}
permute(["A", "B", "C"])
// {"BA", "AC", "ABC", "AB", "BCA", "CB", "BC", "CAB", "ACB", "CA", "CBA", "BAC"}
permute(["A", "A", "B"])
// {"BA", "BAA", "AAB", "AB", "ABA", "AA"}
permute(["A", "A", "B"], minStringLen: 1)
// {"BA", "A", "BAA", "AB", "AA", "B", "AAB", "ABA"}
permute(["A", "A", "B"], minStringLen: 3)
// {"ABA", "BAA", "AAB"}
编辑:
我添加了一个 minStringLen
参数(默认值为 2
)而不是对该值进行硬编码。
有关性能比较,请参阅@MartinR 的回答。
Swift 3 和 Swift 4:
func permute(list: [String], minStringLen: Int = 2) -> Set<String> {
func permute(fromList: [String], toList: [String], minStringLen: Int, set: inout Set<String>) {
if toList.count >= minStringLen {
set.insert(toList.joined(separator: ""))
}
if !fromList.isEmpty {
for (index, item) in fromList.enumerated() {
var newFrom = fromList
newFrom.remove(at: index)
permute(fromList: newFrom, toList: toList + [item], minStringLen: minStringLen, set: &set)
}
}
}
var set = Set<String>()
permute(fromList: list, toList:[], minStringLen: minStringLen, set: &set)
return set
}
print(permute(list: ["A", "B", "C"]))
// ["ABC", "CA", "BAC", "ACB", "BA", "CAB", "BC", "CB", "BCA", "CBA", "AB", "AC"]
print(permute(list: ["A", "A", "B"]))
// ["AA", "AAB", "ABA", "AB", "BA", "BAA"]
print(permute(list: ["A", "A", "B"], minStringLen: 1))
// ["AAB", "ABA", "B", "BA", "A", "BAA", "AA", "AB"]
print(permute(list: ["A", "A", "B"], minStringLen: 3))
// ["AAB", "ABA", "BAA"]
这与@vacawama 的回答非常相似,但希望有所不同 足以值得一个单独的答案:)
这里构建了一个所有组合的数组(解释 内嵌评论):
func combinations(array : [String]) -> [String] {
// Recursion terminates here:
if array.count == 0 { return [] }
// Concatenate all combinations that can be built with element #i at the
// first place, where i runs through all array indices:
return array.indices.flatMap { i -> [String] in
// Pick element #i and remove it from the array:
var arrayMinusOne = array
let elem = arrayMinusOne.removeAtIndex(i)
// Prepend element to all combinations of the smaller array:
return [elem] + combinations(arrayMinusOne).map { elem + [=10=] }
}
}
然后就可以过滤至少两个字母的字符串,并且
将其转换为 Set
:
let c = Set(combinations(["A", "B", "C"]).filter { [=11=].characters.count >= 2 })
print(c)
// ["BA", "AC", "ABC", "AB", "BCA", "CB", "BC", "CAB", "ACB", "CA", "CBA", "BAC"]
我做了一个简单的性能对比(在Release模式下编译 在 Macbook Pro 上):
let array = ["A", "B", "C", "D", "E", "F", "G"]
let t1 = NSDate()
let c1 = Set(combinations(array).filter { [=12=].characters.count >= 2 })
let t2 = NSDate()
let c2 = permute(array)
let t3 = NSDate()
print(c1 == c2) // true
print(t2.timeIntervalSinceDate(t1))
print(t3.timeIntervalSinceDate(t2))
结果取决于输入数组的大小, 但是@vacawama 的更新方法是最快的:
# of array This vacawama's vacawama's elements: method: 1st method: 2nd method: 2 0.00016 0.00005 0.00001 3 0.00043 0.00013 0.00004 4 0.00093 0.00062 0.00014 5 0.00335 0.00838 0.00071 6 0.01756 0.24399 0.00437 7 0.13625 11.90969 0.03692
在您的输出示例中,您也不清楚您真正想要的是什么:
它们的所有组合和排列:
["AB", "BA", "AC", "CA", "AD", "DA", ..., "ABCD", "ABDC", "ACBD", "ACDB", ...]
所有组合:
["AB", "AC", "AD", "BC", "BD", "CD", "ABC", "ABD", ...]
我可以推荐@oisdk 很棒的 Swift 库:SwiftSequence for both of them, it has lots of useful functions. In the Combinations section he even shows an example of its usage with the Power Set,这几乎正是您在案例 1 中寻找的。导入他的库文件后,您可以创建powerSet
在 CollectionType
上的功能如下:
extension CollectionType {
func powerSet() -> LazySequence<FlattenSequence<LazyMapSequence<Self, ComboSeq<Self.Generator.Element>>>>{
var i = 0
return lazy.flatMap{ _ in self.lazyCombos(++i) }
}
}
此方法延迟求值,这意味着它仅在真正需要时才求值。现在您提到您只想拥有至少 2 个元素的组合。使用 filter
方法可以轻松完成此操作:
let combinations = ["A", "B", "C", "D"].powerSet().filter{ [=13=].count >= 2 }
// As an array: [["A", "B"], ["A", "C"], ["A", "D"], ["B", "C"], ["B", "D"], ["C", "D"], ["A", "B", "C"], ["A", "B", "D"], ["A", "C", "D"], ["B", "C", "D"], ["A", "B", "C", "D"]]
对于情况 2,如果您还需要这些排列,您可以这样做:
let combPerms = combinations.flatMap{ [=14=].permutations() }
// As an array: [["A", "B"], ["B", "A"], ["A", "C"], ["C", "A"], ["A", "D"], ["D", "A"], ["B", "C"], ["C", "B"], ["B", "D"], ["D", "B"], ["C", "D"], ["D", "C"], ["A", "B", "C"], ["A", "C", "B"], ["B", "A", "C"], ["B", "C", "A"], ["C", "A", "B"], ["C", "B", "A"], ["A", "B", "D"], ["A", "D", "B"], ["B", "A", "D"], ["B", "D", "A"], ["D", "A", "B"], ["D", "B", "A"], ["A", "C", "D"], ["A", "D", "C"], ["C", "A", "D"], ["C", "D", "A"], ["D", "A", "C"], ["D", "C", "A"], ["B", "C", "D"], ["B", "D", "C"], ["C", "B", "D"], ["C", "D", "B"], ["D", "B", "C"], ["D", "C", "B"], ["A", "B", "C", "D"], ["A", "B", "D", "C"], ["A", "C", "B", "D"], ["A", "C", "D", "B"], ["A", "D", "B", "C"], ["A", "D", "C", "B"], ["B", "A", "C", "D"], ["B", "A", "D", "C"], ["B", "C", "A", "D"], ["B", "C", "D", "A"], ["B", "D", "A", "C"], ["B", "D", "C", "A"], ["C", "A", "B", "D"], ["C", "A", "D", "B"], ["C", "B", "A", "D"], ["C", "B", "D", "A"], ["C", "D", "A", "B"], ["C", "D", "B", "A"], ["D", "A", "B", "C"], ["D", "A", "C", "B"], ["D", "B", "A", "C"], ["D", "B", "C", "A"], ["D", "C", "A", "B"], ["D", "C", "B", "A"]]
您可以将它们转换为 String
的 Set
或 Array
:
let array = Array(combPerms)
let set = Set(combPerms)
但我强烈建议使用惰性版本 ;) 是的,要删除重复项,您可以只使用 Set(["A", "B", "C", "D"])
而不是 ["A", "B", "C", "D"]
你也可以像这样一次性完成案例2:
let x = ["A", "B", "C", "D"]
let result = Set(
x.indices
.flatMap{ x.lazyCombos([=16=] + 1) }
.filter{ [=16=].count >= 2 }
.flatMap{ [=16=].permutations() }
.map{ [=16=].joinWithSeparator("") })
这里有一个 Swift 3 函数,速度要快一些。它基于 Array 类型的扩展,可用于具有任何元素类型的数组。
public func allCombinations(_ array:[String], minLength:Int=2) -> [String]
{
var result:[String] = []
for n in minLength...array.count
{
result = result + array.combinations(of:n).map{ [=10=].joined(separator:"") }
}
return result
}
extension Array
{
public func combinations(of group:Int) -> [[Element]]
{
if group > count { return [] }
if group == count { return [self] }
var result:[[Element]] = []
var comboIndexes = (0..<group).map{[=10=]}
let fullCombo = group - 1
let indexLimit = count - fullCombo
var carry = fullCombo
while carry >= 0
{
if carry == fullCombo
{ result.append(comboIndexes.map{self[[=10=]]}) }
comboIndexes[carry] += 1
if comboIndexes[carry] == carry + indexLimit
{ carry -= 1 ; continue }
while carry < fullCombo
{
carry += 1
comboIndexes[carry] = comboIndexes[carry-1] + 1
}
}
return result
}
}
在我的测试中,它 运行 在 7 个字母上比 vacawama 的第二个版本快大约 40 倍。
[编辑] 后来我意识到这个函数会产生组合(按照 OP 中的要求),而 vacawama 的函数会产生排列。我测试了一个等效的排列算法,它只比 vacawama 的算法快 55%。
extension Array
{
public func permutations(of group:Int? = nil) -> [[Element]]
{
let group = group ?? count
var result : [[Element]] = []
var permutation : [Element] = []
func permute(from baseIndex:Int)
{
if baseIndex == permutation.count - 1
{
result.append(permutation)
return
}
permute(from:baseIndex+1)
for index in baseIndex+1..<permutation.count
{
swap(&permutation[baseIndex],&permutation[index])
permute(from:baseIndex+1)
}
let baseElement = permutation[baseIndex]
permutation.remove(at:baseIndex)
permutation.append(baseElement)
}
var comboIndexes = (0..<group).map{[=11=]}
let fullCombo = group - 1
let indexLimit = count - fullCombo
var carry = fullCombo
while carry >= 0
{
if carry == fullCombo
{
permutation = comboIndexes.map{self[[=11=]]}
permute(from:0)
}
comboIndexes[carry] += 1
if comboIndexes[carry] == carry + indexLimit
{ carry -= 1 ; continue }
while carry < fullCombo
{
carry += 1
comboIndexes[carry] = comboIndexes[carry-1] + 1
}
}
return result
}
}