2009-11-29 254 views
3

首先,感谢您花时间阅读我的问题。计算PHP的数字范围

我想写一个脚本,我遇到了一个难以解决的问题。我有一对数字(例如,1000和2000年)的工作,我有对数字的数组:

$pairs = array(
    array(800, 1100), 
    array(1500, 1600), 
    array(1900, 2100) 
) 

我试图找到,是如何得到未涵盖的范围在这个例子中,1000-1100被阵列(800,1100)覆盖,1500-1600被阵列(1500,1600)覆盖,并且1900-2000被阵列(1900,2100 ),这让我留下了1101-1499和1599-1899的余地。我希望我已经够清楚了。

我想知道的是如何让PHP返回给我一个未被$ pairs变量覆盖的范围的数组。在这个例子中,它将返回:

array(
    array(1101, 1499), 
    array(1599, 1899) 
) 

你知道什么是最好的方法吗?

预先感谢您。

回答

3

嗯,首先你必须定义问题:

  1. 是对排序?
  2. 双重叠吗?
  3. 你想找到一个特定范围的遗漏范围(这似乎是这种情况)?

如果对不排序,排序首先他们:

usort($pairs, 'cmp_pair'); 

function cmp_pair($a, $b) { 
    if ($a[0] == $b[0]) { 
    if ($a[1] == $b[1]) { 
     return 0; 
    } else { 
     return $a[1] < $b[1] ? -1 : 1; 
    } 
    } else { 
    return $a[0] < $b[0] ? -1 : 1; 
    } 
} 

如果重叠范围是允许的,变换对列表中的非重叠集。下面是关于如何做一个建议:

$prev = false; 
$newpairs = array(); 
foreach ($pairs as $pair) { 
    if ($prev) { 
    // this also handles the case of merging two ranges 
    // eg 100-199 with 200 to 250 to 100-250 
    if ($prev[1] >= $pair[0]-1) { 
     $prev = array($prev[0], max($prev[1], $pair[1])); 
    } else { 
     $newpairs[] = $prev; 
    } 
    } 
    $prev = $pair; 
} 
$pairs = $newpairs; 

现在不应该有任何重叠的对,这样的问题变得你也有一个排序的数组有点简单。

function missing($start, $end, $pairs) { 
    $missing = array(); 
    $prev = false; 
    foreach ($pairs as $pair) { 
    // if the current pair starts above the end, we're done 
    if ($pair[0] > $end) { 
     break; 
    } 

    // we can ignore any pairs that end before the start 
    if ($pair[1] < $start) { 
     continue; 
    } 

    // if the pair encompasses the whole range, nothing is missing 
    if ($pair[0] <= $start && $pair[1] >= $end) { 
     break; 
    } 

    // if this is our first overlapping pair and it starts above 
    // the start we can backfill the missing range 
    if ($pair[0] > $start && !$missing) { 
     $missing[] = array($start, $pair[0]); 
    } 

    // compare this pair to the previous one (if there is one) and 
    // fill in the missing range 
    if ($prev) { 
     $missing[] = array($prev[1]+1, $pair[0]-1); 
    } 

    // set the previous 
    $prev = $pair; 
    } 

    // if this never got set the whole range is missing 
    if (!$prev) { 
    $missing[] = array($start, $end); 

    // if the last overlapping range ended before the end then 
    // we are missing a range from the end of it to the end of 
    // of the relevant range 
    } else if ($prev[1] < $end) { 
    $missing[] = array($prev[1]+1, $end); 
    } 

    // done! 
    return $missing; 
} 

希望有帮助。

+0

这个答案完全为我工作,谢谢:)我不得不做出的唯一的修正,“如果($上一页)......”之前应该如果($去因为对于800-1100,函数只设置$ prev,这意味着当它到达第二对时,它仍然认为它是第一对(因此,从1000- 1500被认为是失踪)。非常感谢你的帮助,cletus :) – 2009-11-30 17:03:44

0

我会做这样的事情:

begin = 1000 
end = 2000 
uncovered =() 
foreach pairs as pair 
    if (pair[0] > begin) 
    push (uncovered, begin, pair[0]) 
    begin = pair[1] 
    end if 
end foreach 

这只是一个想法,但这里是点: 考虑你有一个大段(1000至2000年)和小之一。你想要得到那些没有被小方法覆盖的大方块。想象你有一支笔!

初始开始。迭代每个“小细分”你有。如果你严格地说是在开始之后,那么就有一个“漏洞”,所以你必须记住从开始到当前段的开始。

希望这会有所帮助,而且这是正确的!

0
// your original arrays of integers 
$pairs = array(
    array(800, 1100), 
    array(1500, 1600), 
    array(1900, 2100) 
); 

// first, normalize the whole thing into a giant list of integers that 
// are included in the array pairs, combine and sort numerically 
$numbers_in_pairs = array(); 
foreach($pairs as $set) { 
    $numbers_in_pairs = array_merge($numbers_in_pairs, range($set[0], $set[1])); 
} 
sort($numbers_in_pairs); 

// find the min 
$min = $numbers_in_pairs[0]; 

// find the max 
$max = $numbers_in_pairs[count($numbers_in_pairs)-1]; 

找到阵列差异

// create an array of all numbers inclusive between the min and max 
$all_numbers = range($min, $max); 

// the numbers NOT included in the set can be found by doing array_diff 
// between the two arrays, we need to sort this to assure no errors when 
// we iterate over it to get the maxes and mins 
$not_in_set = array_diff($all_numbers, $numbers_in_pairs); 
sort($not_in_set); 

有关的元数据,我们以后将使用集:

// gather metadata about the numbers that are not inside the set 
// $not_in_set_meta['min'] = lowest integer 
// $not_in_set_meta['max'] = highest integer 
// $not_in_set_meta['mins'] = min boundary integer 
// $not_in_set_meta['maxes'] = max boundary integer 
$not_in_set_meta = array(); 
for($i=0;$i<count($not_in_set);$i++) { 
    if ($i == 0) { 
     $not_in_set_meta['min'] = $not_in_set[$i]; 
     $not_in_set_meta['mins'][] = $not_in_set[$i]; 
    } else if ($i == count($not_in_set)-1) { 
     $not_in_set_meta['max'] = $not_in_set[$i]; 
     $not_in_set_meta['maxes'][] = $not_in_set[$i]; 
    } else { 
     // in the event that a number stands alone 
     // that it can be BOTH the min and the max 
     if (($not_in_set[$i+1] - $not_in_set[$i]) > 1) { 
      $not_in_set_meta['maxes'][] = $not_in_set[$i]; 
     } 
     if (($not_in_set[$i] - $not_in_set[$i-1]) > 1) { 
      $not_in_set_meta['mins'][] = $not_in_set[$i]; 
     } 
    } 
} 

最终输出:

// The final variable which we'll dump the ranges not covered into: 
$non_sets = array(); 

while(count($not_in_set_meta['mins']) > 0 && count($not_in_set_meta['maxes'])) { 
    $non_sets[] = array(array_shift($not_in_set_meta['mins']), 
         array_shift($not_in_set_meta['maxes'])); 
} 
// print it out: 
print var_export($non_sets); 

结果:

array (
    0 => 
    array (
    0 => 1101, 
    1 => 1499, 
), 
    1 => 
    array (
    0 => 1601, 
    1 => 1899, 
), 
) 

?>