【问题标题】:How to find a random combination from given numbers that add up to another number in Swift?如何从给定的数字中找到一个随机组合,这些组合在 Swift 中加起来是另一个数字?
【发布时间】:2019-02-17 04:19:19
【问题描述】:

给定一组随机的正整数A,找出一个整数的随机组合,加起来等于N

例如。

A = [4, 2, 10, 8, 13, 1, ...]  
N = 18  
The possibilities are [10, 8], [4, 13, 1], etc.

集合A 也可以是任意长度,并且只能容纳正整数。

N 可以是任何正整数。

我只需要一种数字组合,而不是全部。我也想随机选择,所以如果我得到相同的集合,我不会每次都得到相同的答案,我正在寻找可以在 Swift 中以编程方式执行此操作的最有效方法。

【问题讨论】:

  • 您希望A 有多大?例如,它会超过 100 个吗?
  • 当您说“我正在寻找最有效的方式”时,您是在说时间效率、空间效率还是两者兼而有之?
  • 对源数据有限制吗? A中的所有元素都会小于或等于N吗? A中有多少元素?它们会是真正随机的,还是它们的值会有某种模式或权重?
  • 这似乎是一个足够复杂的问题,证明给定的解决方案是“最有效”的解决方案可能很困难。 Leo 的解决方案肯定会奏效,但我怀疑它是否是最有效的解决方案。
  • Set A 中的值是否都是唯一的? (它们在您的示例数据中唯一的。)

标签: ios swift algorithm


【解决方案1】:

您首先需要创建一个方法来查找您的原始收藏的所有可能子集,正如我在此处发布的 Subset sum Swift。然后你只需要检查任何子集总和是否等于 n:

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

let a = [4, 2, 10, 8, 13, 1]
let n = 18
let matches = a.subSets.lazy.filter { $0.reduce(0,+) == n }
print("Matches:", Array(matches.map{Array($0)}))  // Matches: [[10, 8], [4, 13, 1]]

您还可以使用非递归方法,正如您在我在这里发布的另一篇帖子 Find all combination of string array in swift 中看到的那样。请注意,我这样做的第二种方法是不将空集视为子集:

extension RangeReplaceableCollection {
    var subSetsNotRecursive : [SubSequence] {
        guard !isEmpty else { return [] }
        let count = self.count
        let n = 1 << count - 1
        var subSequences: [SubSequence] = .init(repeating: SubSequence(), count: n-1)
        (0 ..< n).map {
            var counter = 0
            for element in self {
                if $0 & 1 << counter > 0 {
                    subSequences[$0-1].append(element)
                }
                counter += 1
            }
        }
        return subSequences + [self[...]]
    }
}

let matches2 = a.subSetsNotRecursive.lazy.filter { $0.reduce(0,+) == n }
print("Matches2:", Array(matches2.map{Array($0)}))  // Matches2: [[10, 8], [4, 13, 1]]

或者进一步扩展它。如果您只对总和等于某个值的集合感兴趣,您可以在每次完成创建集合时检查附加元素的总和是否等于您想要的值:

extension RangeReplaceableCollection where Element: BinaryInteger {
    func subSetsThatSummedAre(equalTo value: Element) -> [SubSequence] {
        guard !isEmpty else { return [] }
        let count = self.count
        let n = 1 << count - 1
        var subSets: [SubSequence] = []
        (0 ..< n).map {
            var counter = 0
            var sum: Element = 0
            var subSequence = SubSequence()
            for element in self {
                if $0 & 1 << counter > 0 {
                    subSequence.append(element)
                    sum += element
                }
                counter += 1
            }
            if sum == value {
                subSets.append(subSequence)
            }
        }
        if reduce(0, +) == value {
            subSets.append(self[...])
        }
        return subSets
    }
}

let matches4 = a.subSetsThatSummedAre(equalTo: n)
print("Matches4:", Array(matches4.map{Array($0)}))  // Matches4: [[10, 8], [4, 13, 1]]

【讨论】:

  • 那肯定行得通,但我怀疑它是否是 OP 要求的“最有效”的解决方案。你知道Subset sum 函数的时间复杂度吗?
  • 请注意,我添加数组映射仅用于显示目的。那张地图是不需要的。
  • @DuncanC 现在您有 3 种完全不同的方法。您可以检查哪个更快。
  • 递归和非递归“计算所有子集”函数是否至少具有O(n²) 时间复杂度?这就是我一眼看去的样子。因此,我怀疑这是否是 OP 要求的“最有效”的解决方案。
  • 如果不将它们结合起来怎么可能呢? OP 甚至没有解决方案。我认为 OP 要求不会如此复杂,以至于任何这些解决方案都不够用。
猜你喜欢
  • 2010-11-15
  • 1970-01-01
  • 2019-08-13
  • 1970-01-01
  • 2011-03-11
  • 1970-01-01
  • 1970-01-01
  • 2018-10-04
  • 2012-10-17
相关资源
最近更新 更多