代码之家  ›  专栏  ›  技术社区  ›  stevenpcurtis

子集和Swift

  •  1
  • stevenpcurtis  · 技术社区  · 7 年前

    我想使用动态规划来获得给定数组中所有子集的总和。

    subsets([1,2,3]) = 24
    Since the subsets are {1}{2}{3}{1,2}{2,3}{1,3}{1,2,3}
    

    我试图计算子集([1,2]),它=6 我下面的实现得到5

    func subsets(_ arr: [Int]) -> Int {
        var mem = Array(repeating: 0, count: arr.count)
        return subsetsRecursive(arr, 0, &mem)
    }
    
    // correct result, but too slow so requires memorization
    func subsetsRecursive(_ arr: [Int], _ ptr: Int, _ memo : inout [Int]) -> Int {
        if (ptr > arr.count - 1) {return 0}
        if memo[ptr] != 0 {return memo[ptr]}
        // either take the next number, or don't
        let res = (
            subsetsRecursive(arr, ptr + 1, &memo) + arr[ptr]
                +
            subsetsRecursive(arr, ptr + 1, &memo)
        )
        memo[ptr] = res
        return res
    }
    

    为了得到正确的答案,需要纠正什么?这里的方法很重要,它不是家庭作业,而是我关于动态规划的自学(我可以找到其他方法,但想了解这种方法或者为什么不可能)。

    2 回复  |  直到 7 年前
        1
  •  1
  •   Martin R    7 年前

    您的方法的问题是递归步骤没有考虑 子集当前元素 arr[ptr] 可以合并。例如 subsets([1,2]) 计算子集{1,2}和{2}的和,但忽略子集{1}。

    动态规划方法的一个可能的解决方案是不仅要记住和,还要记住 计数 在从某个位置开始的所有子集中:

    func subsets(_ arr: [Int]) -> Int {
        var mem = Array(repeating: (sum: 0, count: 0), count: arr.count)
        return subsetsRecursive(arr, 0, &mem).sum
    }
    
    func subsetsRecursive(_ arr: [Int], _ ptr: Int, _ memo : inout [(sum: Int, count: Int)]) -> (sum: Int, count: Int) {
        if (ptr > arr.count - 1) { return (sum: 0, count: 1) }
        if memo[ptr].sum != 0 { return memo[ptr] }
        let res = subsetsRecursive(arr, ptr + 1, &memo)
        memo[ptr] = (2 * res.sum + arr[ptr] * res.count, 2 * res.count)
        return memo[ptr]
    }
    

    print(subsets([1, 2])) // 6
    print(subsets([1, 2, 3])) // 24
    

    这可以进一步简化,但希望能回答您当前的问题。


    一个迭代的解决方案是

    func subsetsum(_ arr: [Int]) -> Int {
        var sum = 0
        var count = 1
        for elem in arr {
            sum = 2 * sum + count * elem
            count = 2 * count
        }
        return sum
    }
    

    可以简明扼要地写为

    func subsetsum(_ arr: [Int]) -> Int {
        return arr.reduce((sum: 0, count: 1)) {
            (2 * $0.sum + $0.count * $1, 2 * $0.count )
        }.sum
    }
    

    n-1 第二

    func subsetsum(_ arr: [Int]) -> Int {
        return arr.reduce(0, +) * (1 << (arr.count - 1))
    }
    
        2
  •  1
  •   Leo Dabus    7 年前

    您可以首先获取所有子集元素,将它们连接起来,然后使用reduce对元素求和:

    extension RangeReplaceableCollection  {
        public var subSets: [SubSequence] {
            return isEmpty ? [SubSequence()] : dropFirst().subSets.lazy.flatMap { [$0, prefix(1) + $0] }
        }
    }
    

    let subSets = [1,2,3].subSets // [[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]
    

    let sum = subSets.joined().reduce(0,+)   // 24
    

    或

    let sum = subSets.reduce(0) { $1.reduce($0,+) }
    

    RangePlaceableCollection 还一个 SubSequence 您还可以将其用于StringProtocol类型,如 String s和 Substring s:

    let characters = Array("123")
    let charactersSubSets = characters.subSets // [[], ["1"], ["2"], ["1", "2"], ["3"], ["1", "3"], ["2", "3"], ["1", "2", "3"]]
    let stringSubSets = "123".subSets // ["", "1", "2", "12", "3", "13", "23", "123"]
    let substringSubSets = "1234".dropLast().subSets // ["", "1", "2", "12", "3", "13", "23", "123"]