从数字列表中获取极端情况

时间:2012-04-02 15:31:36

标签: php numbers

如果我有一个像这样的数字列表:

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

是否可以编写一个能够获得这些极端值的函数?

这些数字是图表的值,我希望获得图表的峰值。

6 个答案:

答案 0 :(得分:5)

我没有测试过这么多,并且它不适用于少于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
)

如果你想让它与列表完全一样,你必须对数据应用一些平滑处理,或者对检测采用一些容差。

答案 1 :(得分:3)

用于识别局部极值的一阶导数检验的变体,识别δ从一个间隔到下一个间隔交替标记的点。如果delta从正变为负,这些点将是最大值,如果delta从负变为正,则这些点将是最小值,但对于您的使用似乎并不重要。此外,抛出终点因为此测试的间隔被认为是开放的,您似乎希望将它们包括在内。

<?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教科书的第一个衍生测试,仅适用于离散数学。我们将每个点都视为关键点,因为我们不知道图的功能。

编辑:在查看wolframalpha上的数据图之后,我想也许您只是在关闭的时间间隔内寻找全局最大值和最小值,加上结束点?如果是这样,只需使用简单的内容,例如max($points)min($points)

编辑:我以前从未有过使用xor的好机会!

答案 2 :(得分:2)

我的类似于jprofitt's

但是我把它们分成了山峰和山谷,所以我可以用它做更多的东西。

我认为他的循环比我的循环要干净得多,但我只是想为自己测试一下 不要判断我 ee ee e

这个脚本只是绘制出点并选择峰和谷,分别给它们分别为绿色和红色。将其视为视觉辅助。 :P

graph

<?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 :(得分:1)

是的,对于行中的每个数字,您将它与边数进行比较,然后得到它(您需要的数字小于数字之前和之后)。然后添加数字的第一个和最后一个数字,它是完整的。

我希望它是一些排序数组。

答案 4 :(得分:1)

这是执行此操作的伪代码

输入: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

我认为这样做会有,尽管我没有对此进行过测试。

答案 5 :(得分:0)

从数字数组中获取第一个和最后一个数字,然后对数组进行排序,并获取与您的上一个结果不同的第一个和最后一个数字。你将得到一个像你的例子的结果。