Матричное распределение - PullRequest
0 голосов
/ 30 мая 2018

У меня есть список проектов с потребляемыми часами, распределенными линейно по месяцам:

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

У меня есть проблема, похожая на судоку, где мне нужно соблюдать пропорцию столбца и сохранять сумму строк в каждом проекте,Я пытался разными способами (VBA или функциями) создать этот дистрибутив, но пока не получилось.Я полагаю, что кто-то уже сталкивался с этой проблемой, так есть ли способ сделать это распространение программно?Есть ли название для такого рода рассылки?

Ответы [ 3 ]

0 голосов
/ 31 мая 2018

Я не уверен на 100%, что правильно понял ваш вопрос, но, надеюсь, да, потому что это заняло у меня довольно много времени.На самом деле это выглядит обманчиво легко, но на самом деле довольно сложно.

Итак, давайте предположим, что у нас есть эта таблица:

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

0 голосов
/ 31 мая 2018

См. Изображение ниже.Заполнив предельные суммы, введите эту формулу в B2 и заполните оставшуюся таблицу (введите ее как формулу массива с помощью ctrl + shift + enter):

=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 часов для выделения.Таким образом, формула будет применять 120 * 200 / (200 + 180) часов в ноябре для проекта в строке 2 и 120 * 180 / (200 + 180) часов для проекта в строке 3.

Эта методология предполагаетчто все проекты начинаются одновременно.Если это предположение не выполняется, VBA, вероятно, будет правильным решением.Я бы отсортировал месяцы по количеству активных проектов, от наименьшего к наибольшему, а затем применил бы тот же тип расчета, что и здесь.

0 голосов
/ 30 мая 2018

Я думаю, вам нужно думать "задом наперед".Сначала распределите часы среди активных проектов в месяце n (декабрь).Вычтите эти часы из соответствующего проекта и перейдите к месяцу n-1 и т. Д.

...