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
}