Lianne072600
asked on
Determine which figures in a number of cells will add up to a set total.
There may already be an Excel function that does this. Basically what I want to do is, determine which cells contain figures that will add up to a set amount, e.g.: -
A B
1 Description Amount
2 December 12th 22
3 December 15th 12
4 January 1st 1
5 January 8th 29
6 January 21st 5
7
8 Required Value 43 (this value is typed in)
ANS: B3, B4 and B5 = 42
I want to look at all the amounts entered and determine which cells contain amounts that will add up to the figure entered (or the nearest amount with a deficit). The amounts that are entered in the cells cannot be altered - as would be done with solver. I am not too worried on the format of the answer as long as I know the cells that are used and their total.
Help with this is greatly appreciated. Thanks.
Lianne
A B
1 Description Amount
2 December 12th 22
3 December 15th 12
4 January 1st 1
5 January 8th 29
6 January 21st 5
7
8 Required Value 43 (this value is typed in)
ANS: B3, B4 and B5 = 42
I want to look at all the amounts entered and determine which cells contain amounts that will add up to the figure entered (or the nearest amount with a deficit). The amounts that are entered in the cells cannot be altered - as would be done with solver. I am not too worried on the format of the answer as long as I know the cells that are used and their total.
Help with this is greatly appreciated. Thanks.
Lianne
ASKER
functionpages>>
-- Are you looking for the first combination that can reach the answer, or all combinations of cells in the range? (B2:B6, in your above example)?
The first combination would suffice as it does not matter which combination is used, though all combinations would be a nice addition.
-- What is your preference... a list of numbers with a deficit of 1, or an exact match that takes longer to produce?
An exact match if possible.
The range is roughly 200 to 300 cells max.
Hope this helps and clarifies things. Thanks.
Lianne
-- Are you looking for the first combination that can reach the answer, or all combinations of cells in the range? (B2:B6, in your above example)?
The first combination would suffice as it does not matter which combination is used, though all combinations would be a nice addition.
-- What is your preference... a list of numbers with a deficit of 1, or an exact match that takes longer to produce?
An exact match if possible.
The range is roughly 200 to 300 cells max.
Hope this helps and clarifies things. Thanks.
Lianne
Hi,
I'll address your initial question, and then we can discuss if you need amendments.
Place this code into a code module of your workbook:
=== START OF CODE ===
Option Explicit
Public Function Find_Total(ByRef rngCells As Range, ByVal dblFind_Total As Double)
' Experts Exchange - Q_20864109 [https://www.experts-exchange.com/questions/20864109/Determine-which-figures-in-a-number-of-cells-will-add-up-to-a-set-total.html]
'
' Microsoft Excel: Determine which figures in a number of cells will add up to a set total
'
' (c) Copyright 2004 Clearlogic Concepts (UK) Limited
'
' N.Lee - 27/01/2004 - [http://NigelLee.info]
Dim dblTotal As Double
Dim dblValue As Double
Dim intPos As Integer
Dim lngLoop As Long
Dim objCell As Range
Dim strReturn As String
Dim strArray() As String
Dim strResult() As String
On Error Resume Next
strReturn = ""
dblTotal = 0#
ReDim strArray(0) As String
ReDim strResult(0) As String
For Each objCell In rngCells
If IsNumeric(objCell.Value) Then
ReDim Preserve strArray(UBound(strArray) + 1) As String
strArray(UBound(strArray)) = objCell.Value & Chr$(0) & objCell.Address(RowAbsolut e:=False, COLUMNABSOLUTE:=False)
End If
Next objCell
If (blnQuick_Sort_Strings(str Array, 1&, UBound(strArray), False)) Then
For lngLoop = UBound(strArray) To 1& Step -1&
intPos = InStr(strArray(lngLoop), Chr$(0))
dblValue = CDbl(Left$(strArray(lngLoo p), intPos - 1))
If dblTotal + dblValue <= dblFind_Total Then
dblTotal = dblTotal + dblValue
ReDim Preserve strResult(UBound(strResult ) + 1) As String
strResult(UBound(strResult )) = Mid$(strArray(lngLoop), intPos + 1)
If dblValue = dblFind_Total Then
Exit For
End If
End If
Next lngLoop
End If
If UBound(strResult) > 0& Then
If (blnQuick_Sort_Strings(str Result, 1&, UBound(strResult), True)) Then
strReturn = ""
For lngLoop = 1& To UBound(strResult)
strReturn = strReturn & IIf(Len(strReturn) > 0, IIf(lngLoop < UBound(strResult), ", ", " and "), "") & strResult(lngLoop)
Next lngLoop
End If
End If
Find_Total = strReturn & IIf(Len(strReturn) > 0, " = " & CStr(dblTotal), "ERROR!")
End Function
Private Function blnQuick_Sort_Strings(ByRe f strArray() As String, ByRef lngLow_Value As Long, ByRef lngHigh_Value As Long, Optional ByVal blnAlpha_Sort As Boolean = False) As Boolean
Dim blnReturn As Boolean
Dim blnSwap As Boolean
Dim lngLow As Long
Dim lngHigh As Long
Dim lngPivot As Long
Dim lngPosLow As Long
Dim lngPosHigh As Long
Dim strPivot As Variant
On Error GoTo Err_blnQuick_Sort_Strings
blnReturn = False
lngLow = lngLow_Value
lngHigh = lngHigh_Value
If lngLow >= lngHigh Then
blnQuick_Sort_Strings = True
Exit Function
End If
' If only 2 elements in this subdivision; swap them if out of order...
If (lngHigh - lngLow) = 1& Then
If (blnAlpha_Sort) Then
blnSwap = (strArray(lngLow) > strArray(lngHigh))
Else
blnSwap = (Val(strArray(lngLow)) > Val(strArray(lngHigh)))
End If
If (blnSwap) Then
Call strSwap(strArray(lngLow), strArray(lngHigh))
End If
blnQuick_Sort_Strings = True
Exit Function
End If
' Pick a pivot element at random & move it to the end...
lngPivot = CLng(Int(Rnd(1) * (lngHigh - lngLow) + 1&) + lngLow)
Call strSwap(strArray(lngHigh), strArray(lngPivot))
strPivot = UCase$(strArray(lngHigh))
Do
lngPosLow = lngLow
lngPosHigh = lngHigh
' Move in from both sides towards the pivot element...
If (blnAlpha_Sort) Then
Do While (lngPosLow < lngPosHigh) And (UCase$(strArray(lngPosLow )) <= strPivot)
lngPosLow = lngPosLow + 1&
Loop
Do While (lngPosHigh > lngPosLow) And (UCase$(strArray(lngPosHig h)) >= strPivot)
lngPosHigh = lngPosHigh - 1&
Loop
Else
Do While (lngPosLow < lngPosHigh) And (Val(strArray(lngPosLow)) <= Val(strPivot))
lngPosLow = lngPosLow + 1&
Loop
Do While (lngPosHigh > lngPosLow) And (Val(strArray(lngPosHigh)) >= Val(strPivot))
lngPosHigh = lngPosHigh - 1&
Loop
End If
' If we haven't reached the pivot element then two elements on either side are out of order & need swapping...
If lngPosLow < lngPosHigh Then
Call strSwap(strArray(lngPosLow ), strArray(lngPosHigh))
End If
Loop While (lngPosLow < lngPosHigh)
' Move the pivot element back to its proper place in the array...
Call strSwap(strArray(lngPosLow ), strArray(lngHigh))
' Recursively call the Sort procedure (pass the smaller subdivision first to use less stack space)...
blnReturn = True
If (lngPosLow - lngLow) < (lngHigh - lngPosLow) Then
blnReturn = blnQuick_Sort_Strings(strA rray(), lngLow, lngPosLow - 1&, blnAlpha_Sort)
If (blnReturn) Then
blnReturn = blnQuick_Sort_Strings(strA rray(), lngPosLow + 1&, lngHigh, blnAlpha_Sort)
End If
Else
blnReturn = blnQuick_Sort_Strings(strA rray(), lngPosLow + 1&, lngHigh, blnAlpha_Sort)
If (blnReturn) Then
blnReturn = blnQuick_Sort_Strings(strA rray(), lngLow, lngPosLow - 1&, blnAlpha_Sort)
End If
End If
Exit_blnQuick_Sort_Strings :
On Error Resume Next
blnQuick_Sort_Strings = blnReturn
Exit Function
Err_blnQuick_Sort_Strings:
blnReturn = False
Resume Exit_blnQuick_Sort_Strings
End Function
Private Sub strSwap(ByRef strFirst As String, ByRef strSecond As String)
Dim strTemp As String
On Error Resume Next
strTemp = strSecond
strSecond = strFirst
strFirst = strTemp
End Sub
=== END OF CODE ===
If you place this formula in the cell where you would like the result:
="ANS: " & Find_Total(B2:B6,B8)
It will return (as requested):
ANS: B3, B4 and B5 = 42
However, if cell B3 was a value of 13, the result would be:
ANS: B3, B4 and B5 = 43
If cell B3 was 14...
ANS: B3 and B5 = 43
Hope this addresses your problem.
BFN,
fp.
[http://NigelLee.info]
I'll address your initial question, and then we can discuss if you need amendments.
Place this code into a code module of your workbook:
=== START OF CODE ===
Option Explicit
Public Function Find_Total(ByRef rngCells As Range, ByVal dblFind_Total As Double)
' Experts Exchange - Q_20864109 [https://www.experts-exchange.com/questions/20864109/Determine-which-figures-in-a-number-of-cells-will-add-up-to-a-set-total.html]
'
' Microsoft Excel: Determine which figures in a number of cells will add up to a set total
'
' (c) Copyright 2004 Clearlogic Concepts (UK) Limited
'
' N.Lee - 27/01/2004 - [http://NigelLee.info]
Dim dblTotal As Double
Dim dblValue As Double
Dim intPos As Integer
Dim lngLoop As Long
Dim objCell As Range
Dim strReturn As String
Dim strArray() As String
Dim strResult() As String
On Error Resume Next
strReturn = ""
dblTotal = 0#
ReDim strArray(0) As String
ReDim strResult(0) As String
For Each objCell In rngCells
If IsNumeric(objCell.Value) Then
ReDim Preserve strArray(UBound(strArray) + 1) As String
strArray(UBound(strArray))
End If
Next objCell
If (blnQuick_Sort_Strings(str
For lngLoop = UBound(strArray) To 1& Step -1&
intPos = InStr(strArray(lngLoop), Chr$(0))
dblValue = CDbl(Left$(strArray(lngLoo
If dblTotal + dblValue <= dblFind_Total Then
dblTotal = dblTotal + dblValue
ReDim Preserve strResult(UBound(strResult
strResult(UBound(strResult
If dblValue = dblFind_Total Then
Exit For
End If
End If
Next lngLoop
End If
If UBound(strResult) > 0& Then
If (blnQuick_Sort_Strings(str
strReturn = ""
For lngLoop = 1& To UBound(strResult)
strReturn = strReturn & IIf(Len(strReturn) > 0, IIf(lngLoop < UBound(strResult), ", ", " and "), "") & strResult(lngLoop)
Next lngLoop
End If
End If
Find_Total = strReturn & IIf(Len(strReturn) > 0, " = " & CStr(dblTotal), "ERROR!")
End Function
Private Function blnQuick_Sort_Strings(ByRe
Dim blnReturn As Boolean
Dim blnSwap As Boolean
Dim lngLow As Long
Dim lngHigh As Long
Dim lngPivot As Long
Dim lngPosLow As Long
Dim lngPosHigh As Long
Dim strPivot As Variant
On Error GoTo Err_blnQuick_Sort_Strings
blnReturn = False
lngLow = lngLow_Value
lngHigh = lngHigh_Value
If lngLow >= lngHigh Then
blnQuick_Sort_Strings = True
Exit Function
End If
' If only 2 elements in this subdivision; swap them if out of order...
If (lngHigh - lngLow) = 1& Then
If (blnAlpha_Sort) Then
blnSwap = (strArray(lngLow) > strArray(lngHigh))
Else
blnSwap = (Val(strArray(lngLow)) > Val(strArray(lngHigh)))
End If
If (blnSwap) Then
Call strSwap(strArray(lngLow), strArray(lngHigh))
End If
blnQuick_Sort_Strings = True
Exit Function
End If
' Pick a pivot element at random & move it to the end...
lngPivot = CLng(Int(Rnd(1) * (lngHigh - lngLow) + 1&) + lngLow)
Call strSwap(strArray(lngHigh),
strPivot = UCase$(strArray(lngHigh))
Do
lngPosLow = lngLow
lngPosHigh = lngHigh
' Move in from both sides towards the pivot element...
If (blnAlpha_Sort) Then
Do While (lngPosLow < lngPosHigh) And (UCase$(strArray(lngPosLow
lngPosLow = lngPosLow + 1&
Loop
Do While (lngPosHigh > lngPosLow) And (UCase$(strArray(lngPosHig
lngPosHigh = lngPosHigh - 1&
Loop
Else
Do While (lngPosLow < lngPosHigh) And (Val(strArray(lngPosLow)) <= Val(strPivot))
lngPosLow = lngPosLow + 1&
Loop
Do While (lngPosHigh > lngPosLow) And (Val(strArray(lngPosHigh))
lngPosHigh = lngPosHigh - 1&
Loop
End If
' If we haven't reached the pivot element then two elements on either side are out of order & need swapping...
If lngPosLow < lngPosHigh Then
Call strSwap(strArray(lngPosLow
End If
Loop While (lngPosLow < lngPosHigh)
' Move the pivot element back to its proper place in the array...
Call strSwap(strArray(lngPosLow
' Recursively call the Sort procedure (pass the smaller subdivision first to use less stack space)...
blnReturn = True
If (lngPosLow - lngLow) < (lngHigh - lngPosLow) Then
blnReturn = blnQuick_Sort_Strings(strA
If (blnReturn) Then
blnReturn = blnQuick_Sort_Strings(strA
End If
Else
blnReturn = blnQuick_Sort_Strings(strA
If (blnReturn) Then
blnReturn = blnQuick_Sort_Strings(strA
End If
End If
Exit_blnQuick_Sort_Strings
On Error Resume Next
blnQuick_Sort_Strings = blnReturn
Exit Function
Err_blnQuick_Sort_Strings:
blnReturn = False
Resume Exit_blnQuick_Sort_Strings
End Function
Private Sub strSwap(ByRef strFirst As String, ByRef strSecond As String)
Dim strTemp As String
On Error Resume Next
strTemp = strSecond
strSecond = strFirst
strFirst = strTemp
End Sub
=== END OF CODE ===
If you place this formula in the cell where you would like the result:
="ANS: " & Find_Total(B2:B6,B8)
It will return (as requested):
ANS: B3, B4 and B5 = 42
However, if cell B3 was a value of 13, the result would be:
ANS: B3, B4 and B5 = 43
If cell B3 was 14...
ANS: B3 and B5 = 43
Hope this addresses your problem.
BFN,
fp.
[http://NigelLee.info]
Hi,
Sorry... I forgot to declare the data type of the return from the main function.
Please replace:
Public Function Find_Total(ByRef rngCells As Range, ByVal dblFind_Total As Double)
With
Public Function Find_Total(ByRef rngCells As Range, ByVal dblFind_Total As Double) As String
Not strictly necessary, but just for clarity.
Please let me know when you have tried the code, and report any feedback.
Thanks.
BFN,
fp.
Sorry... I forgot to declare the data type of the return from the main function.
Please replace:
Public Function Find_Total(ByRef rngCells As Range, ByVal dblFind_Total As Double)
With
Public Function Find_Total(ByRef rngCells As Range, ByVal dblFind_Total As Double) As String
Not strictly necessary, but just for clarity.
Please let me know when you have tried the code, and report any feedback.
Thanks.
BFN,
fp.
ASKER
I am very impressed the only problem we have is that if the cells contain the following and our required answer is £400, the results is: ANS: B2 and B3 = 350. Instead of ANS: B1 B2 and B3 = 400
B
1 100
2 100
3 250
4 200
If I change the required answer to £450 then this gives me ANS: B3 and B4 =450. Is it also possible to make this work on non-adjacent cells? For example only use B1 B2 and B4.
I must reitterate how impressed I am with this already, I dare not imagine how long it would have taken me to try and come up with something that would come even anywhere near performing what I wanted.
Another excellent point I must make is that it was nice to be able to just paste the code across and it work first time.
Thanks
Lianne
B
1 100
2 100
3 250
4 200
If I change the required answer to £450 then this gives me ANS: B3 and B4 =450. Is it also possible to make this work on non-adjacent cells? For example only use B1 B2 and B4.
I must reitterate how impressed I am with this already, I dare not imagine how long it would have taken me to try and come up with something that would come even anywhere near performing what I wanted.
Another excellent point I must make is that it was nice to be able to just paste the code across and it work first time.
Thanks
Lianne
Hi Lianne,
Thanks for your comments. It's equally as rewarding to be appreciated! :)
Yes, I think you've found an issue I was trying to describe.
As the code just extracts one combination of cells that are equal to, or less than the desired total, the it will return "correct" results, but as above, not the "best correct" result [if that makes sense].
That is, it will return what you asked it to do, but not necessarily the best match.
What I proposed is that the code could be amended to search all combinations possible and then find the closest match to the required total (or the exact total, if possible) before reporting back into the formula cell.
Please leave this with me for a while, and I'll come back with a revised version.
How soon did you need a working solution, btw?
BFN,
fp.
Thanks for your comments. It's equally as rewarding to be appreciated! :)
Yes, I think you've found an issue I was trying to describe.
As the code just extracts one combination of cells that are equal to, or less than the desired total, the it will return "correct" results, but as above, not the "best correct" result [if that makes sense].
That is, it will return what you asked it to do, but not necessarily the best match.
What I proposed is that the code could be amended to search all combinations possible and then find the closest match to the required total (or the exact total, if possible) before reporting back into the formula cell.
Please leave this with me for a while, and I'll come back with a revised version.
How soon did you need a working solution, btw?
BFN,
fp.
Sorry... missed your query regarding non-adjacent cells.
In your example, just B1, B2, B4 could be specified as follows:
Define a "named" range.
"Insert" menu -> "Name" -> "Define..."
Type MY_RANGE into the first field, then using the cell/range selection icon to the far right at the bottom of the dialog box, select the cells you need.
For example:
MY_RANGE
.
.
.
.
.
=Sheet1!B1,Sheet1!B2,Sheet 1!B4
Now click "Add", then "OK".
Edit the existing formula to now read:
=Find_Total(MY_RANGE, 450)
(or whatever)
Hope that helps.
BFN,
fp.
In your example, just B1, B2, B4 could be specified as follows:
Define a "named" range.
"Insert" menu -> "Name" -> "Define..."
Type MY_RANGE into the first field, then using the cell/range selection icon to the far right at the bottom of the dialog box, select the cells you need.
For example:
MY_RANGE
.
.
.
.
.
=Sheet1!B1,Sheet1!B2,Sheet
Now click "Add", then "OK".
Edit the existing formula to now read:
=Find_Total(MY_RANGE, 450)
(or whatever)
Hope that helps.
BFN,
fp.
ASKER
Hello again,
NON-ADJACENT cells - that works great. I have created a lookup range that I can just amend as my data and fields change (i.e. when I take values out of the range, after I have used your functions to get a match).
-- What I proposed is that the code could be amended to search all combinations possible and then find the closest match to the required total (or the exact total, if possible) before reporting back into the formula cell.
Sorry I misinterpreted this previously, I did want the best match if possible. There is no great rush for this as I have a number of other bits and pieces to finish on the workbook.
Thanks again
Lianne
NON-ADJACENT cells - that works great. I have created a lookup range that I can just amend as my data and fields change (i.e. when I take values out of the range, after I have used your functions to get a match).
-- What I proposed is that the code could be amended to search all combinations possible and then find the closest match to the required total (or the exact total, if possible) before reporting back into the formula cell.
Sorry I misinterpreted this previously, I did want the best match if possible. There is no great rush for this as I have a number of other bits and pieces to finish on the workbook.
Thanks again
Lianne
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
You must be a genius!!
Thats brilliant, thanks a lot. It works a treat and does exactly what I want. Thanks for all your help.
Lianne
PS: -- * The actual "ANS:" should be B1, B2 and B4 (not ...B3 as you wrote - presumably just a typo)!
Yes, that was a typo, my maths are not quite that bad!! (I hope)
Thanks again.
Thats brilliant, thanks a lot. It works a treat and does exactly what I want. Thanks for all your help.
Lianne
PS: -- * The actual "ANS:" should be B1, B2 and B4 (not ...B3 as you wrote - presumably just a typo)!
Yes, that was a typo, my maths are not quite that bad!! (I hope)
Thanks again.
You're very welcome.
Nope - not a genius... yet... but ever hopeful! :)
(and also prone to typos!)
Thanks for the grading/points.
BFN,
fp.
Nope - not a genius... yet... but ever hopeful! :)
(and also prone to typos!)
Thanks for the grading/points.
BFN,
fp.
ASKER
Thanks for your speedy responses, for the intial problem and the mods. I have posted some extra points for you at: -
https://www.experts-exchange.com/questions/20865279/Points-for-fanpages.html
Thanks again.
Lianne
https://www.experts-exchange.com/questions/20865279/Points-for-fanpages.html
Thanks again.
Lianne
That's very kind of you.
I appreciate the gesture.
Thank you :)
Happy codin'.
BFN,
fp.
I appreciate the gesture.
Thank you :)
Happy codin'.
BFN,
fp.
i am looking for something similar to this but i do not understand how to get the macro to work. I have a list of values in column A and I want to see which ones add up to total. Could someone help me understand the macro.
Thansks,
Montrof
Thansks,
Montrof
Also my list has negative numbers in it, does this function take that into consideration.
Are you looking for the first combination that can reach the answer, or all combinations of cells in the range? (B2:B6, in your above example)?
That is, if the function were to offer B3, B4 and B5, but another combination (but not from the figures above) was B2, B4, B6, would you like both these to be "returned" as a result?
What is your preference... a list of numbers with a deficit of 1, or an exact match that takes longer to produce?
If the range to search was relatively lengthy (e.g. 2000 cells) then the number of potential results could be quite extensive (and consequently the duration of time taken to produce these results could be too long for your purposes).
Please clarify/advise further.
Thanks.
BFN,
fp.