我有一系列每月利润,我想显示每个月的六个月历史平均值:
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-5
到i
范围的平均值。
$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个。我知道如何做到这一点,但不知道如何处理它的技巧和保存线条/防止丑陋。
答案 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);