Swift - 特定长度的子数组
Posted
技术标签:
【中文标题】Swift - 特定长度的子数组【英文标题】:Swift - Subarrays of specific length 【发布时间】:2019-03-19 14:46:11 【问题描述】:我有一个数组,可以说[1, 2, 3, 4]
。我必须检查一个元素或元素的任何组合是否总和为特定数字。
示例
5
、1 + 4 = 5
和 2 + 3 = 5
。
6
、1 + 2 + 3 = 6
和 2 + 4 = 6
可能是创建数组的幂集as in this answer,然后循环遍历它。但这不是一个好主意,因为如果元素的数量(即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 的最大值是多少? 那是“subset sum problem“,通过动态规划更有效地解决,而不是生成所有子集。 @MartinR 没错,这就是我要指出的。但 PO 去了子集方向。 另请注意,生成大小为 0、1、2、3、... N 的所有子集与生成幂集相同。 举个例子:一个 100 元素的数组有 100891344545564193334812497256 个大小为 50 的子数组。 【参考方案1】:这是子集和问题的变体,或更一般地说,Knapsack problem。以下解决方案假设:
初始数组的所有元素都是严格正数, 初始数组可能包含重复元素, 如果无法求和,则输出为空数组。让我们从一个示例开始:让我们创建一个动态表,我们将在其中尝试通过添加来自[1, 2, 3, 4]
的元素来找到获得5
的所有方法:
在此表中,行表示数组的元素,按升序排列,加上0
。列从0
到总和5
。
在每个单元格中,我们都会问自己,是否可以通过添加当前行和前一行的一个或多个标题来获得该列的标题。
解决方案的数量是其中包含true
的单元格的数量。在这种情况下,有两种解决方案:
1)
绿色单元格是true
,因此当前行是解决方案中的最后一个元素。在这种情况下,3
是解决方案的一部分。因此,寻找总和为 5 的子数组的问题变成了寻找总和为5 - 3
的子数组。这是2
。这由紫色 arrow 1
表示:向左走五列,向上走 1 行。
在arrow 2
中,我们寻找可以得到2
部分和的子集。在这种情况下,我们得到两个感谢 2
元素。所以跟着arrow 2
,我们往上走一排,往左边走两排。
使用arrow 3
,我们到达第一列的第一个单元格,对应于5 - 3 - 2
,即0
。
2)
我们可以采取的另一条路径从红细胞开始:
如你所见,[1, 2, 3, 4]
出 5 的问题,变成了一个新的更小的问题,即从[1, 2, 3]
出 1,然后是 [1, 2]
出 1,最后是 `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 的贡献 here。 this video 详细解释了动态表是如何构建的。
【讨论】:
【参考方案2】:这是一种子集和问题。
对于正整数,它可以使用复杂的动态规划来解决O(length * sum)
制作长度为(sum + 1)
的数组A,用零填充,设置A[0] = 1
对于每个源值v
遍历数组A
从A[sum]
到A[v]
,检查A[i-v]
是否非零。如果是,请将A[i]
单元格标记为A[i-v] + 1
(到达此单元格的步数(值))。
如果A[sum]
不为零并且毕竟包含所需步数的组合,则此总和可能由数组元素组成。
如果您还需要跟踪元素,请将它们的值添加到 A[i]
单元格中以检索子集。
【讨论】:
以上是关于Swift - 特定长度的子数组的主要内容,如果未能解决你的问题,请参考以下文章