【问题标题】:SubSet whose sum is greatest but less than a particular value总和最大但小于特定值的子集
【发布时间】:2017-01-10 20:37:28
【问题描述】:

我有一个包含正值的数组。 例如数组= {5,4,4,3.8,2,1.7} 我需要找到一个总和最大但小于 12 的子数组。 在这种情况下,它将是 {4,4,3.8}

另一个前数组 {7,4,3,2} 在这种情况下,最大和为 12,子集为 {7,3,2}

它的算法是什么,因为我有一个长度超过 1000 的非常大的数组。 我正在用 VBA excel 编写这个程序。

谢谢。

【问题讨论】:

  • 请显示您所做的尝试。
  • {5,4,4,3.8,2,1.7} 中没有反例,但是“子数组”是指set(即任何一组数字)还是sequence
  • @Nelxost 如果 OP 表示子集而不是子序列,这与那些问题完全相同。如果他的意思是子序列,那么强力二次解确实是可能的。这就是共产国际问的原因。
  • @Comintem 是设置(即任何组和任何长度的子数组)

标签: vba algorithm excel knapsack-problem sub-array


【解决方案1】:

试试这个算法的子序列

Sub aargh()
Dim a As Variant
Dim limsum As Double, highestnum As Double
Dim i As Integer, j As Integer
    a = Array(5, 4, 4, 3.8, 2, 1.7)
    limsum = 12

    highestsum = 0
    For i = 0 To UBound(a)
        s = a(i)
        j = i
        Do
            If s > highestsum Then fs = i: ls = j: highestsum = s
            j = j + 1
            If j > UBound(a) Then Exit Do
            s = s + a(j)
        Loop While s <= limsum
    Next i
    MsgBox "subarray (" & fs & "," & ls & ") sum = " & highestsum
End Sub

已编辑以包含以下备注并包含子集的解决方案

Sub aargh()

    Dim sol(), csol()
    a = Array(7, 4, 3, 2)

    ReDim sol(LBound(a) To UBound(a))
    ReDim csol(LBound(a) To UBound(a))
    limsum = 13
    findsum a, sol, csol, maxsum, limsum
    ss = "array "
    For i = 1 To sol(0)
        ss = ss & sep & a(sol(i))
        sep = ","
    Next i
    MsgBox ss & " sum =" & maxsum
End Sub
Sub findsum(ByRef a, ByRef sol, ByRef csol, ByRef maxsum, ByRef limsum, Optional s = 0, Optional lvl = 1, Optional si = 0)
' recursive sub
    For i = si To UBound(a)
        s = s + a(i)
        csol(lvl) = i ' current solution contains number i
        If s <= limsum Then
            If s > maxsum Then ' we found a sum greater than current max we save it
                maxsum = s
                sol(0) = lvl
                For j = 1 To lvl
                    sol(j) = csol(j)
                Next j
            End If
            If i < UBound(a) Then ' pick another number
                findsum a, sol, csol, maxsum, limsum, s, lvl + 1, i + 1
            End If
        End If
        s = s - a(i)
    Next i
End Sub

如果数组已排序(降序)则优化代码

Sub aargh()

    Dim sol(), csol()
    a = Array(20, 15, 10, 7, 6, 5, 4, 3, 2)

    ReDim sol(LBound(a) To UBound(a))
    ReDim csol(LBound(a) To UBound(a))
    limsum = 13
    findsum a, sol, csol, maxsum, limsum, UBound(a)
    ss = "array "
    For i = 1 To sol(0)
        ss = ss & sep & a(sol(i))
        sep = ","
    Next i
    MsgBox ss & " sum =" & maxsum
End Sub
Sub findsum(ByRef a, ByRef sol, ByRef csol, ByRef maxsum, ByRef limsum, si, Optional s = 0, Optional lvl = 1)
' recursive sub
    For i = si To LBound(a) Step -1
        If s + a(i) > limsum Then Exit For
        s = s + a(i)
        csol(lvl) = i    ' current solution contains number i
        If s <= limsum Then
            If s > maxsum Then    ' we found a sum greater than current max we save it
                maxsum = s
                sol(0) = lvl
                For j = 1 To lvl
                    sol(j) = csol(j)
                Next j
            End If
            If i > LBound(a) Then    ' pick another number
                findsum a, sol, csol, maxsum, limsum, i - 1, s, lvl + 1
            End If
        End If
        s = s - a(i)
        If maxsum = limsum Then Exit For 'exit if exact match
    Next i
End Sub

【讨论】:

  • 你可能想要For i = 0 To UBound(a)。否则,您将不允许子序列在最后一个元素处开始(和结束)。因此,例如,如果您的设置是 a = Array(5, 4, 4, 3.8, 2, 13.9)limsum = 14,那么您希望返回子序列 (5,5) w/ sum 13.9 而不是子序列 (1,4) w/ sum 13.8。否则很好的蛮力解决方案。
  • 我检查了你的代码,但它没有给出这个数组的正确结果 (7, 4, 3, 2) 最大总和应该是 12 子集 (7,3,2) 但它的总和=11
  • @Abhishek 您是否编辑了limsum 的值?那是总和限制数。出于某种原因,h2so4 将其设置为 13 而不是 12。如果将其修改为 12,您会发现它适用于您的示例。但是,它适用于子序列而不是子集(因为问题不是很明确)。如果你真的想要子集,你最好研究一些背包问题的算法(在 cmets 中链接)。
  • 实际上我要问的是子集数不必是连续的。它们可以是任何一组数字。
  • @Abhishek en.wikipedia.org/wiki/Knapsack_problem#Solving 有一些伪代码,stackoverflow.com/questions/tagged/knapsack-problem 有很多 StackOverflow 问题和讨论。
【解决方案2】:

正如许多人在 cmets 中提到的,这是 subset sum problem 的简单变体。为了解决这个确切的问题,您只需要记住小于或等于12 的最大数字。

一个简单的递归abroach如下:

list = [...]
N = length of list
dp[ maximum possible sum ][ N ]

f(‌current_sum, index):
  if dp[current_sum][index] is set
    return dp[current_sum][index]
  if index >= N
    if current_sum > 12
       result = 0
    else
       result = current_sum
  else
    result = max( f(current_sum, index+1), f(current_sum+list[index], index+1)
  dp[current_sum][index] = result
  return result

result = f(0,0) // this will return the desired result

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-08-07
    • 2013-08-06
    • 2021-06-04
    • 2016-05-27
    • 1970-01-01
    • 1970-01-01
    • 2018-08-10
    相关资源
    最近更新 更多