Apple Swift - 重复生成组合

时间:2014-08-06 14:04:26

标签: arrays function swift combinations

我试图在Apple的Swift编程语言中生成一个包含所有重复组合的嵌套数组。

可以在本页底部附近找到重复组合的详细说明:http://www.mathsisfun.com/combinatorics/combinations-permutations.html

简言之;顺序没关系,我们可以重复

n =我们选择的一组事物

r =我们选择的事物数量

我想创建一个函数,该函数将生成一个嵌套数组,其中包含对n和r的任何(小)值重复的所有组合。

如果有n = 3件事情可供选择,我们选择r = 2。

n = [0, 1, 2]
r = 2

函数combos(n: [0, 1, 2], r: 2)的结果将是:

result = [
  [0, 0],
  [0, 1],  
  [0, 2],
  [1, 1],
  [1, 2],
  [2, 2]
]

// we don't need [1, 0], [2, 0] etc. because "order does not matter"

这里有许多编程语言的例子:http://rosettacode.org/wiki/Combinations_with_repetitions

这是PHP的例子。它是最简单的之一并返回一个数组,这就是我想要的:

function combos($arr, $k) {
    if ($k == 0) {
        return array(array());
    }

    if (count($arr) == 0) {
        return array();
    }

    $head = $arr[0];

    $combos = array();
    $subcombos = combos($arr, $k-1);
    foreach ($subcombos as $subcombo) {
        array_unshift($subcombo, $head);
        $combos[] = $subcombo;
    }
    array_shift($arr);
    $combos = array_merge($combos, combos($arr, $k));
    return $combos;
}

到目前为止,我已将此功能移植到Swift:

func combos(var array: [Int], k: Int) -> AnyObject { // -> Array<Array<Int>> {
    if k == 0 {
        return [[]]
    }

    if array.isEmpty {
        return []
    }

    let head = array[0]

    var combos = [[]]
    var subcombos: [Array<Int>] = combos(array, k-1)    // error: '(@Ivalue [Int], $T5) -> $T6' is not identical to '[NSArray]'
    for subcombo in subcombos {
        var sub = subcombo
        sub.insert(head, atIndex: 0)
        combos.append(sub)
    }
    array.removeAtIndex(0)
    combos += combos(array, k)    // error: '(@Ivalue [Int], Int) -> $T5' is not identical to '[NSArray]'

    return combos
}

大多数情况下,我似乎遇到了各种变量的类型声明以及天气这些都是可变的或不可变的问题。

我尝试使用类型声明更明确,更不明确,但我设法实现的只是略有不同的错误消息。

如果有人能解释我出错的原因以及原因,我将非常感激。

非常感谢: - )

5 个答案:

答案 0 :(得分:10)

您的示例给出了重复的组合。为了记录,我在Swift中写了一个非重复的组合。我基于这里的JavaScript版本:http://rosettacode.org/wiki/Combinations#JavaScript

我希望它可以帮助其他人,如果有人能看到改进,请做..请注意这是我第一次尝试使用Swift,希望有一种更简洁的方式来做Swift相当于JavaScript切片。

func sliceArray(var arr: Array<Int>, x1: Int, x2: Int) -> Array<Int> {
    var tt: Array<Int> = []
    for var ii = x1; ii <= x2; ++ii {
        tt.append(arr[ii])
    }
    return tt
}

func combinations(var arr: Array<Int>, k: Int) -> Array<Array<Int>> {
    var i: Int
    var subI : Int

    var ret: Array<Array<Int>> = []
    var sub: Array<Array<Int>> = []
    var next: Array<Int> = []
    for var i = 0; i < arr.count; ++i {
        if(k == 1){
            ret.append([arr[i]])
        }else {
            sub = combinations(sliceArray(arr, i + 1, arr.count - 1), k - 1)
            for var subI = 0; subI < sub.count; ++subI {
                next = sub[subI]
                next.insert(arr[i], atIndex: 0)
                ret.append(next)
            }
        }

    }
    return ret
}


var myCombinations = combinations([1,2,3,4],2)

根据OP的请求,这是一个删除自定义数组切片例程以支持标准库中的功能的版本

// Calculate the unique combinations of elements in an array
// taken some number at a time when no element is allowed to repeat
func combinations<T>(source: [T], takenBy : Int) -> [[T]] {
    if(source.count == takenBy) {
        return [source]
    }

    if(source.isEmpty) {
        return []
    }

    if(takenBy == 0) {
        return []
    }

    if(takenBy == 1) {
        return source.map { [$0] }
    }

    var result : [[T]] = []

    let rest = Array(source.suffixFrom(1))
    let sub_combos = combinations(rest, takenBy: takenBy - 1)
    result += sub_combos.map { [source[0]] + $0 }

    result += combinations(rest, takenBy: takenBy)

    return result
}

var myCombinations = combinations([1,2,3,4], takenBy: 2)
// myCombinations = [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]

答案 1 :(得分:9)

您可以通过将循环写为

来摆脱var sub = subcombo
for subcombo in subcombos {
    ret.append([head] + subcombo)
}

使用map()函数可以进一步简化:

func combos<T>(var array: Array<T>, k: Int) -> Array<Array<T>> {
    if k == 0 {
        return [[]]
    }

    if array.isEmpty {
        return []
    }

    let head = [array[0]]
    let subcombos = combos(array, k: k - 1)
    var ret = subcombos.map {head + $0}
    array.removeAtIndex(0)
    ret += combos(array, k: k)

    return ret
}

Swift 4的更新:

func combos<T>(elements: ArraySlice<T>, k: Int) -> [[T]] {
    if k == 0 {
        return [[]]
    }

    guard let first = elements.first else {
        return []
    }

    let head = [first]
    let subcombos = combos(elements: elements, k: k - 1)
    var ret = subcombos.map { head + $0 }
    ret += combos(elements: elements.dropFirst(), k: k)

    return ret
}

func combos<T>(elements: Array<T>, k: Int) -> [[T]] {
    return combos(elements: ArraySlice(elements), k: k)
}

现在将数组 slices 传递给递归调用以避免 创建了许多临时数组。

示例:

print(combos(elements: [1, 2, 3], k: 2))
// [[1, 1], [1, 2], [1, 3], [2, 2], [2, 3], [3, 3]]

答案 2 :(得分:2)

更新了 Swift 4 的@richgordonuk答案,该答案提供了非重复的组合:

func combinations<T>(source: [T], takenBy : Int) -> [[T]] {
    if(source.count == takenBy) {
        return [source]
    }

    if(source.isEmpty) {
        return []
    }

    if(takenBy == 0) {
        return []
    }

    if(takenBy == 1) {
        return source.map { [$0] }
    }

    var result : [[T]] = []

    let rest = Array(source.suffix(from: 1))
    let subCombos = combinations(source: rest, takenBy: takenBy - 1)
    result += subCombos.map { [source[0]] + $0 }
    result += combinations(source: rest, takenBy: takenBy)
    return result
}

答案 3 :(得分:1)

按照现有的答案扩展RangeReplaceableCollection以支持字符串:

extension RangeReplaceableCollection {
    func combinations(of n: Int) -> [SubSequence] {
        guard n > 0 else { return [.init()] }
        guard let first = first else { return [] }
        return combinations(of: n - 1).map { CollectionOfOne(first) + $0 } + dropFirst().combinations(of: n)
    }
    func uniqueCombinations(of n: Int) -> [SubSequence] {
        guard n > 0 else { return [.init()] }
        guard let first = first else { return [] }
        return dropFirst().uniqueCombinations(of: n - 1).map { CollectionOfOne(first) + $0 } + dropFirst().uniqueCombinations(of: n)
    }
}

[1, 2, 3, 4, 5, 6].uniqueCombinations(of: 2)  // [[1, 2], [1, 3], [1, 4], [1, 5], [1, 6], [2, 3], [2, 4], [2, 5], [2, 6], [3, 4], [3, 5], [3, 6], [4, 5], [4, 6], [5, 6]]

"abcdef".uniqueCombinations(of: 3) // ["abc", "abd", "abe", "abf", "acd", "ace", "acf", "ade", "adf", "aef", "bcd", "bce", "bcf", "bde", "bdf", "bef", "cde", "cdf", "cef", "def"]

答案 4 :(得分:0)

我犯的主要错误是使用与我的函数相同的var:

combos += combos(array, k)

这就是为什么我在这一行以及调用我的函数的另一行上看到错误的原因。

在确定其他问题更容易解决之后:)

如果它能帮助任何人,我的工作职能是:

func combos<T>(var array: Array<T>, k: Int) -> Array<Array<T>> {
    if k == 0 {
        return [[]]
    }

    if array.isEmpty {
        return []
    }

    let head = array[0]

    var ret: Array<Array<T>> = []
    var subcombos = combos(array, k - 1)
    for subcombo in subcombos {
        var sub = subcombo
        sub.insert(head, atIndex: 0)
        ret.append(sub)
    }
    array.removeAtIndex(0)
    ret += combos(array, k)

    return ret
}

如果有人能改善它,我会很高兴

例如,任何人都可以解释如何摆脱行var sub = subcombo。即默认情况下如何使subcombo变为可变?