【问题标题】:Is there a standard algorithm to balance overlapping objects into buckets?是否有标准算法将重叠对象平衡到桶中?
【发布时间】:2015-06-26 23:04:19
【问题描述】:

我有一群用户,有给定的开始和结束时间,例如:

{ Name = "Peter", StartTime = "10:30", EndTime = "11:00" },
{ Name = "Dana", StartTime = "11:00", EndTime = "12:30" },
{ Name = "Raymond", StartTime = "10:30", EndTime = "14:00" },
{ Name = "Egon", StartTime = "12:00", EndTime = "13:00" },
{ Name = "Winston", StartTime = "10:00", EndTime = "12:00" }

我想根据它们重叠的时间(基于可配置的阈值,例如,它们至少需要重叠半小时)将它们放入存储桶中。我希望桶最好是 4 件大,但 2-5 的任何范围都是可以接受的。

在上面的示例中,没有 4 个人匹配,所以我有 3 个(Peter、Raymond、Winston)和 2 个中的一个(Dana、Egon)。

我设计了一个算法的原型,该算法似乎依赖于机会而不是科学:

  1. 按开始时间排序列表
  2. 创建一个空桶
  3. 从列表中选择第一个用户
  4. 对照存储桶中的所有用户检查该用户
  5. 如果该用户与存储桶中的每个人都重叠,则将该用户放入其中并将其从列表中删除
  6. 如果存储桶具有理想的大小 (4),或者如果我循环并检查同一用户超过 3 次,请关闭存储桶并创建一个新的空存储桶

这对于前几个存储桶很有效,但会导致只有 2 个人的存储桶可以更好地组合。

我可以更改算法以从列表中删除所有理想存储桶并重新洗牌并尝试更多,但我觉得这应该是一个常见问题 - 这就像工人的轮班分配,或者可能是 knapsack problem

有人知道这类问题的标准算法吗?

(标记组合,因为我认为这是它适用的数学领域,如果有错误请纠正我)

【问题讨论】:

  • 您希望您的存储桶仅根据 30 分钟(阈值)重叠还是根据重叠最多的人进行排序?
  • @OrrenRavid 任何重叠都可以。我们可能会调整最小重叠时间(例如,最少 60 分钟),但重叠 60 分钟的人与重叠 180 分钟的人一样好。
  • 我认为区间树会很好地工作。 en.wikipedia.org/wiki/Interval_tree
  • 我做了一个区间树程序。它绝对不如算法那么干净,但它基本上是我下面的答案。

标签: c# .net algorithm combinatorics


【解决方案1】:

tl;dr: 动态规划获胜(O(sort(n)) 时间)。

首先,请注意按开始时间顺序连续分桶是可以的。

提议(碎片整理):a, b, c, d 成为不同的用户,例如StartTime(a) ≤ StartTime(b) ≤ StartTime(c) ≤ StartTime(d)。如果XY 是有效的桶,例如a, c ∈ Xb, d ∈ Y,那么X - {c} ∪ {b}Y - {a} ∪ {d} 也是有效的桶。

我只知道如何通过繁琐的案例分析来证明这一点(略)。

结果是,您可以假装将段落分成几行,其中“段落”是按开始时间顺序排列的用户列表,每个“行”是一个桶。由于 Knuth 和 Plass,有一种算法可以优化换行,其中给定行的惩罚或多或少是任意函数。例如,您可以使 4 个桶的成本为 0,3 个桶的成本为 1,2 个桶的成本为 2,而 1 个桶的成本为 100。

【讨论】:

    【解决方案2】:

    根据您的问题,我可能会先创建一个名为“Person”的类,类似这样的东西。给这个类属性“名称”、“开始时间”和“结束时间”。

    class Person
    {
         public string name;
         public double start_time;
         public double end_time;
    }
    

    然后将它们放入一些 Person 类型的有序列表中。 (此外,我目前将时间存储为双精度数。您只需将时间的任何小数部分乘以 60/100 即可将它们转换回时间。

    之后,您可以创建一个存储桶列表,如果需要,您可以向其中添加新的存储桶。然后,您根据您定义的阈值对列表进行排序,如果要比较的两个对象基于该阈值重叠,则它们都进入该存储桶。如果它们不重叠,则移动到下一个桶,如果那里有重叠,则将其添加到该桶,依此类推,直到到达最后一个桶。如果你已经遍历了所有的 Bucket 并且仍然没有重叠,那么为那个对象创建一个新的 Bucket。

    class MainFunc
    {    
        static void Main(string[] args)
        {    
    
             //makes a function to check if 2 values overlap over a threshold
             //st stands for start time and et stands for end time
    
             public bool IsWithinThreshold(double st1, double st2, double et1, double et2)
             {
                 double threshold = .5;
                 if(st1 >= et2 || st2 >= et1)
                 {
                     return false
                 }
                 else
                 {
                     if(st1+threshold <= et2 && st1+threshold <= et1 || st2+threshold <= et1 && st2+threshold <=et2)
                     {
                         return true;
                     }
                     else
                     {
                         return false;
                     }
                 }
             }           
            // makes objects of type Person with attributes of name, start time, and end time
    
            Person Peter = new Person();
            Peter.name = "Peter"
            Peter.start_time = 10.5
            Peter.end_time = 11.0
    
            Person Dana = new Person();
            Dana.name = "Dana"
            Peter.start_time = 11.0
            Peter.end_time = 12.5
    
            Person Raymond = new Person();
            Raymond.name = "Raymond"
            Raymond.start_time = 10.5
            Raymond.end_time = 14.0
    
            Person Egon = new Person();
            Egon.name = "Egon"
            Egon.start_time = 12.0
            Egon.end_time = 13.0
    
            Person Winston = new Person();
            Winston.name = "Winston"
            Winston.start_time = 10.0
            Winston.end_time = 12.0
    
            //puts objects of type Person into an unordered list
    
            List<Person> people = new List<Person>();
            people.Add(Peter);
            people.Add(Dana);
            people.Add(Raymond);
            people.Add(Egon);
            people.Add(Winston);
    
            //sets up a list of lists of People (Buckets in our case)
    
            List<List<Person>> Buckets = new List<List<Person>>;
    
            //sets up an intial Bucket and adds the first person on the list to it
    
            List<Person> Bucketinitial = new List<Person>;
            Bucketinitial.add(people[0]);
    
    
            for(var i = 1; i < people.Count; i++)
            {
                for(var j = 0; j< Buckets.count; j++)
                {
                    //sets a checker to make sure that all objects in a given Bucket overlap with the person we are checking
    
                    bool overlap = true;
    
                    for(var k = 0; k< Buckets[k].count; k++)
                    {
                    overlap = overlap & IsWithinThreshold(people[i].start_time,Buckets[j][k].start_time,people[i].end_time,Buckets[j][k].end_time)
                    }
    
                    if (overlap == true)
                    {
                        Buckets[j].add(people[i])
                    }
    
                    //if all the objects in a bucket don't overlap with the person...
                    //... make a new Bucket with that person
                    else
                    {
                        List<Person> NewBucket = new List<Person>;
                        NewBucket.add(people[i]);
                        Buckets.add(NewBucket);
                    }
                }
            }
        }
    }
    

    然后只需添加一个打印命令即可打印出桶列表中每个桶内每个对象的名称属性。如果您有任何问题/疑虑,请发表评论,干杯。

    【讨论】:

    • *注意,我的间距可能会因此而关闭,因此当您将代码放入您最喜欢的编译器时,请确保重新调整它的间距,以便您拥有完美的代码行。
    【解决方案3】:

    您可以修改算法以合并区间树以加快搜索速度

    1. 按开始时间排序列表
    2. 将项目添加到区间树
    3. 创建一个空桶
    4. 从列表中选择第一项
    5. 使用区间树的区间搜索在第一项填满存储桶的阈值时间内查找最早的项
    6. 从列表中删除分桶项
    7. 如果列表为空则停止,否则转到步骤 4

    基本上,您在间隔步骤(由您的可配置阈值给出)中从左向右移动,使用间隔树在移动时快速查询最近的项目。

    【讨论】:

      猜你喜欢
      • 2020-12-31
      • 1970-01-01
      • 1970-01-01
      • 2016-07-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-14
      相关资源
      最近更新 更多