简体   繁体   English

根据星期几确定重叠的时间范围

[英]Determining overlapping time ranges based on day of the week

I have an array of time ranges like this:-我有一系列这样的时间范围:-

$events = array(
    array("Monday", '19:00:00', '19:30:00', 0),
    array("Monday", '19:10:00', '19:40:00', 0),
    array("Tuesday", '19:10:00', '19:40:00', 0),
    array("Wednesday", '19:10:00', '19:40:00', 0),
    array("Monday", '19:30:00', '19:50:00', 0),
);

I am using bubble sort on the array:-我在数组上使用冒泡排序:-

for($i = 0; $i < (count($events) - 1); $i++)
{
    for($j = 1; $j < (count($events) - i - 1); $j++)
    {
        if($events[$i][0] < $events[$j][0])
        {
            if ($events[$j] > $events[($j + 1)]) 
              {
                $swap       = $events[$j];
                $events[$j]   = $events[($j + 1)];
                $events[($j + 1)] = $swap;
              }
        }
    }
}

The result comes like this:-结果是这样的:-

Array ( 
    [0] => Array ( 
                    [0] => Monday 
                    [1] => 19:00:00 
                    [2] => 19:30:00
                    [3] => 0)
    [1] => Array ( 
                    [0] => Monday 
                    [1] => 19:10:00 
                    [2] => 19:40:00
                    [3] => 0) 

    [2] => Array ( 
                    [0] => Monday 
                    [1] => 19:30:00 
                    [2] => 19:50:00
                    [3] => 0)

    [3] => Array ( 
                    [0] => Tuesday 
                    [1] => 19:10:00 
                    [2] => 19:40:00
                    [3] => 0)

    [4] => Array ( 
                    [0] => Wednesday 
                    [1] => 19:10:00 
                    [2] => 19:40:00
                    [3] => 0)
)

Now I need to strike out those time ranges which overlap on a specific day.现在我需要剔除那些在特定日期重叠的时间范围。

Like this:像这样:

Array ( 
[0] => Array ( 
                [0] => Monday 
                [1] => 19:00:00 
                [2] => 19:30:00
                [3] => 1) 
[1] => Array ( 
                [0] => Monday 
                [1] => 19:10:00 
                [2] => 19:40:00
                [3] => 1)

[2] => Array ( 
                [0] => Monday 
                [1] => 19:30:00 
                [2] => 19:50:00
                [3] => 1)


[3] => Array ( 
                [0] => Tuesday 
                [1] => 19:10:00 
                [2] => 19:40:00 
                [3] => 0) 

[4] => Array ( 
                [0] => Wednesday 
                [1] => 19:10:00 
                [2] => 19:40:00
                [3] => 0)

) )

The ones which are [3] => 1, denote there is a time overlap conflict. [3] => 1 表示存在时间重叠冲突。

How can I proceed?我该如何继续?

I tried to use this solution , but got no luck.我尝试使用此解决方案,但没有运气。 Besides, mine has a day of week.此外,我的一周有一天。

I had a little play and this is what I came up with.我玩了一点游戏,这就是我想出来的。 It preserves the original order of the $events array as textual days don't sort well (Friday would come before Wednesday etc).它保留了 $events 数组的原始顺序,因为文本日期排序不好(星期五会在星期三之前等)。 A bit brute force, not optimised or properly tested, but hope it's useful for ideas.有点蛮力,没有优化或正确测试,但希望它对想法有用。

foreach ($events as $event_id=>$event) {
    $days[$event[0]][]=[$event_id,$event[1],$event[2]];
}

foreach ($days as $dayevents) {
    if (count($dayevents)>1) {
        foreach ($dayevents as $dayevent1) {
            foreach ($dayevents as $dayevent2) {
                if ((($dayevent1[1]>$dayevent2[1]) and ($dayevent1[1]<$dayevent2[2])) or 
                    (($dayevent1[2]>$dayevent2[1]) and ($dayevent1[2]<$dayevent2[2]))) {
                    $events[$dayevent1[0]][3]=1;
                    $events[$dayevent2[0]][3]=1;
                }
            }
        }
    }
}
$events = array(
    array("Monday", '19:00:00', '19:30:00', 0),
    array("Monday", '19:10:00', '19:40:00', 0),
    array("Tuesday", '19:10:00', '19:40:00', 0),
    array("Wednesday", '19:10:00', '19:40:00', 0),
    array("Monday", '19:30:00', '19:50:00', 0),
);

$combined = array();
// first we collect all intervals for the given day
foreach($events as $record)
{
  $combined[$record[0]][] = array(
    $record[1], $record[2], 0
  );
}

// then for each day we look if there are overlaps
foreach($combined as $day => &$intervals)
{
  $len = count($intervals);
  // we compare each interval with each of the rest intervals for the same day
  foreach($intervals as $i => &$interval_1)
  {
    // we convert the start/end times of the interval A to an integer
    $begin_1 = str_replace(':','',$interval_1[0]);
    $end_1 = str_replace(':','',$interval_1[1]);
    for($k = $i + 1; $k < $len; $k++)
    {
      // we convert the start/end times of the interval B to an integer
      $begin_2 = str_replace(':','',$intervals[$k][0]);
      $end_2 = str_replace(':','',$intervals[$k][1]);      
      // we compute the overlap of the 2 intervals
      $overlap = max(0,$end_1 - $begin_1 - max(0,$end_1 - $end_2) - max(0,$begin_2 - $begin_1));
      if($overlap)
      {
        $interval_1[2]++; // we increase the counter of interval A
        $intervals[$k][2]++; // we increase the counter of interval B
      }
    }
  }
}
<?php

$events = array(
    array("Monday", '19:00:00', '19:10:00'),
    array("Monday", '19:05:00', '19:20:00'),
    array("Tuesday", '19:10:00', '19:40:00'),
    array("Wednesday", '19:10:00', '19:40:00'),
    array("Monday", '19:15:00', '19:30:00'),

);

function convertToSeconds($time){
    $time = explode(":",$time);
    return intval($time[0]) * 3600 + intval($time[1]) * 60 + intval($time[2]);
}


$time_ranges = [];
$hold_dates = [];
for($i=0;$i<=86401;++$i){
    $time_ranges[] = 0;
    $hold_dates[$i] = [];
}

$time_range_for_week = [];
$week_days = ['monday' ,'tuesday','wednesday','thursday','friday','saturday','sunday'];

foreach($week_days as $day){
    $time_range_for_week[$day] = [
        'hold_dates' => $hold_dates,
        'time_range' => $time_ranges
    ];
}

foreach($events as &$data){
    $start_time = convertToSeconds($data[1]);
    $end_time = convertToSeconds($data[2]);

    $time_range_for_week[strtolower($data[0])]['hold_dates'][$start_time][] = &$data;
    $time_range_for_week[strtolower($data[0])]['hold_dates'][$end_time][] = &$data;
    $time_range_for_week[strtolower($data[0])]['time_range'][$start_time] += 1;
    $time_range_for_week[strtolower($data[0])]['time_range'][$end_time + 1] -= 1;
}


foreach($time_range_for_week as $day_name => &$day_data){
    $sum = 0;
    foreach($day_data['time_range'] as $time => $value){
        $sum += $value;
        if($sum > 1 && count($day_data['hold_dates'][$time]) > 0){
            foreach($day_data['hold_dates'][$time] as &$each_event){
                $each_event[3] = 1;
            }
        }
    }
}


print_r($events);

Demo: https://3v4l.org/JeGdR演示: https : //3v4l.org/JeGdR

Algorithm:算法:

  • This is rather simple and efficient than basic brute force.这比基本的蛮力更简单和有效。
  • To find out collisions between times, we first make an array of size 86400 for each day.为了找出时间之间的冲突,我们首先为每天制作一个大小为86400的数组。 86400 because it is no. 86400因为它不是。 of seconds (24*60*60) for a day(like Monday or Tuesday etc).一天(如星期一或星期二等)的秒数(24*60*60) )。

  • The array looks like this for a particular day(like Monday or Tuesday etc):特定日期(例如星期一或星期二等)的数组如下所示:

Structure:结构:

Array
(
    [monday] => Array
        (
            [hold_dates] => Array
                (
                    [],[],[],... till 86400
                ),
            [time_range] => Array
                (
                    [],[],[],... till 86400
                ),
        ),
      [tuesday] => Array(
          ...
        ),
      ...
)
  • In the hold_dates key for a day, we are just going to have our array values at the start time and end times positions.在一天的hold_dates键中,我们将只在开始时间和结束时间位置拥有数组值。 Here start time is the 1 st position(0-based indexing) in array("Monday", '19:10:00', '19:40:00', 0) and end time is 2 nd position in array("Monday", '19:10:00', '19:40:00', 0) .这里开始时间是在第1个位置(基于0的索引) array("Monday", '19:10:00', '19:40:00', 0)和结束时间是2在位置array("Monday", '19:10:00', '19:40:00', 0) In the code, the lines look like:在代码中,这些行看起来像:

Code:代码:

 $time_range_for_week[strtolower($data[0])]['hold_dates'][$start_time][] = &$data;
 $time_range_for_week[strtolower($data[0])]['hold_dates'][$end_time][] = &$data;

The & is used for syncing updates with actual $events array, so it's a pass by reference. &用于将更新与实际的$events数组同步,因此它是通过引用传递的。

  • The time_range will just hold integer values and those integer values as explained below. time_range将只保存整数值和那些整数值,如下所述。

Let's consider intervals like below:让我们考虑如下间隔:

[2,7],
[4,9],
[6,10]

We have a timeline like below:我们有一个如下的时间表:

1  2  3  4  5  6  7  8  9  10 11 // these are seconds from 1 to 11
  +1                -1
        +1                 -1
              +1              -1
   1  1  2  2  3  3  2  2  1   0    

In the above diagram, for each date's start time, we add +1 , and -1 to it's end time + 1 .在上图中,对于每个日期的开始时间,我们将+1-1添加到它的end time + 1 This means that, when we iterate from 1 to 11 and keep summing values, if we find any date's start time or end time having a sum > 1 , we found there is a collision and we need to set it's value to 1 .这意味着,当我们从 1 迭代到 11 并继续求和值时,如果我们发现任何日期的开始时间结束时间的sum > 1 ,我们就会发现存在冲突,我们需要将其值设置为1

  • We do the same in the above code.我们在上面的代码中做同样的事情。 Here, the space used for this code is 86400 * 7 = 604800 , which is constant space O(1) since this does not depend upon size of $events .这里,用于此代码的空间是86400 * 7 = 604800 ,这是常数空间O(1)因为这不取决于$events大小。

  • Time complexity is again the constant iterations 86400 * 7 = 604800 + looping twice through each element in $events , which makes it O(n) where n is size of $events .时间复杂度再次是恒定迭代86400 * 7 = 604800 + 通过$events每个元素循环两次,这使得它O(n)其中n$events大小。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM