【问题标题】:Get sum of combinations of array values in scala获取scala中数组值组合的总和
【发布时间】:2017-04-17 06:22:38
【问题描述】:

我知道有一些类似的问题,但似乎仍然没有明确的答案,所以我会问它..

我有一个array 的值,我正在尝试找到正确的值,这些值的总和为limit 值或从任何给定组合中可以得到的最接近的值(不超过它)。

使用这个答案https://stackoverflow.com/questions/23168934/calculating-minimal-subset-with-given-sum 我有这个:

    def getLimitArr(arr: Array[Int], limit: Int): Unit = {

       scala.util.Sorting.quickSort(arr) // Array(2, 3, 4, 5, 11, 34)
       var sum = 0L
       var i = arr.length-1

       val arr2 = ArrayBuffer[Integer]()
       while (i >= 0 && sum < limit) {
         if(sum + arr(i)<=limit) {
            sum += arr(i)
            arr2 += arr(i)
         }
         i -= 1 // 6, 5, 4, 3, 2, 1
       }

    println(arr2.mkString(", ") + " = " + sum)

   }

并在 main 方法中使用 this 调用它:

   val arr = Array(3, 34, 4, 11, 5, 2)
   getLimitArr(arr, 9)

返回:

   println(arr2.mkString(", ") + " = " + sum) // 5, 4 = 9

这很好,但前提是(构成总和的)值可以从低于限制的最大值得出;在这个例子中 5 - 正如我们所见,它与这个数组一起工作。但如果此数组的限制值为 12 (getLimitArr(arr, 12)),那么它将返回 11 = 11 而不是使用 5 + 4 + 3

我已经使用 subsets 完成了这项工作,但是当数组超过 10 时,我得到 内存堆错误 因为它在获得答案之前正在制定所有的组合。

那么我们将如何通过提高内存效率、使用当前格式或利用 Scala 的函数式编程能力来做到这一点?

【问题讨论】:

  • 这似乎是背包问题的一种变体,有一个已知的解决方案:en.wikipedia.org/wiki/Knapsack_problem
  • 看起来是这样,但这并不能回答如何使用许多数组值编写有效的解决方案并且不会导致内存堆错误的问题

标签: arrays scala functional-programming


【解决方案1】:

当我们想在找到第一个正确答案后立即终止时,递归通常很有用。

def getLimit(nums: Array[Int], limit: Int): Array[Int] = {
  val subset = nums.filter(limit.>=)
  if (subset.isEmpty) Array()
  else (1 to subset.length).flatMap(subset.combinations)
                           .find(_.sum == limit)
                           .fold(getLimit(subset, limit-1))(identity)
}

getLimit(Array(3, 34, 4, 11, 5, 2), 5)   // res0: Array[Int] = Array(5)
getLimit(Array(3, 34, 4, 11, 5, 2), 9)   // res1: Array[Int] = Array(4, 5)
getLimit(Array(3, 34, 4, 11, 5, 2), 12)  // res2: Array[Int] = Array(3, 4, 5)
getLimit(Array(3, 34, 4, 11, 5, 2), 24)  // res3: Array[Int] = Array(3, 4, 11, 5)

请注意,最后一个总和为 23,因为没有任何组合总和为 24。

更新

添加了更好的快捷方式,该方法现在是尾递归的。

def getLimit(nums: Array[Int], limit: Int): Array[Int] = {
  val subset = nums.filter(limit.>=)
  if (subset.sum <= limit) subset
  else {
    val res = (1 to subset.length).view
                                  .flatMap(subset.combinations)
                                  .find(_.sum == limit)
    if (res.isEmpty) getLimit(subset, limit-1)
    else res.get
  }
}

【讨论】:

  • 谢谢,但数组值不止几个;如果仅使用一两个找不到解决方案,我们会收到 java.lang.OutOfMemoryError: GC 开销限制超出 错误。我正在寻找可以处理多达 30 个数组值的东西_
  • 这绝对是更好的,这是一个有价值的答案,但它不能处理 30 个数组值,我不禁觉得有一个更精简的解决方案可以
  • 只是说虽然它不能处理最多 30 个数组,但我认为它可能是最佳选择,因此将其标记为正确。当然,如果有人要添加任何内容,那么这对其他人来说将很有用。谢谢
猜你喜欢
  • 2023-04-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-14
  • 1970-01-01
  • 2016-07-09
相关资源
最近更新 更多