3

並べ替えたい月の配列(数値、1〜12)があり、これが最大の連続シーケンスになります。例:

array(1,2,3)

シンプルです、それは1月から3月です、ソートは必要ありません

array(1,2,11,12)

これは、11月から2月までの1つの継続的なシーケンスである可能性があるため、11,12,1,2のように並べ替える必要があります。

array(1,4,5,11,12)

これは4月から5月、11月から1月であるため、4,5と11,12,1の2つに分割する必要があります。

これを行う方法はありますか?

4

5 に答える 5

2

12これは、にラップアラウンドできるという事実を表現する方法を理解すれば簡単に実行できます1

これが私の解決策です。着信配列を自然にソートしてから、$months次のような配列を作成します。

$months = 1 2 3 4 5 6 7 8 9 10 11 12 1 2 3 4 5 6 7 8 9 10 11 12 

その配列から、入力配列をループして、月の値が設定されていない場所にゼロを配置します。したがって、の入力配列を使用するarray( 1, 2, 11, 12)と、$inputs配列は次のようになります。

$inputs = 1 2 0 0 0 0 0 0 0 0  11 12 1 2 0 0 0 0 0 0 0 0  11 12

ここから、アルゴリズムは単純です。$inputs配列をループして、0ではない最長のシーケンスを見つけます。これにより、可能なすべてのシーケンシャルシーケンスが生成されます。

function sort_months( $array)
{
    natsort( $array);

    $keys = array_flip( $array);
    $inputs = array();
    $months = array_merge( range( 1, 12), range( 1, 12));

    foreach( $months as $m) {
        $inputs[] = (isset( $keys[$m])) ? $m : 0;
    }

    $sequences = array();

    for( $i = 0, $ii = count( $inputs); $i < $ii; $i++) {
        if( $inputs[$i] != 0) {
            $sequence = array( $inputs[$i]);
            for( $k = $i + 1, $kk = $ii + 1; $k < $kk;  $sequence[] = $inputs[$k], $k++) {
                if( !isset( $inputs[$k]) || $inputs[$k] == 0) {
                    break;
                }
            }
            $sequences[] = $sequence;
        }
    }

    return $sequences;
}

入力するarray( 12, 11, 1, 2)と、これは以下を出力します:

array(8) {
  [0]=>
  array(2) {
    [0]=>
    int(1)
    [1]=>
    int(2)
  }
  [1]=>
  array(1) {
    [0]=>
    int(2)
  }
  [2]=>
  array(4) {
    [0]=>
    int(11)
    [1]=>
    int(12)
    [2]=>
    int(1)
    [3]=>
    int(2)
  }
  [3]=>
  array(3) {
    [0]=>
    int(12)
    [1]=>
    int(1)
    [2]=>
    int(2)
  }
  [4]=>
  array(2) {
    [0]=>
    int(1)
    [1]=>
    int(2)
  }
  [5]=>
  array(1) {
    [0]=>
    int(2)
  }
  [6]=>
  array(2) {
    [0]=>
    int(11)
    [1]=>
    int(12)
  }
  [7]=>
  array(1) {
    [0]=>
    int(12)
  }
}

最長のシーケンシャルシーケンスが実際に(他のすべての可能なシーケンスとともに)配列に存在することがわかります。

[2]=>
  array(4) {
    [0]=>
    int(11)
    [1]=>
    int(12)
    [2]=>
    int(1)
    [3]=>
    int(2)
  }

関数に入力されたすべての月を表す、使用するシーケンスを選択する方法を決定するのはOPに任せています。

于 2012-07-20T14:06:50.683 に答える
2

データを見ると、次の前提条件があります。

  1. 最大範囲は1〜12です。
  2. 入力はソートされているため、順序付けられています。
  3. ラップアラウンドが発生する場所は12から1だけです。

これを考慮に入れると、次のように言うことができます。

  1. 最初の値は1であるか、ラップアラウンドはありません。
  2. そして最後の値は12であるか、ラップアラウンドはありません。
  3. ラップアラウンドがある場合は、最初から連続するすべての月(1,2、...)がラップアラウンドされます。

これは比較的単純な関数です。

function group_months(array $months) {
    $count = count($months);
    if (!$count || $months[0] != 1 || $months[$count-1] != 12 || $count === 12) {
        return $months;
    }
    for ($size = 1; $months[$size] === $size+1;) {
        $size++;
    }
    return array_merge(array_slice($months, $size), range(1, $size));
}

Or in a shift-push variant (move 1,2,... to end):

function group_months(array $months)
{
    $count = count($months);
    if ($count && $count != 12 && $months[$count - 1] === 12) {
        for ($month = 1; $months[0] === $month; $month++) {
            $months[] = array_shift($months);
        }
    }
    return $months;
}

Or in a pop-unshift variant (Move ..,11,12 to front):

function group_months(array $months)
{
    $count = count($months);
    if ($count-- && $count != 11 && $months[0] === 1) {
        for ($month = 12; $months[$count] === $month; $month--) {
            array_unshift($months, array_pop($months));
        }
    }
    return $months;
}

If you then want to group the numbers in the array, please see a related question that has a solution for string output already:

于 2012-07-21T20:33:55.953 に答える
1

私のPHPは実装を書くのに十分ではありませんが、循環配列またはリンクリスト構造を使用してこれが可能かどうかを知りたいと思います。

まず、12個のブール要素でこのような構造を作成しnext、最後の要素のプロパティを最初の要素に設定して円形にします。逆に、previous最初の要素は最後の要素に設定されます。これは、「ラップアラウンド」カレンダーを表します。

次に、配列を読み込んで、「カレンダー」の各要素をtrue、入力配列の要素インデックスが表示される場所に設定します。

true次に、壊れていない最大の要素シーケンスが見つかるまで、「カレンダー」をループします。繰り返して小さいものを見つけ、それらを配列として出力します。

うまくいけば、それがあなたにとって実行可能な出発点であり、誰かがPHPでそれを実現するのを見たいと思います!

于 2012-07-20T13:55:58.933 に答える
1

以下のPHP関数を試してください。与えられた3つの例すべてでうまく機能します:

<pre>
<?php
function split_months($array)
{
    // remove duplicate values, if any
    // sort the array in ascending order
    $array = array_unique($array);
    sort($array);

    $results = array();

    // loop until the array is empty
    while(!empty($array))
    {
        // extract the first entry of the array
        $entry = array_shift($array);

        if(empty($results))
            $results[] = array($entry);
        else
        {
            // find in which sub-array of $results $entry needs to be stored
            foreach($results as $index => $values)
            {
                // extract the last value
                $last = array_pop($values);

                // compare with $entry
                if($entry-1 == $last)
                {
                    $results[$index][] = $entry;
                    unset($entry);
                    break;
                }
            }

            // there was no sub-array to store $entry: store it in a new sub-array
            if(isset($entry))
            $results[] = array($entry);
        }
    }

    // if $results contain no array, or only one, there is nothing to optimize
    if(sizeof($results) <= 1)
        return $results;

    // lastly, search if one result sub-array is starting with "1", and another is ending with "12"
    // in that case, join these 2 sub-arrays in one
    foreach($results as $index => $values)
    {
        if($values[0] == 1)
            $index1 = $index;
        elseif($values[sizeof($values)-1] == 12)
            $index12 = $index;

        if(isset($index1) && isset($index12))
            break;
    }
    if(isset($index1) && isset($index12))
    {
            // merge both sub-arrays
        $results[$index12] = array_merge($results[$index12], $results[$index1]);
            // remove the sub-array starting with "1"
        unset($results[$index1]);
    }

    return $results;
}

$array1 = array(1,2,3);
$array2 = array(1,2,11,12);
$array3 = array(1,4,5,11,12);

print_r(split_months($array1));
print_r(split_months($array2));
print_r(split_months($array3));
?>
</pre>

出力は次のようになります。

Array
(
    [0] => Array
        (
            [0] => 1
            [1] => 2
            [2] => 3
        )

)
Array
(
    [1] => Array
        (
            [0] => 11
            [1] => 12
            [2] => 1
            [3] => 2
        )

)
Array
(
    [1] => Array
        (
            [0] => 4
            [1] => 5
        )

    [2] => Array
        (
            [0] => 11
            [1] => 12
            [2] => 1
        )

)
于 2012-07-20T14:14:55.347 に答える
0

あなたが正しいことをしているのかどうかはわかりません。私はあなたの3番目のような例を意味します。array(1, 2, 4, 5, 11, 12)これらが1月から2月、4月から5月、11月から12月の3つのシーケンス、または11月から2月と4月から5月の2つのシーケンスではないことをどのように確認できますか。このシーケンスを制御するユーザー入力、または私がシャッピングしている他の何かがありますか?

于 2012-07-20T13:21:56.113 に答える