用于查找所有参与者可用的会议时隙的算法

时间:2015-12-22 22:08:38

标签: schedule

在访谈博客中遇到了这个问题。根据{{​​1}}人(a - b) i.e., from 'a' to 'b'格式的自由时间安排,打印所有n参与者都可用的所有时间间隔。这就像一个日历应用程序,建议可能的会议时间。

n

请分享任何已知的最佳算法来解决此问题。

我正在考虑以下解决方案。

  • 创建一个Example: Person1: (4 - 16), (18 - 25) Person2: (2 - 14), (17 - 24) Person3: (6 - 8), (12 - 20) Person4: (10 - 22) Time interval when all are available: (12 - 14), (18 - 20). 个间隔,其中包含每个人的一个间隔。最初是currentList

  • currentList = [4-16, 2-14, 6-8, 10-22]中查找max_startmin_end,如果currentList,请输出(max_start, min_end);更新max_start < min_end中的所有时间间隔,使currentList值为start。从min_end中删除min_end的时区间隔,并将该人员列表中的下一个条目添加到currentList

  • 如果在上一步中currentList,请更新max_start >= min_end中的所有时间间隔,使currentList值为start。如果对于任何时间间隔max_starti,请将end > start中的间隔替换为相应人员的下一个时间间隔。

基于上述想法,它将在给定示例中运行如下:

currentList

虽然我没有实现上述内容。我正在考虑使用min-heap和max-heap来获取最小值和最大值。但我担心更新起始值,因为更新堆可能会变得昂贵。

5 个答案:

答案 0 :(得分:4)

一个起点,仍然要优化一点,可能是以下(代码在Python中)。 您有以下数据(动态清楚地创建allPeople列表):

person_1 = ["4-16","18-24"]
person_2 = ["2-14","17-24"]
person_3 = ["6-8","12-20"]
person_4 = ["10-22"]
allPeople = [person_1, person_2, person_3, person_4]

您可能会创建一个包含当天所有时段的列表(例如["0-1", "1-2", "2-3", etc.],如下所示:

allTimeSlots = []
for j in range(0,24):
    allTimeSlots.append(str(j) + "-" + str(j+1))

然后创建一个名为commonFreeSlots的列表,该列表由每个人的空闲时段集合内的所有时间段组成:

commonFreeSlots = []
for j in range(0,len(allTimeSlots)):
    timeSlotOk = True
    for k in range(0,len(allPeople)):
        person_free_slots = parseSlot(allPeople[k])
        if allTimeSlots[j] not in person_free_slots:
            timeSlotOk = False
            break
    if timeSlotOk:
        commonFreeSlots.append(allTimeSlots[j])

请注意,函数parseSlot只是获取字符串列表(例如"2-14","15-16")并返回每小时时段列表(例如["2-3","3-4","4-5" etc.])以使其与上面创建的每小时时段列表allTimeSlots

def parseSlot(list_of_slots):
    result = []
    for j in range(0,len(list_of_slots)):
        start_time = int(list_of_slots[j].split("-")[0])
        end_time = int(list_of_slots[j].split("-")[1])
        k = 0
        while (start_time + k) < end_time:
            result.append(str(start_time+k) + "-" + str(start_time+k+1))
            k += 1
    return result

如果我运行上面的脚本,我会得到以下结果:

['12-13', '13-14', '18-19', '19-20']

当然,为了聚合小时数(并且使用['12-14','18-20']而不是每小时版本),你仍然需要输出一些输出,但我认为这应该更容易。

上述解决方案应始终有效,但我不确定它是否最佳,它可能存在更好的解决方案。但既然你还没有分享任何尝试,我想你只是想要一些入门提示,所以我希望这个有所帮助。

答案 1 :(得分:2)

今天在一次采访中得到了这个。提出了O(N*logN)解决方案。想知道是否有O(N)个解决方案可用...

概述:将单个计划加入一个列表intervals->按间隔的开始时间对其进行排序->如果相交则合并相邻的间隔->现在可以轻松返回可用性。

import unittest


# O(N * logN) + O(2 * N) time
# O(3 * N) space

def find_available_times(schedules):
    ret = []

    intervals = [list(x) for personal in schedules for x in personal]

    intervals.sort(key=lambda x: x[0], reverse=True)  # O(N * logN)

    tmp = []

    while intervals:
        pair = intervals.pop()    
    
        if tmp and tmp[-1][1] >= pair[0]:
            tmp[-1][1] = max(pair[1], tmp[-1][1])

        else:
            tmp.append(pair)
    
    for i in range(len(tmp) - 1):
        ret.append([tmp[i][1], tmp[i + 1][0]])    

    return ret


class CalendarTests(unittest.TestCase):

    def test_find_available_times(self):
        p1_meetings = [
            ( 845,  900),
            (1230, 1300),
            (1300, 1500),
        ]

        p2_meetings = [
            ( 0,    844),
            ( 845, 1200), 
            (1515, 1546),
            (1600, 2400),
        ]

        p3_meetings = [
            ( 845, 915),
            (1235, 1245),
            (1515, 1545),
        ]

        schedules = [p1_meetings, p2_meetings, p3_meetings]
       
        availability = [[844, 845], [1200, 1230], [1500, 1515], [1546, 1600]]

        self.assertEqual(
            find_available_times(schedules), 
            availability
            )


def main():
    unittest.main()


if __name__ == '__main__':
    main()

答案 2 :(得分:1)

我更喜欢采用基于设定的稍微不同的方法!我会让语言元素为我完成繁重的工作。你们已经弄清楚了,我正在做一个假设,即所有会议都是每小时进行一次,间隔时间为1小时。

def get_timeslots(i, j):
    timeslots = set()
    for x in range (i, j):
        timeslots.add((x, x + 1))
    
    return timeslots

if __name__ == "__main__":
    allTimeSlots = get_timeslots(0, 24)

    person1TimeSlots = get_timeslots(4, 16).union(get_timeslots(18, 24))
    person2TimeSlots = get_timeslots(2, 14).union(get_timeslots(17, 24))
    person3TimeSlots = get_timeslots(6,8).union(get_timeslots(12, 20))
    person4TimeSlots = get_timeslots(10, 22)

    print(allTimeSlots
        .intersection(person1TimeSlots)
        .intersection(person2TimeSlots)
        .intersection(person3TimeSlots)
        .intersection(person4TimeSlots))

答案 3 :(得分:0)

以下是该问题的JavaScript解决方案。它的复杂度为O(n ^ 3),但由于时间有限,因此可以认为n ^ 2

const person_1 = ['4-16', '18-24'];
const person_2 = ['2-14', '17-24'];
const person_3 = ['6-8', '12-20'];
const person_4 = ['10-22'];
const allPeople = [person_1, person_2, person_3, person_4];

function getFreeMeetingTime(allPeople) {
  //get a range of time in a day.
  // you can pass the min and max from the input if its not 0 to 24 hrs
  const allTimeSlotsInDay = getAllTimeSlots();
  let tempResult = [];
  for (const person of allPeople) {
    for (const time of person) {
      for (let i = Number(time.split("-")[0]); i < Number(time.split("-")[1]); i++) {
        const val = `${i}-${i + 1}`;
        if (!tempResult.includes(val)) {
          tempResult.push(val);
        }
      }
    }
  }
  return allTimeSlotsInDay.filter(time => !tempResult.includes(time));
}

function getAllTimeSlots() {
  const result = [];
  for (let i = 0; i < 24; i = i+1) {
    result.push(`${i}-${i + 1}`);
  }
  return result;
}

console.log(getFreeMeetingTime(allPeople));
//[ '0-1', '1-2']

您可以在下面的链接中查看详细的代码

https://hectane.com/meeting-when-all-people-free

答案 4 :(得分:0)

Dmitry的解决方案在大多数情况下都足够好,但是这是另外O(k * N)的时间和O(k)的额外空间,其中N是会议的数量,k是时隙的粒度。如果已知每个会议都是每小时一次,则k可以是24。如果每30分钟举行一次会议,则k可以是48。k可以一直上升到60 * 24(您的粒度是一天中的每一分钟) )。您还可以将k设为所有时间的GCD。

概述:创建一个称为A的k大小的布尔数组,其中每个索引对应于您的时间粒度的可用性。它从每个可用插槽开始。运行所有会议。将在A的会议开始时间和结束时间之间的索引设置为False。最后,A拥有每个人共有的空闲时隙。时间必须以分钟为单位,算法才能生效。

minutesInDay = 60 * 24


def minuteToString(time):
    hour = str(int(time / 60))
    minute = str(int(time % 60))

    if len(hour) == 1:
        hour = '0' + hour
    if len(minute) == 1:
        minute = '0' + minute

    return hour + ':' + minute


def stringToMinute(time):
    hour, minute = time.split(':')
    return 60 * int(hour) + int(minute)


def availableTimeSlots(meetings, k):
    freeTime = [True] * k
    step = int(minutesInDay / k)

    for meet in meetings:
        for i in range(int(meet[0] / step), int(meet[1] / step)):
            freeTime[i] = False

    result = list()
    openInterval = False
    beg, end = 0, 0
    for i, slot in enumerate(freeTime):
        if not openInterval and slot:
            openInterval = True
            beg = i
        elif openInterval and not slot:
            openInterval = False
            end = i
            beg = minuteToString(beg * step)
            end = minuteToString(end * step)
            result.append((beg, end))

    return result


def main():
    p1 = [
        ('9:00', '10:30'),
        ('12:00', '13:00'),
        ('16:00', '18:00'),
    ]

    p2 = [
        ('10:00', '11:30'),
        ('12:30', '14:30'),
        ('14:30', '15:00'),
        ('16:00', '17:00'),
    ]

    p3 = [
        ('00:00', '8:00'),
        ('12:00', '14:00'),
        ('18:00', '24:00'),
    ]

    meetings = [
        list(map(stringToMinute, meeting)) for p in [p1, p2, p3]
        for meeting in p
    ]
    print(meetings)
    print(availableTimeSlots(meetings, 48))


if __name__ == '__main__':
    main()