公平分配分配算法

时间:2013-05-20 13:08:32

标签: sql tsql

我正在努力想出一个有效的SQL来解决公平分配问题。

我的数据模型包含一个“客户”,可以有1个以上的“案例”。 每个客户都需要一个“案件处理程序”,分配给该客户。

我试图在所有“案件处理程序”中分发“客户”,以便所有“案件处理程序”尽可能接近相等数量的“案例”。

我有一个查询,它给了我一个'客户ID'和'案例计数'

Example http://i39.tinypic.com/2zswxdw.jpg

我有一个案例处理程序表,我目前总共有4个案例处理程序(可以添加或删除案例处理程序,然后必须重新运行此分发)。

我知道我需要在案例处理程序表和上面的查询上进行连接,这样我就可以为每个客户执行更新,为它们分配一个案例处理程序。但我不知道如何做到这一点来平衡最公平的庄园中的案例。

我有一个几乎有效的方法是我使用案例处理程序的计数在查询的行号上使用模数加入,因此查询中依次按案例计数排序的每行可用于分配客户循环的案件处理程序。但这并没有给出公平的分配。

(实际上,我的实时系统中的客户表超过100,000,其中大多数只有一个案例,少了2个,然后更少有3个等等,一个客户有51个案例)

感谢任何人给我的帮助/建议。

2 个答案:

答案 0 :(得分:1)

有一些正式的优化框架可以解决这类问题,但我认为你可以用更简单的东西来解决问题。根据您的描述,听起来每个客户只能有一个案件处理程序,因此有一些不幸的人需要处理您最大客户的所有51个案例。

尝试这样的事情(伪代码):

total_cases = SUM(Case_Count)
total_handlers = COUNT(Case_Handlers)

foreach SELECT Customer_Id, Case_Count ORDER BY Case_Count DESCENDING:

   # Calculate the target number of cases to assign to the next handler
   target_cases_per_handler = total_cases / total_handlers

   # If a customer has more than the target number of cases, then
   # it must be assigned to a case handler
   if Case_Count > target_cases_per_handler:
       assign_to_handler(Customer_Id)
       total_handlers = total_handlers - 1
       total_cases = total_cases - Case_Count

   # Otherwise, try to pair up the cases with a small number of cases
   # that is close to average (this part is inefficient)
   else:
       assign_to_handler(Customer_Id)
       residual = CEIL(target_cases_per_handler - Case_Count)
       while (residual > 0)
           best_customer_id, best_case_count = SELECT TOP 1 Customer_Id, Case_Count ORDER BY ABS(Case_Count - residual) ASCENDING

           assign_to_handler(best_customer)
           residual = residual - best_case_count
           total_cases = total_cases - best_case_count

       total_handlers = total_handlers - 1

这可以让您粗略地将客户分配给案件处理程序,同时仍然确保每个客户都有一个处理程序。

答案 1 :(得分:0)

你的案件分布也不错。我建议采用以下方法:

(1)将客户分为两组。 。 。多个病例和单个病例。

(2)以循环方式将多个案例分配给案件工作者。

(3)将单个案例分配给案件工作者以平衡每个案件工作者。

这适用于您的特定发行版(主要是单身人士)。它不一定是通用算法。

这是SQL:

with multiples as (
      select CustomerId, COUNT(*) CaseCnt,
             ROW_NUMBER() over (partition by CustomerId order by CustomerId) % @NumHandlers as theHandler
      from customers c
      group by CustomerId
      having COUNT(*) > 1
     ),
    h as (
     select theHandler, SUM(CaseCnt) as CaseCnt,
            SUM(CaseCnt) - (NumCusts / @NumHandlers) as StillNeeded
     from multiples cross join
          (select COUNT(*) as NumCusts from customers c) const
     group by theHandler
    ),
    hsum as (
     select h.*, SUM(StillNeeded) over (order by StillNeeded) as endnum,
            SUM(StillNeeded) over (order by StillNeeded) - StillNeeded + 1 as startnum
     from h
    ),
    singles as (
     select CustomerId, ROW_NUMBER() over (partition by CustomerId order by CustomerId) as seqnum
     from Customers
     group by CustomerId
     having COUNT(*) = 1
    ),
    singlesh as (
     select singles.Customerid, h.theHandler
     from singles join
          hsum
          on singles.seqnum between h.startnum and h.endnum
   )
select *
from ((select CustomerId, theHandler
       from multiples
      ) union all
      (select CustomerId, theHandler
       from singlesh
      )
     ) t

SQL几乎遵循上面的解释。它首先以循环方式随机地将多个记录分配给处理程序。处理程序只是从0到@NumHandlers的数字。然后它计算“StillNeeded”个案的数量以达到最大值。注意:这假设处理程序的数量是客户数量的精确倍数。修正的调整使查询看起来更复杂。

然后计算每个处理程序仍需填写的数字。关键是将此作为累积总和(这使用SQL Server 2012语法,在早期版本中使用相关子查询执行此操作)。然后使用此信息将单例分配给每个处理程序。

最后一步是将union两组客户聚集在一起,即倍数和单身。