получить крайности из списка номеров

Если у меня есть список чисел, подобных этому:

10,9,8,8,9,7,6,5,4,6,7,8,11,10,12,14,16,20,30,29,28,29,27,25,20,18,15,10,8,5,4,1 

Я хочу получить следующие цифры (в соответствии с этим примером): 10,4,30,1

Можно ли написать функцию, которая получит эти крайности?

Эти цифры являются значениями диаграммы, я хочу получить пики диаграммы.

Solutions Collecting From Web of "получить крайности из списка номеров"

Мой похож на jprofitt's

но я разделил их на вершины и долины, поэтому я могу сделать еще кое-что с этим.

Я думаю, что его цикл намного чище, чем мой, но я просто хотел проверить его для себя.
Не судите меня

Этот сценарий просто отображает точки и выбирает пики и долины и дает им зеленый и красный соответственно. Смотрите на это как наглядное пособие. :П

график

 <?php $plot = array(10,9,8,8,9,7,6,5,4,6,7,8,11,10,12,14,16,20,30,29,28,29,27,25,20,18,15,10,8,5,4,1); $res = local_extremes($plot); function local_extremes(array $array){ $peaks = array(); $valleys = array(); $peak_keys = array(); $valley_keys = array(); for($i = 0; $i < count($array); $i++){ $more_than_last = $array[$i] > $array[$i-1]; $more_than_next = $array[$i] > $array[$i+1]; $next_is_equal = $array[$i] == $array[$i+1]; if($next_is_equal) continue; if($i == 0){ if($more_than_next){ $peaks[] = $array[$i]; $peak_keys[] = $i; }else{ $valleys[] = $array[$i]; $valley_keys[] = $i; } }elseif($i == (count($array)-1)){ if($more_than_last){ $peaks[] = $array[$i]; $peak_keys[] = $i; }else{ $valleys[] = $array[$i]; $valley_keys[] = $i; } }else{ if($more_than_last && $more_than_next){ $peaks[] = $array[$i]; $peak_keys[] = $i; }elseif(!$more_than_last && !$more_than_next){ $valleys[] = $array[$i]; $valley_keys[] = $i; } } } return array("peaks" => $peaks, "valleys" => $valleys, "peak_keys" => $peak_keys, "valley_keys" => $valley_keys); } ?> <style type="text/css"> .container{ position: absolute; } .point{ position: absolute; width: 4px; height: 4px; background: black; } .extreme{ position: absolute; top: 5px; } .extr_low{ background: red; } .extr_high{ background: green; } </style> <?php //Plot echo "<div class='container'>"; foreach($plot as $key => $point){ $left = ($key*10); $top = 400 - ($point*10); if(in_array($key, $res['peak_keys']) || in_array($key, $res['valley_keys'])){ $extreme = "<div class='extreme'>$point</div>"; }else{ $extreme = ""; } if(in_array($key, $res['peak_keys'])){ $xc = "extr_high"; }elseif(in_array($key, $res['valley_keys'])){ $xc = "extr_low"; }else{ $xc = ""; } echo "<div class='point $xc' style='left: ".$left."px; top: ".$top."px;'>$extreme</div>"; } echo "</div>"; ?> <table> <tr> <th>&nbsp;</th> <th>Valley</th> <th>Peak</th> </tr> <tr> <th>Lowest</th> <td><?php echo min($res['valleys']); ?></td> <td><?php echo min($res['peaks']); ?></td> </tr> <tr> <th>Highest</th> <td><?php echo max($res['valleys']); ?></td> <td><?php echo max($res['peaks']); ?></td> </tr> </table> 

Я не очень много тестировал, и на самом деле это не сработает ни с чем менее, чем с 3 очками, но это должно дать вам хорошую отправную точку.

 <?php $array = array(10,9,8,8,9,7,6,5,4,6,7,8,11,10,12,14,16,20,30,29,28,29,27,25,20,18,15,10,8,5,4,1); $extremes = array(); $last = null; $num = count($array); for($i=0;$i<$num - 1;$i++) { $curr = $array[$i]; if($last === null) { $extremes[] = $curr; $last = $curr; continue; } //min if($last > $curr && $curr < $array[$i + 1]) { $extremes[] = $curr; } //maxes else if ($last < $curr && $curr > $array[$i + 1]) { $extremes[] = $curr; } if($last != $curr && $curr != $array[$i + 1]) { $last = $curr; } } //add last point $extremes[] = $array[$num - 1]; print_r($extremes); 

Дает вам результаты (вы пропустили пару в своем списке):

 Array ( [0] => 10 [1] => 8 [2] => 9 [3] => 4 [4] => 11 [5] => 10 [6] => 30 [7] => 28 [8] => 29 [9] => 1 ) 

Если вы хотите, чтобы это было точно так же, как в списке, вам нужно применить некоторое сглаживание к данным или некоторые допуски на обнаружение.

Вариант первого теста для определения локальных экстремумов, идентифицировать точки, где треугольник чередует знак от одного интервала к следующему. Эти точки будут максимумами, если дельта переходит от положительного к отрицательному и минимума, если дельта переходит от отрицательного к положительному, но для вашего использования это, похоже, не имеет значения. Кроме того, бросайте в конечные точки, потому что интервал считается открытым для этого теста, и вы, похоже, хотите, чтобы они были включены.

 <?php define('MSB_MASK', (int)(PHP_INT_MAX + 1)); $points = array(0, 0, 1, 0, -1, -2, -2, -3, -4, -3, 0, 1); $extrema = getExtrema($points); function getExtrema($points) { $extrema = array($points[0]); $limit= count($points)-2; for ($i = 0; $i < $limit; ++$i) { $deltai = $points[$i+1]-$points[$i]; $deltaiplus1 = $points[$i+2]-$points[$i+1]; if (($deltai ^ $deltaiplus1) & MSB_MASK) $extrema[] = $points[$i+1]; } $extrema[] = $points[$limit+1]; return $extrema; } ?> 

Примечание. Я тестировал немного на ideone.com, он работает, но у него могут быть необнаруженные проблемы. Это также должно работать для поплавков.

Кредит: Это первый производный тест из каждого учебника Исчисления I, адаптированный только для дискретных математических вычислений. Мы рассматриваем каждую точку как критическую точку, потому что мы не знаем функции для графика.

Редактировать: посмотрев на график данных о вольфрамальфе, я думаю, что, возможно, вы просто ищете глобальный максимум и минимум на закрытом интервале, плюс конечные точки? Если это так, просто используйте что-то простое, например max($points) и min($points) .

Изменить: у меня никогда не было хорошей возможности использовать xor раньше!

да, для каждого числа в строке вы сравниваете его с боковыми номерами, и вы его получили (это число меньше, чем число до и после). Затем добавьте числа первым и последним числом, и это целое.

Я ожидаю, что это какой-то отсортированный массив.

Вот псевдокод для этого

input: listOfNumbers

 //Handle exceptional cases if listOfNumbers.length == 0 return [] if listOfNumbers.length == 1 return [listOfNumbers[0]] //Pre-condition listOfNumbers.length > 1 extremes = emptyList lastNumber = listOfNumbers[0] isIncreasing = listOfNumbers[0] < listOfNumbers[1] extremes.push(listOfNumbers[0]) foreach number in listOfNumbers[1...listOfNumbers.length] if(isIncreasing AND lastNumber > number) extremes.push(lastNumber) isIncreasing = false if(NOT isIncreasing AND lastNumber < number) extremes.push(lastNumber) isIncreasing = true extremes.push(listOfNumbers.length-1) return extremes 

Я думаю, что это будет сделано, хотя я не проверял это.

Получите первое и последнее число из массива чисел, затем отсортируйте массив и возьмите первый и последний, которые отличаются вашим последним результатом. И у вас будет такой результат, как ваш пример.