检测重复事件中的冲突

时间:2009-04-12 12:18:28

标签: ruby calendar constraints recurrence

我正在编写一个需要检查冲突的日历应用程序 在经常性的条目之间。每个Entry对象都有一个recurrences()方法 返回一个范围数组 - 每个范围包含开始和结束 每个未来发生的时间。

我需要检查新条目和现有条目之间的冲突。我 这样做是通过检查新的未来发生的 与未来现有条目发生的条目冲突:

def conflicts?(other)
  conflicts = 0
  recurrences.each do |my_rec|
    other.recurrences.each do |other_rec|
      start, finish = other_rec.first, other_rec.last
      conflicts += 1 if my_rec.include?(start) || my_rec.include?(finish)
    end
  end
  conflicts > 0
end

recurrences()默认返回开始时间之间的所有匹配项 并开始时间+ 1年

问题是这种方法效率不高。比较两个条目,每个条目每天重复超过1年,导致365 * 365比较(在我的机器上需要4秒以上)。可能有任意数量的现有条目来比较新条目 我现在的方法毫无用处。

我没有计算机科学或数学背景,但我一直都是 阅读有关算法的各种教科书,我一直无法找到 优化方法的方法。还有其他人有什么想法吗?

感谢

戴夫

4 个答案:

答案 0 :(得分:2)

首先,你可以通过引起早期的函数返回来改善这一点:

def conflicts?(other)
  conflicts = 0
  recurrences.each do |my_rec|
    other.recurrences.each do |other_rec|
      start, finish = other_rec.first, other_rec.last
      return true if my_rec.include?(start) || my_rec.include?(finish)
    end
  end
  false
end

但是,这不会提高算法的平均性能,但只会在发生冲突时进行一次比较。您唯一的选择是尽早发现“简单”碰撞。所以喜欢

  • 将重复类型(每周,每日,每月)存储到重复对象中。
  • 如果两者都是每日复发,请找到可能存在潜在冲突的第一天。示例:每日,a:1月至7月,b:5月至10月应仅检查5月1日的时间冲突。如果没有发生,则不需要检查是否存在任何其他冲突。
  • 对不同的星座(周周,周日,日)进行相同的处理。
  • 避免写day-weekweek-day - week_day(x,y)day_week(y,x)相同。
  • 如果找不到匹配的方法,则必须使用上面给出的方法作为后备。

正如你所看到的那样,后者的工作要多得多 - 而最坏情况下的执行时间可能相同(因为它使用原始算法作为后备)。最糟糕的情况可能是由于“不规则”的复原(例如“每天一小时后”)造成的。

答案 1 :(得分:1)

require 'set' #ruby standard lib
first_dates  = Set.new [1,2]  #where 1 and 2 are your sample dates, in an array
second_dates = Set.new [2,3]  #where 2 and 3 are your sample dates,
first_dates.intersection( second_dates ).empty?  #if empty, then no conflict

答案 2 :(得分:0)

一些想法:

  1. 使用从日历日期指向该日期所有条目列表的数据结构。然后查看冲突日期的条目列表。
  2. 查看星期一 - 周一的重复参赛作品永远不会与周三的参赛作品发生碰撞(包含在第一个想法中)。
  3. 使用到期日期 - 检查碰撞时,只检查适合先前过期的条目的日期。 您可以从Sieve of Eratosthenes获得灵感。

答案 3 :(得分:0)

假设重复是可排序的,您可以在O(n * log(n)中对它们进行排序,并且只与相邻事件进行比较。这是一个开始:

def conflicts?(other)
 conflicts = 0
 # Generate all recurrences and sort
 all_recurrences = recurrences + other.recurrences
 all_recurrences.sort!

 # Keep track of what immediate neighbors could be conflicting
 conflicting = []
 all_recurrences.each do |my_rec| 
     conflicting.each do |other_rec| do
       start, finish = other_rec.first, other_rec.last
       if my_rec.include?(start) || my_rec.include?(finish) then
          # TODO update conflicting array: add my_rec + other_rec if conflicting
          conflicts += 1
       else 
          # TODO remove other_rec if not conflicting
       end
     end
 end
 conflicts > 0
end