Apple Swift-генерировать комбинации с повторением

Я пытаюсь создать вложенный массив, содержащий все комбинации с повторением на языке программирования Swift от Apple.

подробное объяснение комбинаций с повторением можно найти в нижней части этой страницы: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
}

в основном у меня, похоже, возникают проблемы с объявлениями типов различных переменных, и погода они изменчивы или неизменяемы.

Я пробовал быть более явным и менее явный с объявлениями типа, но все, что мне удалось достичь, - это немного разные сообщения об ошибках.

Я был бы очень признателен, если бы кто-нибудь объяснил, где я ошибаюсь и почему?

большое спасибо :-)

4 ответов


вы можете избавиться от 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 + }
    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 +  }
    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)
}

теперь массиве фрагментов передаются рекурсивные вызовы, чтобы избежать создание множества временных массивов.

пример:

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

ваш пример дает сочетания с повторением. Для записи я написал неповторяющуюся комбинацию в Swift. Я основал его на версии JavaScript здесь:http://rosettacode.org/wiki/Combinations#JavaScript

Я надеюсь, что это поможет другим, и если кто-то может видеть улучшение, пожалуйста, сделайте.. обратите внимание, что это моя первая попытка Swift и надеялся на более аккуратный способ сделать Swift эквивалент JavaScript slice.

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)

за ОП запрос, вот версия, которая удаляет пользовательскую процедуру нарезки массива в пользу функциональности в стандартной библиотеке

// 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 { [] }
    }

    var result : [[T]] = []

    let rest = Array(source.suffixFrom(1))
    let sub_combos = combinations(rest, takenBy: takenBy - 1)
    result += sub_combos.map { [source[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]]

основная ошибка, которую я делал, заключалась в использовании 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 mutable по умолчанию?


Обновлено @richgordonuk ответ для Swift 4 который обеспечивает неповторяющуюся комбинацию:

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 { [] }
    }

    var result : [[T]] = []

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