获取scala中数组值的组合总和

时间:2017-04-15 17:22:14

标签: arrays scala functional-programming

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

我有一个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方法中使用它调用它:

   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

我是使用 子集 完成的,但当数组超过10时,我得到 内存堆错误 因为它正在制定所有组合才能获得答案。

那么我们如何通过提高内存效率,使用当前格式或利用Scala的函数编程功能来实现这一目标呢?

1 个答案:

答案 0 :(得分:0)

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

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
  }
}