kore5657 wrote:
不好意思...我忘了還有一個很重要的條件
我的問題中並不用考慮組合的順序...
例如:
100 = 8+8+8+8+8+8+8+8+8+8+10+10
不管組合內的數字怎麼替換順序,都只算一種
這是可重複選取的不重複組合公式
而我原來的範例是不可重複選取的不重複組合公式
不重複組合、重複組合,雖然公式不同
但在我寫的vba範例中,計算用的程式碼,只有差在其中一個變數 +1 改成 +0
請自行和4樓舊版程式碼比較看看有何不同
測試:7取1=>7取2=>…7取15,有170543種組合,加總100的365組,約0.9秒可計算完
Sub test()
Dim data(), targetsum() As Double, item_name() As String, lastrow As Integer, ttt As Double
Dim total As Long, i As Integer, ok As Integer, target As Double, limited As Integer, maxcombin As Integer, start As Integer, startend As Integer, tolerance As Double, rs As Boolean
Columns("E:Z").Clear
'esc中斷功能,計算極大量資料時無效,程式會來不及回應
On Error GoTo Esc_Stop
Application.EnableCancelKey = xlErrorHandler
ttt = Timer
lastrow = Range("A2").End(xlDown).Row
ReDim data(1 To 2, 1 To lastrow - 1)
For d = 1 To lastrow - 1
data(1, d) = Cells(d + 1, 2).Value
' data(2, d) = Cells(d + 1, 1).Value
Next d
If Range("C1") = 0 Then End
target = Range("C1"): tolerance = Range("C2"): limited = Range("C3"): maxcombin = Range("C4")
Range("C5") = "": Range("C8") = "": Range("E1") = Format(Now(), "hh:mm:ss")
If Range("c6") = "" Then
Range("c6") = False
Range("c7") = ""
End If
If Range("c6") = True And Range("c7") = "" Then Range("c7") = 15
rs = Range("c6")
If limited = 0 Then
start = 1
If rs = True Then startend = Range("c7") Else startend = UBound(data, 2)
Else
start = limited: startend = start
End If
For i = start To startend
ReDim targetsum(1 To i)
' ReDim item_name(1 To i)
Call findCombin(data, targetsum, item_name, i, total, 1, 1, ok, target, limited, maxcombin, tolerance, rs)
If start <> startend Then Range("C8") = Int((i / startend * 100) + 0.5) & "%請耐心等待"
Next i
If ok <> 0 Then Call find_name
Range("C8") = "100%計算結束": Range("E2") = Format(Now(), "hh:mm:ss")
Debug.Print total, Timer - ttt
Exit Sub
Esc_Stop:
If Err = 18 Then
MsgBox "stop"
End If
End Sub
Sub findCombin(data(), targetsum() As Double, item_name() As String, j As Integer, total As Long, k As Integer, n As Integer, ok As Integer, target As Double, limited As Integer, maxcombin As Integer, tolerance As Double, rs As Boolean)
Dim i As Integer, tempsum As Double
For i = k To UBound(data, 2)
targetsum(n) = data(1, i): tempsum = 0
' item_name(n) = data(2, i)
If n = j Then
total = total + 1
For s = 1 To UBound(targetsum)
tempsum = tempsum + targetsum(s)
Next s
If tempsum = target Then '把變數tolerance代入這一行,就可增加誤差值功能
ok = ok + 1
Range("G" & ok).Resize(1, j) = targetsum
' Range("F" & ok) = Join(item_name, ",")
If (ok = maxcombin And maxcombin <> 0) Then
Range("C5") = ok: Range("C8") = "100%計算結束": Range("E2") = Format(Now(), "hh:mm:ss"): Debug.Print total
If ok <> 0 Then Call find_name
End
End If
Range("c5") = ok
End If
Else
Call findCombin(data, targetsum, item_name, j, total, i + IIf(rs, 0, 1), n + 1, ok, target, limited, maxcombin, tolerance, rs)
End If
Next i
End Sub
Sub find_name()
Dim lastrow As Integer, lastcol As Integer, n As Object, name_data() As String
Set n = CreateObject("Scripting.Dictionary")
Application.ScreenUpdating = False
lastrow = Range("A1").End(xlDown).Row
For i = 2 To lastrow
n.Add Str(Cells(i, 2)), Cells(i, 1)
Next i
For i = 1 To Range("c5").Value
lastcol = Range("G" & i).End(xlToRight).Column
ReDim name_data(1 To lastcol - 6)
For j = 7 To lastcol
name_data(j - 6) = n(Str(Cells(i, j)))
Next j
Cells(i, 6) = Join(name_data, ",")
Next i
Set n = Nothing
Application.ScreenUpdating = True
End Sub
[點擊下載]
誤差值功能保留,想要請自行練習修改