Swift - Подмассивы определенной длины

У меня есть массив, скажем, [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]]

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

каково максимальное значение n?

hasan 15.10.2018 10:18

Это «Проблема суммы подмножества», который более эффективно решается с помощью динамического программирования, вместо генерации всех подмножеств.

Martin R 15.10.2018 10:29

@MartinR Именно на это я и собирался указать. но заказ на поставку пошел в направлении подмножества.

hasan 15.10.2018 10:31

Отметим также, что создание всех подмножеств размера 0, 1, 2, 3, ... N является тем же как генерирующий набор мощности.

Martin R 15.10.2018 10:32

@hasan n может иметь любое значение. Если подход к подмножествам не подходит, я открыт для лучшего подхода.

Adeel Miraj 15.10.2018 10:37

@Adeel, вы можете проверить вики-ссылку, добавленную MartinR

hasan 15.10.2018 10:38

@MartinR Я не собираюсь создавать все подмножества. Я буду создавать подмножества, если не найду сумму, равную заданному числу.

Adeel Miraj 15.10.2018 10:38

Просто в качестве примера: массив из 100 элементов имеет 100891344545564193334812497256 подмассивов размером 50.

Martin R 15.10.2018 10:38

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

Adeel Miraj 15.10.2018 10:41

Не могли бы вы добавить дополнительные пояснения: 1) Все ли элементы строго положительные? 2) Уникальны ли элементы массива? 3) Если искомая сумма равна нулю, будет ли ответом пустой массив?

ielyamani 15.10.2018 22:20

1) все элементы положительные. 2) элементы массива могут повторяться. 3) в случае нуля ответом может быть пустой массив.

Adeel Miraj 15.10.2018 22:25

@ Carpsen90, как упомянул Мартин, подход с использованием субмассивов также не будет эффективным, поскольку размер массива увеличивается. Сумма подмножества - это то, что нужно. Мне просто трудно реализовать.

Adeel Miraj 15.10.2018 22:30

@Adeel Да я знаю, это классический geeksforgeeks.org/perfect-sum-problem-print-subsets-given-su‌ m

ielyamani 15.10.2018 22:31
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
13
900
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

Для положительных целых чисел это может быть решено с помощью динамического программирования со сложностью 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], чтобы получить подмножество.

Ответ принят как подходящий

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

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

Начнем с примера: создадим динамический стол, в котором мы попытаемся найти все способы получить 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)                  //[]

Это решение было вдохновлено вкладом Сумит Гошздесь. Подробное объяснение того, как строится динамическая таблица, можно найти в это видео.

Другие вопросы по теме