微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

非连续移动平均线

如何解决非连续移动平均线

我们有抽样数据。每次我们有不同的采样值时:

We have sampling data. on each time we have a different sampling value.

每个样本是 (time,value) -> 样本列表 [(t1,v1),...(tn,vn)]

我们的工作是实现moving_avg(data,window_sec)。 数据 -> 样本列表 [(t1,vn)] Windows_secs -> 以秒为单位的窗口大小

示例输入: 数据 = [(1,2),(2,4),(3,3),(4,(6,8),(8,(12,1)]

moving_avg(data,2) 返回: [(1,5),3) 返回: [(1,2.75),4.33),1)]

如何在 C# 中以最佳时间复杂度实现它?面试官告诉我我不能使用额外的空间(我在字典中的面试中使用过)

解决方法

假设具有更大 t 值的连续样本,一个简单的先验项目列表应该足以处理任务。将每个传入的值添加到缓冲区,删除窗口外的所有时间,然后对这些值求平均值。

像这样:

public class TimedValue
{
    public readonly int Time;
    public readonly double Value;
    
    public TimedValue(int time,double value)
    {
        Time = time;
        Value = value;
    }
}

public static partial class Extensions
{
    public static IEnumerable<TimedValue> MovingAverage(this IEnumerable<TimedValue> source,int numSeconds)
    {
        var buffer = new List<TimedValue>();
        
        foreach (var item in source)
        {
            // Add next item to the buffer
            buffer.Add(item);

            // Remove expired items from buffer
            int limit = item.Time - numSeconds;
            while (buffer.Count > 0 && buffer[0].Time < limit)
                buffer.RemoveAt(0);

            // Calculate average and yield back
            double average = buffer.Average(i => i.Value);
            yield return new TimedValue(item.Time,average);
        }
    }
}

不确定标题中的“非连续”是什么意思,但如果您不想使用 IEnumerable<> 和 Linq 扩展(这并不总是最有效的方法)来做到这一点,那么有其他方式。

一种方法是分配结果缓冲区并在迭代源数据时填充它。对当前项目使用头指针,对最旧的包含项目使用尾指针,从总和中加减......等等。它看起来像:

static TimedValue[] MakeMovingAverage(TimedValue[] source,int numSeconds)
{
    // Allocate the result buffer
    var result = new TimedValue[source.Length];
    
    // index of the first item to average
    int tail = 0;
    // the running total for the window
    double sum = 0;
    for (int head = 0; head < source.Length; head++)
    {
        // Add the current item to the running total
        sum += source[head].Value;
        
        // Remove values from the running total for expired items
        var limit = source[head].Time - numSeconds;         
        while (source[tail].Time < limit)
            sum -= source[tail++].Value;
        
        // Set windowed average for this item
        var average = sum / (head + 1 - tail);
        result[head] = new TimedValue(source[head].Time,average);
    }
    return result;
}

对于数组输入,这更有效,因为每个输入项只处理一次。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。