Swift - Подмассивы определенной длины - PullRequest
0 голосов
/ 15 октября 2018

У меня есть массив, скажем, [1, 2, 3, 4].Я должен проверить, суммирует ли элемент или любая комбинация элементов конкретное число.

Примеры

  1. 5, 1 + 4 = 5 и 2 + 3 = 5.
  2. 6, 1 + 2 + 3 = 6 и 2 + 4 = 6

На пути может быть создание набора мощности для массива , как в этом ответе, и цикл повторяется по нему.Но это не очень хорошая идея, потому что, если количество элементов, например, n, увеличивается, набор мощности станет большим объемом памяти.В этом отношении лучшим способом было бы создать подмножества / подмассивы определенной длины и перебирать их одну за другой.

Допустим, k - это длина подмассива, тогда

  • k = 2 должен дать мне [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]
  • k = 3 должен дать мне [[1, 2, 3], [1, 2, 4], [2, 3, 4]]

Теперь вопрос заключается в том, как бы я пошел о создании подмассивов / подмножествконкретная длина, как указано выше?

Ответы [ 2 ]

0 голосов
/ 16 октября 2018

Это вариант задачи о подмножестве сумм , или, в более общем случае, задачи о ранце .Следующее решение предполагает, что:

  • Все элементы исходного массива строго положительны,
  • Исходный массив может содержать повторяющиеся элементы,
  • Если сумма может 'После этого мы получим пустой массив.

Начнем с примера: давайте создадим динамическую таблицу 1016 *, в которой мы попытаемся найти все способы получения5 путем добавления элементов из [1, 2, 3, 4]:

dynamic table

В этой таблице строки представляют элементы массива в порядке возрастания,плюс 0.Столбцы переходят от 0 к сумме 5.

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

Количество решений - это количество ячеек, содержащих true.В этом случае два решения:

1)

3_2

Зеленая ячейка - true,поэтому текущая строка является последним элементом решения.В этом случае 3 является частью решения.Таким образом, проблема поиска подмассива, сумма которого равна 5, становится нахождением подмассива, сумма которого равна 5 - 3.Что 2.Это обозначено фиолетовым arrow 1: идите пять столбцов влево и 1 ряд вверх.

В arrow 2 мы ищем подмножество, которое позволило получить частичную сумму 2.В этом случае мы получаем два благодаря элементу 2.Таким образом, следуя arrow 2, мы идем на одну строку вверх, а две влево.

С помощью arrow 3 мы достигаем первой ячейки в первом столбце, соответствующей 5 - 3 - 2, что составляет 0.

2)

Другой путь, который мы могли бы выбрать, начинается с красной ячейки:

4_1

Как видите, проблема получения 5 из [1, 2, 3, 4] становится новой меньшей проблемой: 1 из [1, 2, 3], затем 1 из [1, 2] и, наконец, 1 из ` 1.


Давайте создадим и заполним динамическую таблицу:

var dynamicTable: [[Bool]] =
    Array(repeating: Array(repeating: false, count: sum + 1),
          count: array.count + 1)

//All of the elements of the first column are true
//since we can always make a zero sum out of not elements
for i in 0...array.count {
    dynamicTable[i][0] = true
}

for row in 1...array.count {
    for column in 1...sum {
        if column < array[row - 1] {
            dynamicTable[row][column] = dynamicTable[row - 1][column]
        } else {
            if dynamicTable[row - 1][column] {
                dynamicTable[row][column] = true
            } else {
                dynamicTable[row][column] = dynamicTable[row - 1][column - array[row - 1]]
            }
        }
    }
}

Найдем все пути, ведущие к сумме:

var solutions = [[Int]]()

func getSubArraysWithTheSum(arr: [Int], row: Int, currentSum: Int, currentSolution: [Int]) {

    //The following block will be executed when
    //we reach the first cell in the first column
    if row == 0,
        currentSum == 0
    {
        solutions.append(currentSolution)
        //notice the return to exit the scope
        return
    }

    //The following block will be executed if
    //the current cell is NOT used to reach the sum
    if dynamicTable[row - 1][currentSum]
    {
        getSubArraysWithTheSum(arr: arr,
                               row: row - 1,
                               currentSum: currentSum,
                               currentSolution: currentSolution)
    }

    //The following block will be executed if
    //the current cell IS used to reach the sum
    if currentSum >= arr[row - 1],
        dynamicTable[row - 1][currentSum - arr[row - 1]]
    {
        getSubArraysWithTheSum(arr: arr,
                               row: row - 1,
                               currentSum: currentSum - arr[row - 1],
                               currentSolution: currentSolution + [arr[row - 1]])
    }
}

Вся функция выглядит следующим образом:

func getSubArrays(from array: [Int], withSum sum: Int) -> [[Int]] {

    guard array.allSatisfy({ $0 > 0 }) else {
        fatalError("All the elements of the array must be strictly positive")
    }

    guard array.count > 0, sum > 0 else {
        return []
    }

    var solutions = [[Int]]()
    var dynamicTable: [[Bool]] =
        Array(repeating: Array(repeating: false, count: sum + 1),
              count: array.count + 1)

    //All of the elements of the first column are true
    //since we can always make a zero sum out of not elements
    for i in 0...array.count {
        dynamicTable[i][0] = true
    }

    for row in 1...array.count {
        for column in 1...sum {
            if column < array[row - 1] {
                dynamicTable[row][column] = dynamicTable[row - 1][column]
            } else {
                if dynamicTable[row - 1][column] {
                    dynamicTable[row][column] = true
                } else {
                    dynamicTable[row][column] = dynamicTable[row - 1][column - array[row - 1]]
                }
            }
        }
    }

    func getSubArraysWithTheSum(arr: [Int], row: Int, currentSum: Int, currentSolution: [Int]) {

        //The following block will be executed when
        //we reach the first cell in the first column
        if row == 0,
            currentSum == 0
        {
            solutions.append(currentSolution)
            return
        }

        //The following block will be executed if
        //the current cell is NOT used to reach the sum
        if dynamicTable[row - 1][currentSum]
        {
            getSubArraysWithTheSum(arr: arr,
                                   row: row - 1,
                                   currentSum: currentSum,
                                   currentSolution: currentSolution)
        }

        //The following block will be executed if
        //the current cell IS used to reach the sum
        if currentSum >= arr[row - 1],
            dynamicTable[row - 1][currentSum - arr[row - 1]]
        {
            getSubArraysWithTheSum(arr: arr,
                                   row: row - 1,
                                   currentSum: currentSum - arr[row - 1],
                                   currentSolution: currentSolution + [arr[row - 1]])
        }
    }

    getSubArraysWithTheSum(arr: array, row: array.count , currentSum: sum, currentSolution: [])

    return solutions
}

Вот несколько тестов:

getSubArrays(from: [3, 1, 4, 2], withSum: 5)        //[[3, 2], [4, 1]]
getSubArrays(from: [1, 2, 2, 4], withSum: 3)        //[[2, 1], [2, 1]]
getSubArrays(from: [7, 3, 4, 5, 6, 1], withSum: 9)  //[[5, 3, 1], [5, 4], [6, 3]]
getSubArrays(from: [3], withSum: 3)                 //[[3]]
getSubArrays(from: [5], withSum: 10)                //[]
getSubArrays(from: [1, 2], withSum: 0)              //[]
getSubArrays(from: [], withSum: 4)                  //[]

Это решение было вдохновлено Sumit Ghosh * Вклад 1099 * здесь .Подробное объяснение того, как создается динамическая таблица, можно найти в этом видео .

0 голосов
/ 15 октября 2018

Это своего рода проблема с суммой подмножеств.

Для натуральных чисел ее можно решить с помощью динамического программирования со сложностью O(length * sum)

Сделать массив A длиной (sum + 1), заполненный нулями, установите A[0] = 1

Для каждого исходного значения v массив перемещений A от A[sum] до A[v], проверяя, если A[i-v] не ноль.Если да, пометьте A[i] ячейку A[i-v] + 1 (число шагов (значений) для достижения этой ячейки).

Если A[sum] не равен нулю и содержит комбинацию с необходимым количеством шагов, в конце концов, этосумма может состоять из элементов массива.

Если вам необходимо отслеживать также элементы, добавьте их значения в ячейки A[i], чтобы получить подмножество.

...