如何计算n个数组元素的平均值(如果存在)?

时间:2015-12-12 16:22:49

标签: php arrays loops

我有一系列每月利润,我想显示每个月的六个月历史平均值:

for each month i:

6monthAvg = round(
                (
                $profits[$i]
                + (isset($profits[$i-1]) ? $profits[$i-1] : 0)
                + (isset($profits[$i-2]) ? $profits[$i-2] : 0)
                + (isset($profits[$i-3]) ? $profits[$i-3] : 0)
                + (isset($profits[$i-4]) ? $profits[$i-4] : 0)
                + (isset($profits[$i-5]) ? $profits[$i-5] : 0)
                ) / 6 
            , 2);

您会看到有效平均值仅来自第6个月以后,因为它会计算基于i-5i范围的平均值。

$profits = array(5,7,2,4,7,3,6);
$6moAvg = algorithm($profits);
//Current output =  { 0.83,   2, 2.33,    3, 4.17, 4.67, 4.83}
//Expected output = {    5,   6, 4.67,  4.5,    5, 4.67, 4.83}

或更具声明性的定义:

if month i has >5 predecessors, sum i and last 5 predecessors, then divide by 6
...
if month i has 5 predecessors, sum i and last 5 predecessors, then divide by 6
if month i has 4 predecessors, sum i and last 4 predecessors, then divide by 5
if month i has 3 predecessors, sum i and last 3 predecessors, then divide by 4
if month i has 2 predecessors, sum i and last 2 predecessors, then divide by 3
if month i has 1 predecessor, sum i and last 1 predecessor, then divide by 2
if month i has no predecessor, sum i, then divide by 1

有更简单的方法吗?我可以保留一个非零月份的计数器,然后除以该计数器,但它会变成一行(为了可见性而添加换行符)到20个。我知道如何做到这一点,但不知道如何处理它的技巧和保存线条/防止丑陋。

3 个答案:

答案 0 :(得分:2)

这应该适合你:

只需遍历您的月份数组,然后使用array_slice()获取过去6个月(如果存在)并将其与array_sum()结合起来,例如

<?php

    $profits = [5, 7, 2, 4, 7, 3, 6];

    for($i = 1, $length = count($profits); $i <= $length; $i++) {
        $sixMonthAvg = array_slice($profits, ($i - 6 >= 0 ?$i - 6:0), $i);
        echo round(array_sum($sixMonthAvg) / count($sixMonthAvg), 2) . PHP_EOL;
    }

?>

输出:

5
6
4.67
4.5
5
4.67
4.83

答案 1 :(得分:1)

我没有真正看到多行的问题。此外,您可以使用某种扫描线方法,通常会更快:

function algorithm($profits) {
    $n = count($profits);
    $sum = 0;
    $result = array();
    for($i = 0; $i < $n; $i++) {
        $sum += $profits[$i];
        if($i >= 6) {
            $sum -= $profits[$i-6];
        }
        $result[] = round($sum/min(6,$i+1),2);
    }
    return $result;
}

或者您可以将其表示为单行:

function algorithm($profits) {$n = count($profits);$sum = 0;$result = array();for($i = 0; $i < $n; $i++) {$sum += $profits[$i];if($i >= 6) {$sum -= $profits[$i-6];}$result[] = round($sum/min(6,$i+1),2);}return $result;}

使用扫描线算法的优点是,它不必每个月对六个元素求和。对于每个月,它最多只进行一次加法和一次减法(显然是除法和舍入操作)。但从而使它更快一点。此外,不会从一个列表复制到另一个列表。

演示

使用php -a运行此操作会生成:

$ php -a
Interactive mode enabled

php > function algorithm($profits) {
php {     $n = count($profits);
php {     $sum = 0;
php {     $result = array();
php {     for($i = 0; $i < $n; $i++) {
php {         $sum += $profits[$i];
php {         if($i >= 6) {
php {             $sum -= $profits[$i-6];
php {         }
php {         $result[] = round($sum/min(6,$i+1),2);
php {     }
php {     return $result;
php { }
php > 
php > $profits = array(5,7,2,4,7,3,6);
php > $sixmoAvg = algorithm($profits);
php > var_dump($sixmoAvg);
array(7) {
  [0]=>
  float(5)
  [1]=>
  float(6)
  [2]=>
  float(4.67)
  [3]=>
  float(4.5)
  [4]=>
  float(5)
  [5]=>
  float(4.67)
  [6]=>
  float(4.83)
}

答案 2 :(得分:0)

我最终做了类似下面的事情,因为我觉得它更具可读性:

avg = 1;
for each month i
    if i-1 exists
        avg++;
    if i-2 exists
        avg++;
    ...
    return round(
         (($profits[$i]
         +((isset($profits[$i-1])) ? $profits[$i-1] : 0)
         +((isset($profits[$i-2])) ? $profits[$i-2] : 0)
         ...
         )/avg),2);