PHP中数字范围的计算

时间:2009-11-29 22:29:53

标签: php arrays range integer

首先,感谢您抽出宝贵时间阅读我的问题。

我正在尝试编写一个脚本,而且我遇到了一个我很难解决的问题。我正在使用一对数字(例如,1000和2000),我有一对数字对:

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

我想要找到的是如何获得数字对未涵盖的范围,介于1000和2000之间。在此示例中,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 个答案:

答案 0 :(得分:3)

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

  1. 这些对是否排序?
  2. 对是否重叠?
  3. 您想找到特定范围的缺失范围(这似乎是这种情况)?
  4. 如果未对对进行排序,请先对它们进行排序:

    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;
    }
    

    希望有所帮助。

答案 1 :(得分: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)和小部分。你想得到那个小部分未覆盖的大部分。想象一下,你有一支笔!

初始化。迭代你拥有的每个“小段”。如果您(严格地)在开头之后,那么就会有一个“洞”,所以你必须记住从开始到当前段的开头。

希望这有帮助,这是正确的!

答案 2 :(得分: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,
  ),
)

?>