• 2

請教 - 多組數字挑選加總為指定數值的方法

感謝 snare 大及 yinhell 的指導。

最後以寫程式的方式來做這個挑選功能的實現。

以多個執行緒來產生各種組合,在電腦上一秒約可產生 100 萬個組合。
再配2個執行緒來檢查各組合是否符合所要的結果,並另行記錄兩組最接近完全符合結果的記錄。每秒也差不多可檢查 100 萬個組合。

再設個20秒的處理時間上限,若找不到完全符合結果,就傳回最接近的結果。

執行的效率比 Excel 的規劃求解只能用單核心快了許多。

大部份算出來的結果都是可挑到完全符合的,真是太神奇了。
不想唸物理了...
請問程式要怎麼寫阿
可以教我嗎
非常感謝你!!求解
LBJisgod5566 wrote:
請問程式要怎麼寫阿...(恕刪)


直接寫給您吧,這不是3言2語就能教的,有興趣的話,先去看一下統計學的排列組合
為了方便您看懂,這用迴圈式,是比較簡單的寫法
雖然這樣有點慢,但每秒還是能計算大約550萬種組合

範列:N個數字(需大於5個),取5個,在所有不重覆組合中,找出所有合計168的組合(想改成字串組合也可以)
把要排列組合的放在a欄,a1~a??
("3+2+1"=>"1+2+3"=>"2+1+3",在這個程式中,這樣算是重覆喔,所以只會列出一筆)

Sub test()
Dim data() As Variant, total As Long, ok As Long

'如果組合太多超過2,147,483,647種
'as long 要改成 as double 或是as Currency,不然會造成溢位錯誤

data = Application.Transpose(Range("A1", Range("A1").End(xlDown)))

t = Timer
For i = 1 To UBound(data)
For j = i + 1 To UBound(data)
For k = j + 1 To UBound(data)
For l = k + 1 To UBound(data)
For m = l + 1 To UBound(data)

If data(i) + data(j) + data(k) + data(l) + data(m) = 168 Then

'找到的結果,建議不要一筆一筆列出,數字多的話,會嚴重影響計算速度,請先丟陣列,最後再一次輸出
'如果只要找一筆,這裡加一行exit sub 直接跳出迴圈即可,就算2000筆,也大約1秒就有結果了
'想列出全部組合的話,a欄超過500筆,用這個慢速程式,只會等到到天荒地老…
'想看全部組合只適合數量少約300筆內,才可以用debug.print 測試看結果,或是把結果丟到儲存格裡面
'Debug.Print data(i), data(j), data(k),data(l), data(m)

ok = ok + 1
End If

total = total + 1
Next m, l, k, j, i

Debug.Print "計算時間經過" & Timer - t
Debug.Print "合計不重覆組合共" & total & "組"
Debug.Print "加總168成立共" & ok & "組"


End Sub

感謝 snare 大的程式跟解說,實在是太厲害了。

我在 #5 有寫我的程式構想。


20組數字,挑選其中來組成目標數 10000。

1. 20組數字先進行排序
516,528,561,688,715,740,797,803,820,891,1059,1077,1137,1192,1330,1372,1379,1401,1466,1467

2. 由小加到大,到第 13 組破目標值 10000。

3. 由大加到小,到第 8 組破目標值 10000。

4. 使用排列組合 20挑8,20挑9,20挑10,20挑11,20挑12,20挑13 的各種來嘗試達到目標數。總共有 850,136 種組合。


最後就是用程式來做這個排列組合 20挑8,20挑9,20挑10,20挑11,20挑12,20挑13 的各種來嘗試達到目標數10000。


不想唸物理了...
wenwenwen wrote:
我在 #5 有寫我的程式構想。

2. 由小加到大,到第 13 組破目標值 10000。 ...(恕刪)


我有用您的構想試寫一下,因為由小加到大、由大加到小,很好寫
先排序+一個迴圈就解決了

但發現一個問題

範例,找總合15,由小加到大(1 3 5 7 9 )
1+3+5=9
1+3+5+7=16
有發現到了嗎??
由小加到大,您可以找到 9 跟 16 ,就是找不到15
但 3+5+7=15,是存在的

如果加總時,沒有剛剛好加到所要的數值時,只能找到最接近的

解決方式就是
一、隨意減二個值 或更多
二、再把減掉的,加總
三、搜尋數列中,減掉的加總,有沒有這個數值
四、如果沒有,再回到一,減更多

因判斷的結構,不容易說明,所以最後用迴圈的方式解釋

以下這是用您的構想寫的,您參考看看吧
Sub test()

Set data = CreateObject("system.collections.arraylist")

For i = 1 To Range("A1").End(xlDown).Row
data.Add Cells(i, 1).Value
Next

data.Sort
'由小排序到大
'這裡多加一行 'data.Reverse ,可變成大排到小

For j = 0 To data.Count - 1
total = total + data(j)
If total = 10000 Then
For k = 0 To j
Debug.Print data(k),
Next
Exit Sub
end if

'if total >10000 then
'如果照順序加沒有結果,在這裡,要加程式碼另外處理
'end if


Next

End Sub

至於上一個迴圈那個範例,您想取幾個
就多加幾個迴圈就可以了,6個就6層,7個就7層

====================================
補充另一種方式,找出"所有可能",結果可能是1個數字,也可能是10個數字的組合 或 更多組數的組合
不過,超級慢~~~~,如果超過100個數字,不想只找一組,想看全部組合
還是別用了,基本上跑不完的…

Sub test()
Dim data As Variant, targetsum As Variant, total As Long, i As Long, ok As Long
data = Application.Transpose(Range("A1", Range("A1").End(xlDown)))
Columns("b:z").Clear

For i = 1 To UBound(data)
ReDim targetsum(1 To i)
Call findCombin(data, targetsum, i, total, 1, 1, ok)
Next i

Debug.Print "總共" & total & "種不重覆組合"
Debug.Print "合計10000共" & ok & "種組合"

End Sub

Sub findCombin(data As Variant, targetsum As Variant, j As Long, total As Long, k As Long, n As Long, ok As Long)
Dim i As Long

For i = k To UBound(data)

targetsum(n) = data(i)

If n = j Then
total = total + 1
'設定目標值(預設10000)
If Application.WorksheetFunction.Sum(targetsum) = 10000 Then

ok = ok + 1
Range("c" & ok).Resize(1, j) = targetsum
If ok = 1 Then End '找到一組就結束,整行刪掉就是找全部,也可以自行設定要幾組

End If


Else
Call findCombin(data, targetsum, j, total, i + 1, n + 1, ok)
End If
Next i
End Sub
wenwenwen wrote:
公司的產品,因生產...(恕刪)

蠻有趣的問題,好像很多公司都會遇到

話說,真的不能標準化每包的數量嗎?

不然就是最後那包重新包裝 湊齊數量就好了。

snare wrote:
我有用您的構想試寫...(恕刪)

其實他是什麼?我完全不明白!!
Is excel方程式來的嗎?
  • 2
內文搜尋
X
評分
評分
複製連結
Mobile01提醒您
您目前瀏覽的是行動版網頁
是否切換到電腦版網頁呢?