iT邦幫忙

1

Excel VBA 如何將機台貨物數量平均分配到每個人(已解決)

  • 分享至 

  • xImage

https://ithelp.ithome.com.tw/upload/images/20221207/20150851OdPFuvovOD.jpg
補充一個問題點,在分配貨物的同時間,機台編號也會同時記錄起來。

看更多先前的討論...收起先前的討論...
這不是單純 VBA 不 VBA 的問題
我覺得已經有點 DP 的味道
追蹤等真●高手來回答
win895564 iT邦新手 1 級 ‧ 2022-12-08 08:45:13 檢舉
想請教一下 機台的貨物數量是預先知道嗎
vivianss iT邦新手 5 級 ‧ 2022-12-08 09:26:54 檢舉
不好意思,海綿寶寶,什麼是DP?剛查了一下,意指是『動態規畫』對嗎?
win895564 機台的貨物數量都是已知的。
blanksoul12 iT邦研究生 5 級 ‧ 2022-12-08 10:12:52 檢舉
數學不好,等高手
blanksoul12 iT邦研究生 5 級 ‧ 2022-12-08 10:15:39 檢舉
這樣也可,但何為合理排法...
1 12 10
2 5
3 6
vivianss iT邦新手 5 級 ‧ 2022-12-08 10:34:10 檢舉
blanksoul12 也可以喔!主要是接近平均分配。
aaron3399 iT邦好手 1 級 ‧ 2022-12-08 13:28:45 檢舉
你平常都怎麼排的呢? 有沒有甚麼SOP?
是有70~90台機台要排列嗎?
這需要排序演算法...
vivianss iT邦新手 5 級 ‧ 2022-12-08 14:15:46 檢舉
aaron3399 主要是把總貨物數先除人數之平均分配後,再一一的比對比較接近的值先排,當然這期間可能會有誤差(+-)20(越小越好),而最後一個會因前面的排列(+-)20,而未達標,這沒關係的。至少前面都是在平均值。如A分到貨物600,B分到貨物580,C分到590,D分到600,最後一個分到320。
blanksoul12 iT邦研究生 5 級 ‧ 2022-12-09 09:06:00 檢舉
很多問題在內
1. 最好大家差不多件數,但宗數有要差不多嗎?
2. 每個機台最大和最少差多少? 會不會有 1 同 100 同時出現, 那很難分配.
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中
5
paicheng0111
iT邦大師 5 級 ‧ 2022-12-08 18:15:47
最佳解答

用excel內建的「規劃求解」功能即可,不用寫vba。

大致如下圖。

B17格的公式為=VAR.P(D13:F13)
D13格的公式為=SUMPRODUCT($B$2:$B$8,D2:D8)
C12格的公式為=SUM(D2:F2)

看更多先前的回應...收起先前的回應...

今天又學到新知識了
/images/emoticon/emoticon12.gif

野人獻曝了

aaron3399 iT邦好手 1 級 ‧ 2022-12-08 23:12:52 檢舉

太厲害了~

vivianss iT邦新手 5 級 ‧ 2022-12-09 00:07:19 檢舉

感謝大神幫忙解答,目前卡在少了solver32.dll,沒辦法執行!謝謝你們!辛苦了。

檔案\選項\增益集

管理(A):Excel增益集 點「執行」

規劃求解增益集打勾,再點「確定」

vivianss iT邦新手 5 級 ‧ 2022-12-11 05:50:24 檢舉

paicheng0111 在家可以用,但公司的就…,感謝您。

0
froce
iT邦大師 1 級 ‧ 2022-12-08 15:05:04

不要叫我用VBA寫,然後這是我想到最好的演算法了,有其他更好的歡迎盡量打臉。
https://colab.research.google.com/drive/10RJpfaaXZpqOxGk7awbiJBmqmwrOklcV?usp=sharing

data = {
	1:12,
	2:15,
	3:16,
	5:8,
	6:10,
	10: 12,
	12: 2
}

persons = ['A', 'B', 'C']

avg = sum(data.values())/len(persons)
# 對工作量排序,工作量從大到小的機台號碼
sortedIndexs = sorted(data, key=lambda x:data[x], reverse=True)

result = {}

# 計算當前工作量
def sumTaskLoads(tasks):
  return sum([task[1] for task in tasks])

for p in persons:
  # 防爆屏障
  if not sortedIndexs:
    break
    
  # 先把工作量最大的存起來
  tempTasks = [(sortedIndexs[0], data[sortedIndexs[0]])]
  # 刪除已完成派工的機台號碼
  del sortedIndexs[0]
  
  sumTemp = sumTaskLoads(tempTasks)
  # 當目前被派工的人,工作量還沒超過平均,且還有機台沒完成派工時,持續派工
  # 會先取1項,讓總和加此項工作,離平均值差最小
  while sumTemp < avg and sortedIndexs:
    # 計算目前與平均值差異
    distFromAvg = [(i, abs(avg- sumTemp - data[i])) for i in sortedIndexs]
    # 找出最小差距的機台號碼
    minDistTask = sorted(distFromAvg, key=lambda x: x[1])[0]
    # 存入機台號碼、工作量,並刪除已完成派工的機台號碼
    tempTasks.append((minDistTask[0], data[minDistTask[0]]))
    sortedIndexs.remove(minDistTask[0])
    sumTemp = sumTaskLoads(tempTasks)
  result[p] = tempTasks

print(result)
# {'A': [(3, 16), (6, 10)], 'B': [(2, 15), (1, 12)], 'C': [(10, 12), (5, 8), (12, 2)]}

要產出xlsx麻煩自己加openpyxl去輸出。
這年頭不是只有VBA可以處理xlsx。

看更多先前的回應...收起先前的回應...

實務上可能不會發生
純粹是公堂之上做的假設
1.第一筆改大值 120
結果正確(平均值61)
https://ithelp.ithome.com.tw/upload/images/20221208/200017876giBEPPUOe.jpg
2.壓力測試(第一筆改大值 1200)就「爆」了
https://ithelp.ithome.com.tw/upload/images/20221208/20001787d3lC0KMxVn.jpg

froce iT邦大師 1 級 ‧ 2022-12-08 15:27:00 檢舉

會爆正常啦。第一項就大過其他總和了...
這就算不爆,第三個人也沒辦法派到工。


防爆屏障加好了,不會爆了。

froce iT邦大師 1 級 ‧ 2022-12-08 16:04:09 檢舉
data = {
	1:12,
	2:15,
	3:16,
	5:8,
	6:10,
	10: 12,
	12: 2
}

persons = ['A', 'B', 'C']

# 複製一份人員清單供動態改變
personsCopy = persons[:]

sortedIndexs = sorted(data, key=lambda x:data[x], reverse=True)

result = {}

# 計算剩餘工作
def sumOfRemainingTasks(sortedIndexs):
  result = 0
  for i in sortedIndexs:
    result += data[i]
  return result

def sumTaskLoads(tasks):
  return sum([task[1] for task in tasks])

for p in persons:
  if not sortedIndexs:
    break
  
  # 根據剩下的工作量算平均值
  avg = sumOfRemainingTasks(sortedIndexs)/len(personsCopy)
  tempTasks = [(sortedIndexs[0], data[sortedIndexs[0]])]
  del sortedIndexs[0]

  sumTemp = sumTaskLoads(tempTasks)
  while sumTemp < avg:
    distFromAvg = [(i, abs(avg- sumTemp - data[i])) for i in sortedIndexs]
    minDistTask = sorted(distFromAvg, key=lambda x: x[1])[0]
    tempTasks.append((minDistTask[0], data[minDistTask[0]]))
    sortedIndexs.remove(minDistTask[0])
    sumTemp = sumTaskLoads(tempTasks)
  result[p] = tempTasks
  personsCopy.remove(p)

# 把沒派到的工作都丟給最後一個
if sortedIndexs:
  remainingTasks = []
  for i in sortedIndexs:
    remainingTasks.append((i, data[i]))
  result[persons[-1]] = result[persons[-1]] + remainingTasks

print(result)
# {'A': [(3, 16), (6, 10)], 'B': [(2, 15), (5, 8), (12, 2)], 'C': [(1, 12), (10, 12)]}

改了一版,應該會比前面的好。
這版有考慮到平均值應該根據前面的派工結果改變,所以現在A就算被派了一項大數,B、C兩個都會盡量平均派工。

re.Zero iT邦研究生 5 級 ‧ 2022-12-08 18:27:06 檢舉

搞不好是工作環境的問題導致 vivianss 只能用 VBA (我就遇過在這種麻煩的神奇狀況下被要求用 VBA + Excel 寫員工業務管制與教育系統,當時覺得根本 #@$%……);反正有提供方法,剩下的他應該能看著辦吧~(?

vivianss iT邦新手 5 級 ‧ 2022-12-09 00:03:50 檢舉

re.Zero 你猜對了,公司的電腦不能安裝其他軟體,所以只能硬著操作。

froce iT邦大師 1 級 ‧ 2022-12-09 08:03:36 檢舉

只要不是還是XP的電腦,python可以打包成exe去執行,不用安裝。
另外這種情形可以去向公司申請權限、用其他台電腦、或用web環境如colab還是ideone這種網站執行,有很多方法可以解決,也沒必要一定都要自動輸出,光是你不用自己靠眼力排你就能省很多工。

免費的諮詢能給你到解決關鍵問題就已經不錯了,剩下的請用錢來解決。
找人改寫成golang加輸出應該也還好,去學校找個學生也能改。
要用VBA寫光dict排序就很頭大了...

re.Zero iT邦研究生 5 級 ‧ 2022-12-10 13:40:18 檢舉

froce 提出很多好方法,就只能祈禱 vivianss 待的不是很硬的環境了。(各種公司 + 各種人 就能見到各種世界奇觀~!

0
海綿寶寶
iT邦大神 1 級 ‧ 2022-12-08 15:10:33

正確,我指的 DP 是動態規劃

寫好了
1.執行前
https://ithelp.ithome.com.tw/upload/images/20221208/20001787rMVqWXJI5U.jpg
2.執行VBA後(顯示數量)
https://ithelp.ithome.com.tw/upload/images/20221208/20001787uyXh8EdYla.jpg
3.執行VBA後(顯示機台號碼)
https://ithelp.ithome.com.tw/upload/images/20221208/20001787O6fy7ZxATR.jpg
4.用100台機台來測試
https://ithelp.ithome.com.tw/upload/images/20221208/20001787WvLrPy5p7W.jpg
5.執行結果如下(顯示數量,總計是後來手動加上去比較好看)
https://ithelp.ithome.com.tw/upload/images/20221208/20001787NOtUcqO5ko.jpg
6.100的機台號碼
https://ithelp.ithome.com.tw/upload/images/20221208/20001787pGbJoMi2SD.jpg

看更多先前的回應...收起先前的回應...
froce iT邦大師 1 級 ‧ 2022-12-08 15:23:31 檢舉

我上面的code和你的想法基本上是一樣的。
只是這個叫我用VBA寫,我真的辦不到。XD

這個要真的盡量公平只能用動態規劃。

剛剛我試出來一個暴力解決法
程式寫好再 po 上來

寫好了
我跟你剛好相反
我跟 python 很不熟
當成 basic 來寫
/images/emoticon/emoticon25.gif

def assign(amt):
    avg = (sum(emp1)+sum(emp2)+sum(emp3)+amt) / 3
    delta1 = (sum(emp1)+amt) - avg
    delta2 = (sum(emp2)+amt) - avg
    delta3 = (sum(emp3)+amt) - avg
    if delta1 <= delta2 and delta1 <= delta3:
        emp1.append(amt)
    elif delta2 <= delta1 and delta2 <= delta3:
        emp2.append(amt)
    elif delta3 <= delta1 and delta3 <= delta2:
        emp3.append(amt)
    
data = [16,15,12,12,10,8,2]

emp1 = []
emp2 = []
emp3 = []

for amt in data:
    assign(amt)
    
print(sum(emp1),emp1)
print(sum(emp2),emp2)
print(sum(emp3),emp3)

簡單說就是
把貨物數逐一指定給每個人,並試算「所有人的總數跟平均數的差異總合」
看那個人的「差異總合」最小
就指派給那個人

vivianss iT邦新手 5 級 ‧ 2022-12-09 00:09:00 檢舉

謝謝大家的幫忙,你們真棒!

1
ccenjor
iT邦高手 1 級 ‧ 2022-12-09 21:25:33

E2:
=IF(SUM($D2:D2)=$B2,"",IF(SUM(E$1:E1)+$B2<=E$15,$B2,""))
將公式向右及向下複製到E2:G13。
以E15:G15來管控。
https://ithelp.ithome.com.tw/upload/images/20221209/20109881haPROWGYe3.jpg

不用規劃求解
不用VBA
只用SUM一個函數就做到
連INDEX/MATCH都不用
真●Excel高手
/images/emoticon/emoticon32.gif

ccenjor iT邦高手 1 級 ‧ 2022-12-10 09:50:37 檢舉

海綿大神,若論VBA我只能向您仰望。
(◉ω◉)

0
mapper
iT邦見習生 ‧ 2023-05-09 20:21:30

sub 簡單六分法分配()
set s1=activesheet
sheets.add before:=sheets(1)
set s2=sheets(1)
s1.columns("a:b").copy destination:=s2.cells(1,1)
s2.select
i9=cells(65536,1).end(xlup).row
cells(1,3)="撿貨員"
set r2=columns("a:c")
r2.sort key1=cells(1,2),Order1:=xldescending,header:=xlyes
for i=2 to i9 step 6
k1=k1 +cells(i,2)+cells(i+5,2):cells(i,3)=1:cells(i+5,3)=1
k2=k2+cells(i+1,2)+cells(i+4,2):cells(i+1,3)=2:cells(i+4,3)=2
k3=k3+cells(i+2.2)+cells(i+3,2):cells(i+2,3)=3:cells(i+3,3)=3
next i
r2.sort key1=cells(1,3),Order1:=xlascending,header:=xlyes
msgbox k1 & "," & k2 & "," & k3
end sub

我要發表回答

立即登入回答