22

単純移動平均を計算するための最速のライブラリ/アルゴリズムは何ですか? 私は自分で書きましたが、330 000 アイテムの 10 進データセットでは時間がかかりすぎます。

  • 周期/時間(ミリ秒)
  • 20/300;
  • 60/1500;
  • 120/3500。

私のメソッドのコードは次のとおりです。

public decimal MA_Simple(int period, int ii) {
    if (period != 0 && ii > period) {
        //stp.Start();
        decimal summ = 0;
        for (int i = ii; i > ii - period; i--) {
            summ = summ + Data.Close[i];
        }
        summ = summ / period;
        //stp.Stop();
        //if (ii == 1500) System.Windows.Forms.MessageBox.Show((stp.ElapsedTicks * 1000.0) / Stopwatch.Frequency + " ms");
        return summ;
    } else return -1;
}

これData.Close[]は、固定サイズ (1 000 000) の 10 進配列です。

4

16 に答える 16

16

あなたの主な問題は、反復ごとにあまりにも多くの情報を捨てることです。これを高速に実行するには、フレームの長さと同じサイズのバッファーを保持する必要があります。

このコードは、データセット全体の移動平均を実行します。

(実際のC#ではありませんが、アイデアを得る必要があります)

decimal buffer[] = new decimal[period];
decimal output[] = new decimal[data.Length];
current_index = 0;
for (int i=0; i<data.Length; i++)
    {
        buffer[current_index] = data[i]/period;
        decimal ma = 0.0;
        for (int j=0;j<period;j++)
            {
                ma += buffer[j];
            }
        output[i] = ma;
        current_index = (current_index + 1) % period;
    }
return output;

バッファ全体を保持して各反復の値を計算する代わりに、実行中の累積を維持したくなるかもしれませんが、これは非常に長いデータ長では機能しません。累積合計が非常に大きくなり、小さな値を追加すると、丸め誤差が発生します。

于 2012-10-14T21:44:00.373 に答える
5

データが静的な場合は、配列を前処理して、移動平均クエリを非常に高速にすることができます。

decimal[] GetCSum(decimal[] data) {
    decimal csum[] = new decimal[data.Length];
    decimal cursum = 0;
    for(int i=0; i<data.Length; i++) {
        cursum += data[i];
        csum[i] = cursum;
    }
    return csum;
}

これで、移動平均の計算が簡単かつ高速になりました。

decimal CSumMovingAverage(decimal[] csum, int period, int ii) {
    if(period == 0 || ii <= period)
        return -1;
    return csum[ii] - csum[ii - period];
}
于 2012-10-14T17:37:10.907 に答える
2

現在の (受け入れられた) 解決策には、内部ループが含まれています。これも削除した方が効率的です。これがどのように達成されるかは、次の場所で確認できます。

移動標準偏差を効率的に計算する方法

于 2016-02-19T21:13:48.240 に答える
1
// simple moving average
int moving_average(double *values, double *&averages, int size, int periods)
{
    double sum = 0;
    for (int i = 0; i < size; i ++)
        if (i < periods) {
            sum += values[i];
            averages[i] = (i == periods - 1) ? sum / (double)periods : 0;
        } else {
            sum = sum - values[i - periods] + values[i];
            averages[i] = sum / (double)periods;
        }
    return (size - periods + 1 > 0) ? size - periods + 1 : 0;
}

1 つの C 関数、13 行のコード、単純移動平均。使用例:

double *values = new double[10]; // the input
double *averages = new double[10]; // the output
values[0] = 55;
values[1] = 113;
values[2] = 92.6;
...
values[9] = 23;
moving_average(values, averages, 10, 5); // 5-day moving average
于 2013-12-21T09:12:57.763 に答える
1
/// <summary>
/// Fast low CPU usage moving average based on floating point math
/// Note: This algorithm algorithm compensates for floating point error by re-summing the buffer for every 1000 values
/// </summary>
public class FastMovingAverageDouble
{
    /// <summary>
    /// Adjust this as you see fit to suit the scenario
    /// </summary>
    const int MaximumWindowSize = 100;

    /// <summary>
    /// Adjust this as you see fit
    /// </summary>
    const int RecalculateEveryXValues = 1000;

    /// <summary>
    /// Initializes moving average for specified window size
    /// </summary>
    /// <param name="_WindowSize">Size of moving average window between 2 and MaximumWindowSize 
    /// Note: this value should not be too large and also bear in mind the possibility of overflow and floating point error as this class internally keeps a sum of the values within the window</param>
    public FastMovingAverageDouble(int _WindowSize)
    {
        if (_WindowSize < 2)
        {
            _WindowSize = 2;
        }
        else if (_WindowSize > MaximumWindowSize)
        {
            _WindowSize = MaximumWindowSize;
        }
        m_WindowSize = _WindowSize;
    }
    private object SyncRoot = new object();
    private Queue<double> Buffer = new Queue<double>();
    private int m_WindowSize;
    private double m_MovingAverage = 0d;
    private double MovingSum = 0d;
    private bool BufferFull;
    private int Counter = 0;

    /// <summary>
    /// Calculated moving average
    /// </summary>
    public double MovingAverage
    {
        get
        {
            lock (SyncRoot)
            {
                return m_MovingAverage;
            }
        }
    }

    /// <summary>
    /// Size of moving average window set by constructor during intialization
    /// </summary>
    public int WindowSize
    {
        get
        {
            return m_WindowSize;
        }
    }

    /// <summary>
    /// Add new value to sequence and recalculate moving average seee <see cref="MovingAverage"/>
    /// </summary>
    /// <param name="NewValue">New value to be added</param>
    public void AddValue(int NewValue)
    {
        lock (SyncRoot)
        {
            Buffer.Enqueue(NewValue);
            MovingSum += NewValue;
            if (!BufferFull)
            {
                int BufferSize = Buffer.Count;
                BufferFull = BufferSize == WindowSize;
                m_MovingAverage = MovingSum / BufferSize;
            }
            else
            {
                Counter += 1;
                if (Counter > RecalculateEveryXValues)
                {
                    MovingSum = 0;
                    foreach (double BufferValue in Buffer)
                    {
                        MovingSum += BufferValue;
                    }
                    Counter = 0;
                }
                MovingSum -= Buffer.Dequeue();
                m_MovingAverage = MovingSum / WindowSize;
            }
        }
    }
}
于 2017-01-18T14:36:32.210 に答える