• 5

(6/28 小更新)Excel多組數字挑選加總為指定數值的方法(vba 範例)

在這篇看到,材料長度、需求數量、最佳解的問題
https://www.mobile01.com/topicdetail.php?f=511&t=6656360

其實這也是排列組合的一種,只取1解
(不代表一定只有1解,所以可能不是最佳解)
不需要太多計算,算是簡化版的排列組合
所以程式結構,跟上面幾樓的範例很像,只是沒那麼複雜

以下是簡單改寫後的範例









'使用方式,a欄填長度,b欄填各長度總數量,c2格填單個材料長度

Sub find_combin_Length()

Dim Data() As Double, Find_Answer() As Double, Temp_Length As Double, Min_Length As Double, ttt As Double
Dim i As Long, j As Long, k As Long, LastRow As Long, temp As Double, temp1 As Double, Total As Double, tempSum As Double
Dim Original_Length As Double, Original_Data As Range, Check As Boolean, Check1 As Double

If WorksheetFunction.CountA(Sheets("工作表1").Range("a:a")) = 1 Then Exit Sub

ttt = Timer
Sheets("工作表1").Range("e:s").ClearContents
Sheets("工作表1").Range("e:s").ColumnWidth = 13
LastRow = WorksheetFunction.CountA(Sheets("工作表1").Range("a:a"))
Set Original_Data = Sheets("工作表1").Range("A2").Resize(LastRow - 1, 2)
Original_Length = Sheets("工作表1").Range("c2")
ReDim Data(LastRow - 2, 1)

For i = 0 To UBound(Data, 1)
For j = 0 To UBound(Data, 2)
Data(i, j) = Original_Data.Cells(i + 1, j + 1)
Next j
Next i

For i = 0 To UBound(Data, 1) - 1
For j = i + 1 To UBound(Data, 1)
If Data(i, 0) < Data(j, 0) Then
temp = Data(j, 0)
temp1 = Data(j, 1)
Data(j, 0) = Data(i, 0)
Data(j, 1) = Data(i, 1)
Data(i, 0) = temp
Data(i, 1) = temp1
End If
Next j
Next i

If Data(0, 0) > Original_Length Then
MsgBox "長度超過原材料", vbOKOnly, "Error"
Exit Sub
End If

i = 0: k = 0: Check1 = 1: Min_Length = Data(UBound(Data), 0): Temp_Length = Original_Length

Do While Check1 > 0
Do While Temp_Length >= Min_Length
If Data(i, 0) <= Temp_Length And Data(i, 1) > 0 Then
Data(i, 1) = Data(i, 1) - 1
Temp_Length = Temp_Length - Data(i, 0)
ReDim Preserve Find_Answer(1, k)
Find_Answer(0, k) = Total + 1: Find_Answer(1, k) = Data(i, 0)
k = k + 1
Else
i = i + 1
End If

Check = True
For j = LBound(Data) To UBound(Data)
If Data(j, 1) > 0 Then
Min_Length = Data(j, 0)
Check = False
End If
Next j
If Check Then Exit Do
Loop

Temp_Length = Original_Length: Total = Total + 1

For j = UBound(Data) To LBound(Data) Step -1
If Data(j, 1) <> 0 Then
i = j
End If
Next j

Check1 = 0
For j = LBound(Data) To UBound(Data)
Check1 = Check1 + Data(j, 1)
Next j
Loop

j = 1: k = 0
For i = 0 To UBound(Find_Answer, 2)
If Find_Answer(0, i) <> j Then
Sheets("工作表1").Cells(1, j + 5) = "第" & j & "組" & vbNewLine & "合計=" & tempSum & vbNewLine & "尾料=" & Original_Length - tempSum 'debug
j = j + 1: k = 0: tempSum = 0
End If
Sheets("工作表1").Cells(k + 2, Find_Answer(0, i) + 5) = Find_Answer(1, i)
tempSum = tempSum + Find_Answer(1, i)
k = k + 1
Next i

Sheets("工作表1").Range("e1") = "最少需要" & vbNewLine & Total & "個"
Sheets("工作表1").Cells(1, j + 5) = "第" & j & "組" & vbNewLine & "合計=" & tempSum & vbNewLine & "尾料=" & Original_Length - tempSum 'debug
Sheets("工作表1").Range("e2") = Timer - ttt & "s" 'deubg

End Sub







[點擊下載]
我有看之前提到的用23個數字組成7143的範例
算這種問題用EXCEL內建規劃求解,選單純LP,大約十幾秒(據說收費版速度快幾十倍)

LINDO SYSTEM 的what's best 程式也可以去下載,按下去約一秒就解答
因為我的EXCEL有裝

所以我說句不中聽的話,還是用套裝就好,自已寫程式應該不會比專業寫程式的好



snare
snare 樓主

另外,看了您的歷史發文,都是新聞、政治類的,怎麼突然出現在不曾來過的文書處理區,您是帳號本人嗎?

2023-03-21 11:07
bluejay27

我只是最近在研究這個而已,哈哈,就東找西找

2023-03-21 11:14
open solver 免錢的,也是一秒解



snare
snare 樓主

您把姓名也貼出來了,不知您會不會介意,但我認為塗掉比較好喔。

2023-03-21 11:42
bluejay27

塗掉了,感謝提醒

2023-03-21 11:51
感謝大大的蓋樓以及持續追蹤,這巨集幫了我很大的忙
後面與高手切磋後的高速運算法也是非常驚豔,受益良多

不過我發現沒有解的時候,他會一直跑XD
所以我有寫個簡單的判斷式去處理他
另外有個小困擾,不知為啥常常數字少貼一個,有時候卻不會 (尤其解很少的時候)
snare
snare 樓主

謝謝,能把不完整的教學文,自行改成實際應用,您也是相當厲害的高手

2023-07-29 21:16
1.85
1.05

目標2.9
想問問為什麼這樣都跑不出來

先謝謝兩位大神了,
受益良多
snare
snare 樓主

變數類型的關係,預設是算整數,小數點需修改程式碼,1樓有說明。

2023-11-09 6:14
MinShane

第一時間有修改成Long 不過其他的小數點目標都跑得出來,不知道為什麼只有這個目標和組合有問題

2023-11-09 14:11
MinShane wrote:
第一時間有修改成Long 不過其他的小數點目標都跑得出來,不知道為什麼只有這個目標和組合有問題


我開新檔,手動輸入數值,是可以正常算出來的



也許是您原始數值、excel設定、儲存格格式…等等的設定,剛好觸發了excel特有的問題
就是加總會誤差0.1、0.0001,您看到是2.9,excel實際上也許是2.8999
所以此組合條件不成立

微軟官方說明
https://learn.microsoft.com/zh-tw/office/troubleshoot/excel/floating-point-arithmetic-inaccurate-result

想看更多奇怪的例子,請google
"excel 小數點 加總 錯誤值 差 0.1"
MinShane

應該是這個原因 感謝 找了很久的原因找不到= =

2023-11-14 15:29
snare wrote:
ren1244的教學


又是我!
請問30樓ren1244大大提供的下載檔案是可以支援有小數位嗎?
我試了一下,就出現下列版面



按偵錯,又出現以下版面,請問怎樣解決呢?謝!




我用的例子,就是以上圖中的數字,目標值是 39,897.49

---------------------------------------------------------
p.s. 之前一直是用樓主的程式,雖然不能支援較多數值,但如果不超過A-Z的話,還是可以運作的。
但最近樓主這程式不能運作,不知為何,即使我將組合加到目標數值,還是沒有運作,如下圖:

pretty_woman wrote:
即使我將組合加到目標數值,還是沒有運作


您的問題和36樓一樣,中招了
格子內看到的數值,和vba看到的是不一樣的

以我的程式碼來說
tempsum=39897.49
target=39897.49

tempsum=target,tempsum-target=0 才對

但是小數點的問題,造成vba加總的結果不同





如果您的資料很容易出現這種問題
最簡單的方式,就是在vba中限制小數點的數量












以您圖片中,11樓的範例,程式碼修改方式如下
把這行
If tempsum = target Then

改成如下
If Round(tempsum, 2) = Round(target, 2) Then



round(數值,2),是指小數點,2位數
也可用 WorksheetFunction.Round(數值,2) 代替


但要注意的是
vba的round()

excel儲存格用的函數=round()、=roundup、=rounddown()
計算出來的結果是不一樣的,通常是用round就行

不同之處,請參考微軟官方說明
https://learn.microsoft.com/zh-tw/office/vba/language/reference/user-interface-help/round-function
snare wrote:
tempsum


非常感謝!已照你的指示更改了程式,但你會知道如何更改ren1244大大的程式嗎?因我將可能有超過26個有小數點的數值需要運算,希望可以把運算速度加快一點點!
pretty_woman wrote:
如何更改ren1244大大的程式


一樣是小數點計算的問題,如果您資料不是手動輸入
常常用copy & paste ,就會莫明奇妙中招
詳細請到36樓,看微軟的官方說明







修改方式一樣,vba中限制小數點的個數
把這行
sumVal(stackIdx) = sumVal(stackIdx - 1) + data(0, idx)

改成這樣,暫訂小數2位
sumVal(stackIdx) = Round(sumVal(stackIdx - 1), 2) + Round(data(0, idx), 2)




另外發現一個bug,1解時輸出會少1個,
原來在2022-11-17 22:31(30樓),就有人提醒,只是他用“留言”
系統沒提示,因為不會沒事回頭看以前文章,我又漏看
所以沒發現到,不好意思,現在才處理








修改方式如下
把這行
Range("f2").Resize(nSolutions, maxLen) = outputData

改成
Range("f2").Resize(nSolutions, maxLen + 1) = outputData

  • 5
內文搜尋
X
評分
評分
複製連結
Mobile01提醒您
您目前瀏覽的是行動版網頁
是否切換到電腦版網頁呢?