数直線上の範囲が重複しているか否かを判別する簡易な条件

 単に範囲Aと範囲Bが被っているか否かを識別するならば、次の関数があっさり書けて便利です。

/** 範囲が被っているならば true */
function isOverLap($a, $b)
{
    // 範囲が被らない条件を全て否定できれば、範囲が被っている
    // Aの終点がBの始点の左側にあるか、Aの終点がBの始点の右側にあれば被っていない
    // 被っていないならば true になる条件の否定で、被っているならば true
    return !($a['end'] < $b['start'] || $b['end'] < $a['start']);
}

 図でざっくばらんに説明すると次です。

# Aの終点がBの始点の左側にある
A |----|
B            |----|
# Aの始点がBの終点の右側にある
A            |----|
B |----|
# それ以外の場合は被っている

 求められているものが被っているか否かの判定のみでしたら、これであっさり作れます。一つ一つ被る条件を確かめるより計算量が少なく済むのもなかなかうれしいです。

 被っているいないの真偽にとどまらず両者の関係も分けるならば、次の様に数直線上の並びを表現した if を繰り返すのが単純で理解しやすいです。

<?php

/** 被っているか被っていないか、それはどういう状況なのかの種類をまとめた enum */
enum OverlapType
{
    case NO_OVERLAP_A_MORE_LEFT_OF_B;
    case NO_OVERLAP_A_MORE_RIGHT_OF_B;
    case A_RIGHT_LAP_B_LEFT;
    case A_INCLUDE_B;
    case A_INCLUDED_B;
    case A_LEFT_LAP_B_RIGHT;
}

/** 図解コメント付き */
function getOverlapTypeWithChartComment($a, $b): OverlapType
{
    if (isset($a['end'], $a['start'], $b['end'], $b['start'])) {
        throw new \InvalidArgumentException('存在しない値');
    }
    if ($a['end'] < $a['start'] || $b['end'] < $b['start']) {
        throw new \InvalidArgumentException('不正な大小');
    }
    // AがBの左側にある
    if ($a['end'] < $b['start']) {
        return OverlapType::NO_OVERLAP_A_MORE_LEFT_OF_B;
    }
    // AはBの右側にある
    if ($b['end'] < $a['start']) {
        return OverlapType::NO_OVERLAP_A_MORE_RIGHT_OF_B;
    }
    // Aの右端にBの左端が被っている
    // A |----|
    // B    |----|
    if ($a['start'] <= $b['start'] && $b['start'] <= $a['end'] && $a['end'] <= $b['end']) {
        return OverlapType::A_RIGHT_LAP_B_LEFT;
    }
    // # AがBを包含している
    // A |-----------|
    // B    |----|
    if ($a['start'] <= $b['start'] && $b['start'] <= $b['end'] && $b['end'] <= $a['end']) {
        return OverlapType::A_INCLUDE_B;
    }
    // # AがBに包含されている
    // A    |----|
    // B |-----------|
    if ($b['start'] <= $a['start'] && $a['start'] <= $a['end'] && $a['end'] <= $b['end']) {
        return OverlapType::A_INCLUDED_B;
    }

    // # Aの左端にBの右端が被っている
    // A           |----|
    // B |-----------|
    if ($b['start'] <= $a['start'] && $a['start'] <= $b['end'] && $b['end'] <= $a['end']) {
        return OverlapType::A_LEFT_LAP_B_RIGHT;
    }

    throw new \RuntimeException('想定していない分岐');
}

/** 名前の工夫のみ */
function getOverlapTypeWithValName($a, $b): OverlapType
{
    if (isset($a['end'], $a['start'], $b['end'], $b['start'])) {
        throw new \InvalidArgumentException('存在しない値');
    }
    if ($a['end'] < $a['start'] || $b['end'] < $b['start']) {
        throw new \InvalidArgumentException('不正な大小');
    }
    // AS,AE,BS,BE で今どれを参照しているか見やすくするために変数化
    $AStart = $a['start'];
    $AEnd   = $a['end'];
    $BStart = $b['start'];
    $BEnd   = $b['end'];

    if ($AEnd < $BStart) {// AはBの左側にある
        return OverlapType::NO_OVERLAP_A_MORE_LEFT_OF_B;
    }
    if ($BEnd < $AStart) {// AはBの右側にある
        return OverlapType::NO_OVERLAP_A_MORE_RIGHT_OF_B;
    }
    // Aの右端にBの左端が被っている
    if ($AStart <= $BStart && $BStart <= $AEnd && $AEnd <= $BEnd) {
        return OverlapType::A_RIGHT_LAP_B_LEFT;
    }
    // AがBを包含している
    if ($AStart <= $BStart && $BStart <= $BEnd && $BEnd <= $AEnd) {
        return OverlapType::A_INCLUDE_B;
    }
    // AがBに包含されている
    if ($BStart <= $AStart && $AStart <= $AEnd && $AEnd <= $BEnd) {
        return OverlapType::A_INCLUDED_B;
    }
    // Aの左端にBの右端が被っている
    if ($BStart <= $AStart && $AStart <= $BEnd && $BEnd <= $AEnd) {
        return OverlapType::A_LEFT_LAP_B_RIGHT;
    }

    throw new \RuntimeException('想定していない分岐');
}

>株式会社シーポイントラボ

株式会社シーポイントラボ

TEL:053-543-9889
営業時間:9:00~18:00(月〜金)
住所:〒432-8003
   静岡県浜松市中央区和地山3-1-7
   浜松イノベーションキューブ 315
※ご来社の際はインターホンで「316」をお呼びください

CTR IMG