【问题标题】:Calculations of ranges of numbers in PHPPHP中数字范围的计算
【发布时间】:2010-12-21 11:32:13
【问题描述】:

首先,感谢您花时间阅读我的问题。

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

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

我要查找的是如何获取数字对未涵盖的范围,介于 1000 和 2000 之间。在此示例中,array(800, 1100) 涵盖了 1000-1100,涵盖了 1500-1600数组(1500、1600)和 1900-2000 被数组(1900、2100)覆盖,这让我剩下 1101-1499 和 1599-1899 来覆盖。我希望我说得够清楚。

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

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

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

提前谢谢你。

【问题讨论】:

    标签: php arrays range integer


    【解决方案1】:

    我会这样做:

    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】:

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

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

      希望对您有所帮助。

      【讨论】:

      • 这个答案对我来说非常有效,谢谢:) 我必须做的唯一更正是“if ($prev)...”应该放在 if ($pair[0} > $ start && !$missing),因为对于 800-1100,该函数只设置 $prev,这意味着当它到达第二对时,它仍然认为它是第一对(因此从 1000-1500 的所有内容都被视为丢失)。非常感谢你的帮助,cletus :)
      【解决方案3】:
      // 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,
        ),
      )
      
      ?>
      

      【讨论】:

        猜你喜欢
        • 2016-03-22
        • 2021-12-23
        • 2021-07-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-12-15
        • 2017-05-17
        相关资源
        最近更新 更多