我将其实现为“获取这些值的中位数”。但这感觉不对(太长,分支点太多),所以我想将其发布在这里以查看您的想法。

<?php
private function calculateMedian($aValues) {
    $aToCareAbout = array();
    foreach ($aValues as $mValue) {
        if ($mValue >= 0) {
            $aToCareAbout[] = $mValue;
        }
    }
    $iCount = count($aToCareAbout);
    sort($aToCareAbout, SORT_NUMERIC);
    if ($iCount > 2) {
        if ($iCount % 2 == 0) {
            return ($aToCareAbout[floor($iCount / 2) - 1] + $aToCareAbout[floor($iCount / 2)]) / 2;
        } else {
            return $aToCareAbout[$iCount / 2];
        }
    } elseif (isset($aToCareAbout[0])) {
        return $aToCareAbout[0];
    } else {
        return 0;
    }
}


评论

第一次迭代中的$ iCount应该是$ count。

#1 楼

函数的第一部分过滤掉负值。这与计算中位数本身无关,因此应远离此函数。

我会怎么做。

在全局范围(或​​静态方法)中创建array_median()函数,如下所示:

/**
 * Adapted from Victor T.'s answer
 */
function array_median($array) {
  // perhaps all non numeric values should filtered out of $array here?
  $iCount = count($array);
  if ($iCount == 0) {
    throw new DomainException('Median of an empty array is undefined');
  }
  // if we're down here it must mean $array
  // has at least 1 item in the array.
  $middle_index = floor($iCount / 2);
  sort($array, SORT_NUMERIC);
  $median = $array[$middle_index]; // assume an odd # of items
  // Handle the even case by averaging the middle 2 items
  if ($iCount % 2 == 0) {
    $median = ($median + $array[$middle_index - 1]) / 2;
  }
  return $median;
}


这样,我们通常可以使用通用目的的函数,其命名与核心php函数一致。

您的方法看起来像

/**
 * The name should probably be changed, to reflect more your business intent.
 */
private function calculateMedian($aValues) {
  return array_median(
    array_filter(
      $aValues, 
      function($v) {return (is_numeric($v) && $v >= 0);}
      // You can skip is_numeric() check here, if you know all values in $aValues are actually numeric 
    )
  );
}


无论是在calculateMedian()内还是在调用它的代码中,您都应注意捕获如果数组为空时可能引发的DomainException)。

#2 楼

我想知道是否可以将上面的内容压缩为:

private function calculateMedian($aValues) {
    $aToCareAbout = array();
    foreach ($aValues as $mValue) {
        if ($mValue >= 0) {
            $aToCareAbout[] = $mValue;
        }
    }
    $iCount = count($aToCareAbout);
    if ($iCount == 0) return 0;

    // if we're down here it must mean $aToCareAbout
    // has at least 1 item in the array.
    $middle_index = floor($iCount / 2);
    sort($aToCareAbout, SORT_NUMERIC);
    $median = $aToCareAbout[$middle_index]; // assume an odd # of items

    // Handle the even case by averaging the middle 2 items
    if ($iCount % 2 == 0)
        $median = ($median + $aToCareAbout[$middle_index - 1]) / 2;

    return $median;
}


我不是写PHP的,而是从在线手册中获取计数:


count()对于未设置的变量
可能返回0,但对于用
初始化的变量也可能返回
0。一个空数组。使用
isset()测试是否设置了变量。


但是在您的情况下,该函数似乎并不关心数组是空还是变量是否为空。未设置-在两种情况下均返回0。通过检查返回的计数,我们可以消除一些if分支。

评论


\ $ \ begingroup \ $
我不喜欢它为空的$ aToCareAbout返回0。在这种情况下,我希望返回null。 (或者可能引发异常?)
\ $ \ endgroup \ $
– Mchl
2011-1-26在8:21



\ $ \ begingroup \ $
@Mchl是的,这是困扰我的其他要点之一。原始代码使用0表示某种问题。但是,零填充数组也可以具有有效的中位数。
\ $ \ endgroup \ $
–狼人
2011年1月26日上午8:26

\ $ \ begingroup \ $
平均值应该为+1吗? (或者对于2个元素,它将尝试访问[-1]?不久将提供包含所有反馈的第一次迭代。
\ $ \ endgroup \ $
– edorian
2011年1月26日上午8:43

\ $ \ begingroup \ $
@edorian通过查看您的原始代码,我假设php数组是基于0的,例如C。因此,如果一个数组包含2个元素,iCount为2,middle_index将为1,middle_index-1将为0然后将这两个索引中的元素平均。
\ $ \ endgroup \ $
–狼人
2011年1月26日9:00

\ $ \ begingroup \ $
@edorian:不,这是正确的方法。两个简单的例子:奇数:(3个元素,索引0到2,预期= 1)中位数是元素floor(3/2)==1。通过。偶数:(4个元素,索引0到3,预期=平均1和2)对于4个元素,它将使用floor(4/2)==2。元素数量为偶数,因此使用元素2-1 = 1。中位数是元素1和2的平均值。
\ $ \ endgroup \ $
–doppelgreener
2011年1月26日9:03



#3 楼

就我个人而言,这就是构建函数的方式:

function calculateMedian($Values)
{
    //Remove array items less than 1
    $Values = array_filter($Values,array($this,"callback"));

    //Sort the array into descending order 1 - ?
    sort($Values, SORT_NUMERIC);

    //Find out the total amount of elements in the array
    $Count = count($Values);

    //Check the amount of remainders to calculate odd/even
    if($Count % 2 == 0)
    {
        return $Values[$Count / 2];
    }

    return (($Values[($Count / 2)] + $Values[($Count / 2) - 1]) / 2);
}


我做了什么更改?


我已经使用过减少变量,覆盖$Values
将条件语句从2减少到1 *
使代码看起来更具可读性和可理解性。在回调中必须使用foreach和if语句,但要进行逻辑检查。回调将是类中的一种简单方法,如下所示:

public function callback($value) { return $value > 0; }


不幸的是,由于本地函数empty实际上是一种语言构造,因此不是有效的回调,但是您可以在回调方法中使用return !empty($value);来删除其他实体,例如NULLFALSE等。
可以按照说明将其删除,并放置在函数外部。

*注意:建议您进行某种线性数组检查,以确保数组基于整数索引(正如我们的代码假定的那样),可以像这样进行线性处理:

if(array_keys($Values) !== range(0,($Count-1)))
{
    return null;
}


将在$Count值起作用后添加。

我用来测试的示例测试是:

$values = array(
    0,4,7,5,6,9,5,3,2,7,5,6,4,3,7
);
echo calculateMedian($values);


得出正确答案为5

评论


\ $ \ begingroup \ $
我不是故意要挑剔,但您是否有特定的原因将变量名大写(其中的一些)?我认为通常使用与问题相同的变量命名约定作为答案的好主意(除非问题的命名约定与语言的约定相矛盾)。
\ $ \ endgroup \ $
–sepp2k
2011年1月26日19:42

\ $ \ begingroup \ $
没有理由,这只是我倾向于编写代码的方式,即时通讯显示了我的逻辑的好处,OP可以相应地更改它们,因为大写只是我喜欢使用的命名约定。
\ $ \ endgroup \ $
– RobertPitt
2011年1月27日,0:53

\ $ \ begingroup \ $
通过将> = 0转换为> 0,您已经默默地更改了含义。
\ $ \ endgroup \ $
– Fred Nurk
2011年1月27日,9:13

\ $ \ begingroup \ $
Fred,我相信您对回调的看法是,您是在说计算中应该包含零值吗?
\ $ \ endgroup \ $
– RobertPitt
2011年1月27日在9:17