補充一個問題點,在分配貨物的同時間,機台編號也會同時記錄起來。
用excel內建的「規劃求解」功能即可,不用寫vba。
大致如下圖。
B17格的公式為=VAR.P(D13:F13)
D13格的公式為=SUMPRODUCT($B$2:$B$8,D2:D8)
C12格的公式為=SUM(D2:F2)
不要叫我用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)
2.壓力測試(第一筆改大值 1200)就「爆」了
會爆正常啦。第一項就大過其他總和了...
這就算不爆,第三個人也沒辦法派到工。
防爆屏障加好了,不會爆了。
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兩個都會盡量平均派工。
搞不好是工作環境的問題導致 vivianss 只能用 VBA (我就遇過在這種麻煩的神奇狀況下被要求用 VBA + Excel 寫員工業務管制與教育系統,當時覺得根本 #@$%……);反正有提供方法,剩下的他應該能看著辦吧~(?
re.Zero 你猜對了,公司的電腦不能安裝其他軟體,所以只能硬著操作。
只要不是還是XP的電腦,python可以打包成exe去執行,不用安裝。
另外這種情形可以去向公司申請權限、用其他台電腦、或用web環境如colab還是ideone這種網站執行,有很多方法可以解決,也沒必要一定都要自動輸出,光是你不用自己靠眼力排你就能省很多工。
免費的諮詢能給你到解決關鍵問題就已經不錯了,剩下的請用錢來解決。
找人改寫成golang加輸出應該也還好,去學校找個學生也能改。
要用VBA寫光dict排序就很頭大了...
froce 提出很多好方法,就只能祈禱 vivianss 待的不是很硬的環境了。(各種公司 + 各種人 就能見到各種世界奇觀~!
正確,我指的 DP 是動態規劃
寫好了
1.執行前
2.執行VBA後(顯示數量)
3.執行VBA後(顯示機台號碼)
4.用100台機台來測試
5.執行結果如下(顯示數量,總計是後來手動加上去比較好看)
6.100的機台號碼
剛剛我試出來一個暴力解決法
程式寫好再 po 上來
寫好了
我跟你剛好相反
我跟 python 很不熟
當成 basic 來寫
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)
簡單說就是
把貨物數逐一指定給每個人,並試算「所有人的總數跟平均數的差異總合」
看那個人的「差異總合」最小
就指派給那個人
謝謝大家的幫忙,你們真棒!
E2:
=IF(SUM($D2:D2)=$B2,"",IF(SUM(E$1:E1)+$B2<=E$15,$B2,""))
將公式向右及向下複製到E2:G13。
以E15:G15來管控。
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