Matricial发行

时间:2018-05-30 16:15:25

标签: excel vba algorithm excel-vba matrix

我有一个项目列表,其中包含几个月的消费时间:

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或函数)进行此分发,但到目前为止我都失败了。我相信有人之前已经解决了这个问题,那么有没有办法以编程方式进行此分发?这种分布有名称吗?

3 个答案:

答案 0 :(得分:2)

我不是百分百肯定我的问题是对的,但希望是的,因为我花了很多时间。它实际上看起来很容易,但实际上非常棘手。

所以我们假设我们有这个表:

enter image description here

  

现在,如果我做对了,我们基本上想要计算,我们需要多少小时   已经用完了,现在我们要分发我们剩下的东西了   取决于未使用的(空)月

所以,例如,在第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

结果表格如下所示:

enter image description here

答案 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)

enter image description here

给出的示例没有唯一的解决方案,因此上述公式的目标是通过从最后开始并按时向后移动来平衡小时,同时按小时数应用小时数留在每个项目上。例如,在解析Dec之后,第2行上的项目仍然有200小时要分配,而第3行上的项目仍然有180小时要分配。因此,该公式将于11月从第2行的项目和120 * 180 /(200 + 180)小时的第3行应用120 * 200 /(200 + 180)小时。

此方法假设所有项目同时开始。如果这个假设不成立,那么VBA可能就是这样。我会根据活动项目的数量对月份进行排序,从最小到最大,然后应用与此处相同的计算。

答案 2 :(得分:0)

我猜你需要“倒退”。 首先在月份 n (12月)中分配活动项目中的小时数。 从相应项目中扣除这些时间并移至月份 n-1 等。