Есть ли способ равномерно распределить сотрудников по большому списку задач при учете некоторых критериев условий? - PullRequest
0 голосов
/ 02 февраля 2020

У меня динамическая c группа задач, которые я получаю ежемесячно, я хочу распределить эти задачи по 6 группам, чтобы они могли распределяться равномерно. Каждое задание имеет ранжирование / приоритет, поэтому, если группа получает задание номер 1, я не хочу также давать этой же группе первые 100 приоритетов. Я хочу применить распределение по змее и зигзагам.

Это привело меня к использованию формулы =MIN(MOD(ROW()-2,12),MOD(-ROW()+1,12)). Я получаю нужный дистрибутив, хотя на данном этапе я не знаю, как учесть какие-либо критерии, которые мне нужно добавить в мои логи c.

enter image description here

На изображении выше я пытаюсь взять группы в column F и применить их к Column D. Column E показывает пример формулы =MOD(), и я мог бы просто использовать поиск, чтобы заменить значения Mod 0-5 моими группами 1-6.

Место, где я столкнулся с дорожным заграждением, находится в строке 21 , где я хочу объяснить некоторые критерии или исключения. Я добавил двоичный файл column A для визуализации, но, по сути, я хочу сказать, где столбец C (местоположение задачи) = Loc4, чтобы никогда не назначать задачу для Group 4. В случае, когда я не хочу, чтобы задача была назначена группе 4 в Lo c 4, я надеюсь пропустить Group 4 для одного назначения, пока оно не будет применено к следующей возможной ранжированной задаче. Простое решение состоит в том, чтобы удалить все эти вхождения в конце, но это действительно искажает равномерное распределение, к которому я иду.

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

Это привело меня к другому посту, использующему vba logi c, который мне действительно нравится, но я не могу понять, как изменить его, чтобы учесть некоторые исключения. введите описание ссылки здесь

В идеале я хотел бы использовать эту концепцию создания единого массива моих групп, применяя каждую группу к задаче, если критерии удовлетворяются в этом коротком списке задачи, записать группы в список, выполнить сброс и перейти к следующему подмножеству задач. Таким образом, каждый раз, когда я выбираю следующие 6 задач, они назначаются одной из моих 6 групп, которая будет сохранять тот дистрибутив, на который я надеюсь.

Вот код от пользователя K.Davis пост, который я пытался применить:

    Sub assignEmployeeTasks()

            Dim ws As Worksheet, i As Long
        Set ws = ThisWorkbook.Worksheets(1)
        Dim employeeList() As Variant

        With ws
            For i = 2 To lastRow(ws, 2)
                If (Not employeeList) = -1 Then
                    'rebuild employeelist / array uninitialized
                    employeeList = buildOneDimArr(ws, "F", 2, lastRow(ws, "F"))
                End If
                .Cells(i, 4) = randomEmployee(employeeList)
            Next
        End With

    End Sub

Это «вспомогательные» функции, которые позволяют вашей программе чтобы сделать свою работу:

Function randomEmployee(ByRef employeeList As Variant) As String

    'Random # that will determine the employee chosen
    Dim Lotto As Long
    Lotto = randomNumber(LBound(employeeList), UBound(employeeList))
    randomEmployee = employeeList(Lotto)

    'Remove the employee from the original array before returning it to the sub
    Dim retArr() As Variant, i&, x&, numRem&
    numRem = UBound(employeeList) - 1
    If numRem = -1 Then     'array is empty
        Erase employeeList
        Exit Function
    End If
    ReDim retArr(numRem)
    For i = 0 To UBound(employeeList)
        If i <> Lotto Then
            retArr(x) = employeeList(i)
            x = x + 1
        End If
    Next i
    Erase employeeList
    employeeList = retArr

End Function

' This will take your column of employees and place them in a 1-D array
Function buildOneDimArr(ByVal ws As Worksheet, ByVal Col As Variant, _
        ByVal rowStart As Long, ByVal rowEnd As Long) As Variant()

    Dim numElements As Long, i As Long, x As Long, retArr()
    numElements = rowEnd - rowStart
    ReDim retArr(numElements)

    For i = rowStart To rowEnd
        retArr(x) = ws.Cells(i, Col)
        x = x + 1
    Next i

    buildOneDimArr = retArr

End Function

' This outputs a random number so you can randomly assign your employee
Function randomNumber(ByVal lngMin&, ByVal lngMax&) As Long
    'Courtesy of https://stackoverflow.com/a/22628599/5781745
    Randomize
    randomNumber = Int((lngMax - lngMin + 1) * Rnd + lngMin)
End Function

' This gets the last row of any column you specify in the arguments
Function lastRow(ws As Worksheet, Col As Variant) As Long
    lastRow = ws.Cells(ws.Rows.Count, Col).End(xlUp).Row
End Function

Любая помощь будет высоко ценится! Я нахожусь на любом пути, который приближается к моему желаемому решению, формулам или VBA. Пожалуйста, дайте мне знать, если у вас есть какие-либо вопросы.

Спасибо!

1 Ответ

0 голосов
/ 03 февраля 2020

Эта программа работает с блоком задач в то время, когда размер блока кратен количеству групп. У меня SIZE = 2, чтобы получить размер блока 12, поскольку это дает больше возможностей для разрешения конфликтов, чем 6. Он работает, первоначально назначая задачи зигзагообразному шаблону, а затем проверяет это в соответствии с определенными вами правилами. Они находятся в модуле validLocn (). Если проверка в порядке, процесс перемещается вниз по листу к следующему блоку. Если проверка не пройдена, план перетасовывается путем замены 2 случайно выбранных элементов и повторной проверки. Это продолжается до максимального числа, установленного MAXTRY. Если все еще не решено, пользователь может выбрать повторить попытку, проигнорировать и продолжить или прервать процесс. Я проверил его с 150000 записей, и это заняло меньше минуты, но мои тестовые данные могут не соответствовать вашим реальным данным. Результаты в сводной таблице на столбцах B, C, D показывают равномерное распределение и никаких задач для Grp4 в Locn4.

Count of Task                               
Row     Loc1    Loc2    Loc3    Loc4    Loc5    Loc6    Gand Total
Gp1     4013    3975    3926    5082    3986    4018    25000
Gp2     4021    3992    4077    4928    3975    4007    25000
Gp3     3976    3952    4027    5023    4049    3973    25000
Gp4     5050    4915    4936            5035    5064    25000
Gp5     4072    3996    4034    4890    3969    4039    25000
Gp6     3964    4087    3986    5018    3996    3949    25000                               
Grand  25096    24917   24986   24941   25010   25050   150000

Надеюсь, что поможет.

Option Explicit
Sub assignEmployeeTasks()


    Dim ws As Worksheet, t0 As Single, t1 As Single
    Set ws = ThisWorkbook.Sheets("Sheet1")
    t0 = Timer

    Const COL_GROUP = "F"
    Const COL_LOCN = "C"
    Const SIZE As Integer = 2 ' plan size =  2 * group count

    Const MAXTRY = 50 ' no of tries to validate

    Dim bOK As Boolean
    Dim grp As Variant, iBlockStart As Long, i As Integer, r As Integer, step As Integer

     'initialize grps and location
    Dim countGrp As Integer, lastLocn As Long
    lastLocn = ws.Range(COL_LOCN & Rows.Count).End(xlUp).Row

    countGrp = ws.Range(COL_GROUP & Rows.Count).End(xlUp).Row - 1
    grp = ws.Range(COL_GROUP & "2").Resize(countGrp, 1).Value
    Dim plan() As String
    ReDim plan(countGrp * SIZE, 2)

    Dim itry As Integer, res
    iBlockStart = 1

    Do While iBlockStart < lastLocn

        ' initialize plan
        Call zigzag(plan, grp)
        For i = 1 To UBound(plan)
            plan(i, 1) = ws.Range("C" & iBlockStart + i).Value
        Next

        ' save 1st attempt
        For i = 1 To UBound(plan)
            ws.Range("D" & iBlockStart + i).Value = plan(i, 2)
        Next

        ' validate
        bOK = validLocn(plan, 0)

retry:

        ' retry to validate
        itry = 0
        While bOK = False And itry < MAXTRY
            Call shuffle(plan, 1)
            bOK = validLocn(plan, itry)
            itry = itry + 1
        Wend

        ' write new plan to sheet
        For i = 1 To UBound(plan)
            ws.Range("D" & iBlockStart + i).Value = plan(i, 2)
        Next

        ' check rule again
        If itry = MAXTRY Then
            ws.Range(COL_LOCN & iBlockStart).Select
            res = MsgBox("Failed to vaidate after " & MAXTRY & " attempts", vbAbortRetryIgnore, iBlockStart)
            If res = vbRetry Then GoTo retry
            If res = vbAbort Then Exit Sub
        End If
        iBlockStart = iBlockStart + UBound(plan)
    Loop
    t1 = Timer
    MsgBox "Assigned " & lastLocn - 1 & " tasks in " & Int(t1 - t0) & " secs"

End Sub

 ' valid plan against rules
Function validLocn(plan As Variant, itry) As Boolean
    Dim sLocn As String, sGrp As String, i As Integer

    validLocn = True
    For i = 1 To UBound(plan)
        sLocn = plan(i, 1)
        sGrp = plan(i, 2)
        ' rule 1
        If sGrp = "Gp4" And sLocn = "Loc4" Then
            validLocn = False
            'Debug.Print itry, i, "Fail Rule 1", sGrp, sLocn
        Else
            'Debug.Print itry, i, "Pass Rule 1", sGrp, sLocn
        End If
    Next
End Function

' populate plan groups
Sub zigzag(plan As Variant, grp As Variant)
    Dim i As Integer, r As Integer, step As Integer
    r = 1: step = 1
    For i = 1 To UBound(plan)
        plan(i, 2) = grp(r, 1)
        r = r + step
        If r > UBound(grp) Then
            r = UBound(grp)
            step = -1
        ElseIf r < 1 Then
           r = 1
           step = 1
        End If
    Next
End Sub

' shuffle plan
Sub shuffle(plan As Variant, i As Integer)
    Dim tmp As String, n As Integer, j As Integer, k As Integer
    For n = 1 To i
       ' random choose elements to shuffle
retry:
        k = Int(1 + Rnd() * UBound(plan))
        j = Int(1 + Rnd() * UBound(plan))
        If k = j Then GoTo retry
        tmp = plan(k, 2)
        plan(k, 2) = plan(j, 2)
        plan(j, 2) = tmp
    Next
End Sub


' generate test data
Sub testdata()
    Dim ws As Worksheet, i As Long
    Set ws = ThisWorkbook.Sheets("Sheet1")
    For i = 2 To 150001
        ws.Cells(i, 2) = i - 1
        ws.Cells(i, 3) = "Loc" & 1 + Int(Rnd() * 6)
    Next
End Sub
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...