我有一个项目列表,其中包含几个月的消费时间:
Hours Aug Sep Oct Nov Dec
100 20 20 20 20 20
200 40 40 40 40 40
300 60 60 60 60 60
600 120 120 120 120 120
有些项目将在不同的月份结束,因此在接下来的几个月内不得标记任何一小时(必须为0):
Hours Aug Sep Oct Nov Dec End
100 33.3 33.3 33.3 0 0 Oct
200 50 50 50 50 0 Nov
300 60 60 60 60 60 Dec
600 143.3 143.3 143.3 110 60
然而,我们必须保持每月20%的比例(20%* 600 = 120.我已经把20%,因为我们有5个月,但可能是不同的百分比),所以:
Hours Aug Sep Oct Nov Dec End
100 20 30 50 0 0 Oct
200 60 50 30 60 0 Nov
300 40 40 40 60 120 Dec
600 120 120 120 120 120
我这里有类似Sudoku的问题,我需要尊重列的比例并保持每个项目的行总和。我已经尝试过多种方式(VBA或函数)进行此分发,但到目前为止我都失败了。我相信有人之前已经解决了这个问题,那么有没有办法以编程方式进行此分发?这种分布有名称吗?
答案 0 :(得分:2)
我不是百分百肯定我的问题是对的,但希望是的,因为我花了很多时间。它实际上看起来很容易,但实际上非常棘手。
所以我们假设我们有这个表:
现在,如果我做对了,我们基本上想要计算,我们需要多少小时 已经用完了,现在我们要分发我们剩下的东西了 取决于未使用的(空)月
所以,例如,在第3行(100小时),我们不想分发任何小时,因为我们已经用完100小时中的所有100小时
在下一行(4)中,我们希望将115小时(200-85)分发给剩余的2个单元格=>这将使我们每个月有57.5小时的轮班离职
等等......
根据推定,这就是你想要的算法:
Private Sub divide_time()
Dim tbl As ListObject: Set tbl = Sheets("Sheet1").ListObjects("Table1")
Dim hour_dist() As Integer
ReDim hour_dist(1 To 3)
' first we need to learn how many hours total we have available per project
For i = 1 To 3
With tbl.ListColumns(1)
hour_dist(i) = .Range(i + 1) ' we store each value into an array per project
End With
Next i
Dim current As Double
Dim sumof As Double
Dim hours_left As Double
Dim empty_counter As Integer
For i = 1 To 3
'we reset all of the counters per row
sumof = 0
empty_counter = 0
'looping through all the column values in the row
For j = 2 To 6
current = tbl.ListRows(1).Range(i, j)
sumof = sumof + current ' we get a sum of the current values in the row
If (current = 0) Then 'if there is an empty cell, we keep track of it _
(so we know into how many cells we can still divide the remaining time)
empty_counter = empty_counter + 1
End If
Next j
' so we get how many hours we have left for the project _
in comparison to how many months are free to distribute
hours_left = (hour_dist(i) - sumof)
'if we also want to store the info, _
'what month we ended on before we distribute the remaining hours
If (hours_left = 0) Then
Select Case empty_counter
Case 0
tbl.ListRows(1).Range(i, 7) = "Dec"
Case 1
tbl.ListRows(1).Range(i, 7) = "Nov"
Case 2
tbl.ListRows(1).Range(i, 7) = "Oct"
Case 3
tbl.ListRows(1).Range(i, 7) = "Sep"
Case 4
tbl.ListRows(1).Range(i, 7) = "Aug"
End Select
Else
tbl.ListRows(1).Range(i, 7) = "Dec"
End if
If (empty_counter <> 0) Then '( we dont want to be dividing by 0 )
For n = 6 To (6 - empty_counter + 1) Step -1
'for each month we divide what we have left _
depending on the % of the months available
tbl.ListRows(1).Range(i, n) = (hours_left / empty_counter)
Next n
End If
' and we loop it for each and every row
Next i
End Sub
结果表格如下所示:
答案 1 :(得分:2)
见下图。填写边际金额后,将此公式输入B2并填写表格的其余部分(将其作为数组公式输入,其中 ctrl + shift + 输入):
=IF(MATCH($H2,$B$1:$F$1,0)<COLUMNS($B$1:B$1),0,($A2-SUM(C2:$G2))/(SUM(IF(MATCH($H$2:$H$4,$B$1:$F$1,0)<COLUMNS($B$1:B$1),0,$A$2:$A$4))-SUM(IF(MATCH($H$2:$H$4,$B$1:$F$1,0)<COLUMNS($B$1:B$1),0,C$2:$G$4)))*B$5)
给出的示例没有唯一的解决方案,因此上述公式的目标是通过从最后开始并按时向后移动来平衡小时,同时按小时数应用小时数留在每个项目上。例如,在解析Dec之后,第2行上的项目仍然有200小时要分配,而第3行上的项目仍然有180小时要分配。因此,该公式将于11月从第2行的项目和120 * 180 /(200 + 180)小时的第3行应用120 * 200 /(200 + 180)小时。
此方法假设所有项目同时开始。如果这个假设不成立,那么VBA可能就是这样。我会根据活动项目的数量对月份进行排序,从最小到最大,然后应用与此处相同的计算。
答案 2 :(得分:0)
我猜你需要“倒退”。 首先在月份 n (12月)中分配活动项目中的小时数。 从相应项目中扣除这些时间并移至月份 n-1 等。