寻找数学函数以找到最大可能的总和
Looking for math function for finding highest possibile sum
我有一组 4 位数字,我必须从中找出最大可能的和,最多包含 5 个项目,不超过 24,000,但不低于 21,000。尽管这听起来微不足道,而且我手工完成没有任何问题(集合不是很多),但我无法将其转移到电子表格中。
@编辑
前任。数据集{4785,4890,5030,5790,6020,3230,4500,5000,4550,2300,5400,5350.....}
如果你按降序排列数据并计算部分和,那么递归算法很快就找到了解决方案(当然,如果存在的话)
Option Explicit
Dim valRes() As Boolean
Dim indRng() As Long
Dim rngSum() As Currency
Dim ttlSum() As Currency
Dim lowA As Long, upA As Long
Rem The function returns an array of boolean values,
Rem in which True marks the line numbers in the rngSumIsh range,
Rem from which the total can be collected with an acceptable error of no more than delta,
Rem or completely False - "No result"
Function findSet2(total As Double, rngSumIsh As Variant, Optional delta) As Variant
Dim restSum As Currency
Dim i As Long
Dim firstEl As Long, lastEl As Long
If IsMissing(delta) Then delta = 0
If Not IsArray(rngSumIsh) Then
findSet2 = False
Exit Function
End If
If delta < 0 Then
findSet2 = False
Exit Function
End If
Call createWorkArrays(rngSumIsh)
restSum = total
findSet2 = valRes
If restSum - delta < ttlSum(upA) Then Exit Function
If restSum - delta > ttlSum(lowA) Then
For i = lowA To upA
valRes(i, 1) = True
Next i
findSet2 = valRes
Exit Function
End If
Call getNextElem(lowA - 1, restSum, delta, firstEl, lastEl)
If firstEl <= lastEl Then ' There is a suitable element
For i = firstEl To lastEl
If Abs(restSum - rngSum(indRng(i))) <= delta Then
valRes(indRng(i), 1) = True
Exit For
ElseIf isfindSet2(i, restSum - rngSum(indRng(i)), delta) Then
valRes(indRng(i), 1) = True
Exit For
End If
Next i
End If
findSet2 = valRes
End Function
Function isfindSet2(curIndex As Long, restSum As Currency, delta As Currency) As Boolean
Dim i As Long, firstEl As Long, lastEl As Long
isfindSet2 = False
Call getNextElem(curIndex, restSum, delta, firstEl, lastEl)
If firstEl <= lastEl Then
For i = firstEl To lastEl
If Abs(restSum - rngSum(indRng(i))) <= delta Then
valRes(indRng(i), 1) = True
isfindSet2 = True
Exit For
ElseIf isfindSet2(i, restSum - rngSum(indRng(i)), delta) Then
valRes(indRng(i), 1) = True
isfindSet2 = True
Exit For
End If
Next i
End If
End Function
Sub createWorkArrays(arrSum As Variant)
Dim i As Long, j As Long
Dim tmpVal As Long
lowA = LBound(arrSum)
upA = UBound(arrSum)
ReDim rngSum(lowA To upA)
ReDim valRes(lowA To upA, 1 To 1)
ReDim indRng(lowA To upA)
ReDim ttlSum(lowA To upA)
For i = lowA To upA
rngSum(i) = arrSum(i, 1)
indRng(i) = i
valRes(i, 1) = False
Next i
Rem Let's "sort" the rngSum array (actually sort the array of its indices)
For i = lowA To upA - 1
For j = i + 1 To upA
If rngSum(indRng(i)) < rngSum(indRng(j)) Then
tmpVal = indRng(i)
indRng(i) = indRng(j)
indRng(j) = tmpVal
End If
Next j
Next i
ttlSum(upA) = rngSum(indRng(upA))
For i = upA - 1 To lowA Step -1
ttlSum(i) = ttlSum(i + 1) + rngSum(indRng(i))
Next i
End Sub
Rem The procedure tries to find the next matching element in the rngSum array - firstEl starting from the next one after curIndex.
Rem A qualifying element is one whose sum adjusted for delta does not exceed the remainder.
Rem The last matching element in the ttlSum array is also searched for - lastEl
Rem (the amount of the remaining items is not less than the remainder)
Rem Returns the indices of found elements
Sub getNextElem(curIndex As Long, restSum As Currency, delta As Currency, ByRef firstEl As Long, ByRef lastEl As Long)
Dim i As Long
firstEl = upA + 1
lastEl = lowA - 1
If curIndex < upA Then
For i = curIndex + 1 To upA
If restSum + delta >= rngSum(indRng(i)) Then
firstEl = i
Exit For
End If
Next i
For i = firstEl To upA
If restSum - delta <= ttlSum(i) Then
lastEl = i
Else
Exit For
End If
Next i
End If
End Sub
如果您需要更多注释来理解代码,请参阅此 file
中的完整代码版本
Tools -> Solver 适用于您示例中的值。这是我使用的电子表格,单元格 A2 到 A13 中有数据。
Data Chosen Rows Chosen Values Calculation
4785 5 5790 24000
4890 7 3230
5030 4 5030
5790 12 5400
6020 10 4550
3230
4500
5000
4550
2300
5400
5350
C2
的公式为=INDIRECT("A"&B2)
,向下填充至C6。那么 D2
就是 =SUM(C2:C6)
.
这里是规划求解设置。
同样在选项下,使用非线性求解器。指定变量是整数(这是必需的,即使我添加了整数作为限制条件)和非负数。
编辑:
every value can be used only once
在这种情况下,在单元格 B8 中输入以下数组公式,在 中进行了解释。
=SUM(1/COUNTIF(B2:B6;B2:B6))
然后给求解器添加另一个限制条件:$B => 5
.
我有一组 4 位数字,我必须从中找出最大可能的和,最多包含 5 个项目,不超过 24,000,但不低于 21,000。尽管这听起来微不足道,而且我手工完成没有任何问题(集合不是很多),但我无法将其转移到电子表格中。
@编辑 前任。数据集{4785,4890,5030,5790,6020,3230,4500,5000,4550,2300,5400,5350.....}
如果你按降序排列数据并计算部分和,那么递归算法很快就找到了解决方案(当然,如果存在的话)
Option Explicit
Dim valRes() As Boolean
Dim indRng() As Long
Dim rngSum() As Currency
Dim ttlSum() As Currency
Dim lowA As Long, upA As Long
Rem The function returns an array of boolean values,
Rem in which True marks the line numbers in the rngSumIsh range,
Rem from which the total can be collected with an acceptable error of no more than delta,
Rem or completely False - "No result"
Function findSet2(total As Double, rngSumIsh As Variant, Optional delta) As Variant
Dim restSum As Currency
Dim i As Long
Dim firstEl As Long, lastEl As Long
If IsMissing(delta) Then delta = 0
If Not IsArray(rngSumIsh) Then
findSet2 = False
Exit Function
End If
If delta < 0 Then
findSet2 = False
Exit Function
End If
Call createWorkArrays(rngSumIsh)
restSum = total
findSet2 = valRes
If restSum - delta < ttlSum(upA) Then Exit Function
If restSum - delta > ttlSum(lowA) Then
For i = lowA To upA
valRes(i, 1) = True
Next i
findSet2 = valRes
Exit Function
End If
Call getNextElem(lowA - 1, restSum, delta, firstEl, lastEl)
If firstEl <= lastEl Then ' There is a suitable element
For i = firstEl To lastEl
If Abs(restSum - rngSum(indRng(i))) <= delta Then
valRes(indRng(i), 1) = True
Exit For
ElseIf isfindSet2(i, restSum - rngSum(indRng(i)), delta) Then
valRes(indRng(i), 1) = True
Exit For
End If
Next i
End If
findSet2 = valRes
End Function
Function isfindSet2(curIndex As Long, restSum As Currency, delta As Currency) As Boolean
Dim i As Long, firstEl As Long, lastEl As Long
isfindSet2 = False
Call getNextElem(curIndex, restSum, delta, firstEl, lastEl)
If firstEl <= lastEl Then
For i = firstEl To lastEl
If Abs(restSum - rngSum(indRng(i))) <= delta Then
valRes(indRng(i), 1) = True
isfindSet2 = True
Exit For
ElseIf isfindSet2(i, restSum - rngSum(indRng(i)), delta) Then
valRes(indRng(i), 1) = True
isfindSet2 = True
Exit For
End If
Next i
End If
End Function
Sub createWorkArrays(arrSum As Variant)
Dim i As Long, j As Long
Dim tmpVal As Long
lowA = LBound(arrSum)
upA = UBound(arrSum)
ReDim rngSum(lowA To upA)
ReDim valRes(lowA To upA, 1 To 1)
ReDim indRng(lowA To upA)
ReDim ttlSum(lowA To upA)
For i = lowA To upA
rngSum(i) = arrSum(i, 1)
indRng(i) = i
valRes(i, 1) = False
Next i
Rem Let's "sort" the rngSum array (actually sort the array of its indices)
For i = lowA To upA - 1
For j = i + 1 To upA
If rngSum(indRng(i)) < rngSum(indRng(j)) Then
tmpVal = indRng(i)
indRng(i) = indRng(j)
indRng(j) = tmpVal
End If
Next j
Next i
ttlSum(upA) = rngSum(indRng(upA))
For i = upA - 1 To lowA Step -1
ttlSum(i) = ttlSum(i + 1) + rngSum(indRng(i))
Next i
End Sub
Rem The procedure tries to find the next matching element in the rngSum array - firstEl starting from the next one after curIndex.
Rem A qualifying element is one whose sum adjusted for delta does not exceed the remainder.
Rem The last matching element in the ttlSum array is also searched for - lastEl
Rem (the amount of the remaining items is not less than the remainder)
Rem Returns the indices of found elements
Sub getNextElem(curIndex As Long, restSum As Currency, delta As Currency, ByRef firstEl As Long, ByRef lastEl As Long)
Dim i As Long
firstEl = upA + 1
lastEl = lowA - 1
If curIndex < upA Then
For i = curIndex + 1 To upA
If restSum + delta >= rngSum(indRng(i)) Then
firstEl = i
Exit For
End If
Next i
For i = firstEl To upA
If restSum - delta <= ttlSum(i) Then
lastEl = i
Else
Exit For
End If
Next i
End If
End Sub
如果您需要更多注释来理解代码,请参阅此 file
中的完整代码版本Tools -> Solver 适用于您示例中的值。这是我使用的电子表格,单元格 A2 到 A13 中有数据。
Data Chosen Rows Chosen Values Calculation
4785 5 5790 24000
4890 7 3230
5030 4 5030
5790 12 5400
6020 10 4550
3230
4500
5000
4550
2300
5400
5350
C2
的公式为=INDIRECT("A"&B2)
,向下填充至C6。那么 D2
就是 =SUM(C2:C6)
.
这里是规划求解设置。
同样在选项下,使用非线性求解器。指定变量是整数(这是必需的,即使我添加了整数作为限制条件)和非负数。
编辑:
every value can be used only once
在这种情况下,在单元格 B8 中输入以下数组公式,在
=SUM(1/COUNTIF(B2:B6;B2:B6))
然后给求解器添加另一个限制条件:$B => 5
.