Перевод функции Фортрана в C # - PullRequest
0 голосов
/ 19 мая 2018

Я смотрю на приведенный ниже код на Фортране, который вычисляет скользящее среднее и пытаюсь выяснить, является ли это скользящее, центрированное или прямое среднее.

subroutine ma(x, n, len, ave)

integer n, len, i, j, k, m, newn
real x(n), ave(n), flen, v

newn = n-len+1
flen = float(len)
v = 0.0
# get the first average
do i = 1,len
    v = v+x(i)
ave(1) = v/flen 
if (newn>1) {
    k = len
    m = 0
    do j = 2, newn {
# window down the array
        k = k+1
        m = m+1
        v = v-x(m)+x(k)
        ave(j) = v/flen 
    }
}
return
end

РЕДАКТИРОВАТЬ: я особенно запутался, потому что эта функция вызывается как

ma(x,n,np,out)

, где x является массивом, а n равен длине x плюс 2*np.Кажется, что второй цикл do будет индексироваться за пределами x.

РЕДАКТИРОВАТЬ 2: Вот как эта подпрограмма называется:

subroutine fts(x,n,np,trend,work)

integer n, np
real x(n), trend(n), work(n)

call ma(x,n,np,trend)
call ma(trend,n-np+1,np,work)
call ma(work,n-2*np+2,3,trend)
return
end

1 Ответ

0 голосов
/ 19 мая 2018

Так что, если я собрал это правильно ...

Эта функция возвращает массив средних.(ave)

Здесь определяется первый элемент этого массива:

do i = 1,len
  v = v+x(i)
ave(1) = v/flen

Он суммирует (len) количество элементов, а затем делит на (len) - фактически (flen), чтопросто int len ​​преобразуется в вещественное (например, float для c #).

Следующее условное выражение на самом деле производит 0 для многих дополнительных (ave) элементов.Число определяется разницей между (n) и (len).

Он добавляет каждое последующее значение к ранее вычисленной сумме, затем получает среднее значение для нового итога путем деления на (k):as (len + 1)

РЕДАКТИРОВАТЬ: я сделал ошибку в моем первоначальном понимании логики.У меня все еще есть код для того, что я описал.Но код фактически перемещает окно по входному набору, каждый раз получая среднее значение для одного и того же числа элементов, а не накапливая среднее для растущего размера ввода.Я также добавил обновленный ответ в нижней части этого поста.

Имея это в виду, попробуйте этот перевод C #:

  public float[] CalcRollingAverage (float[] inputs, int beginAvg, int numInputs)
 {
  float[] averages = new float[(numInputs - beginAvg + 1)];

  float sumUpToBeginAvg = 0;

  for(int i = 0; i < beginAvg; i++)
  {
      sumUpToBeginAvg += inputs[i];
  }

  averages[0] = sumUpToBeginAvg / beginAvg;

  int additionalAvgs = numInputs - beginAvg;

  if(additionalAvgs > 0)
  {
      float currentSum = sumUpToBeginAvg;
      int nextValIndex = numInputs - additionalAvgs - 1;

      for(int j = 1; j <= additionalAvgs; j++)
      {
          currentSum += inputs[nextValIndex];

          averages[j] = currentSum / (nextValIndex + 1); 
      }   

      nextValIndex++;
  }

  return averages;  
}

Пожалуйста, дважды проверьте математику, было много настроек для0 основанный массив в C # против 1 основанного массива в Fortran.

Кроме того, версия Fortran в основном использовала параметр ref для средних значений, я вместо этого создал новый массив и возвратил его.Оба работают, но второй способ более привычен в C #. Я полагаю.

Выше был как можно более прямой перевод.Ниже приведена значительно упрощенная версия, использующая более подходящие структуры данных.

  public List<float> CalcRollingAverage(List<float> inputs, int beginAvg)
  {
  List<float> results = new List<float>();

  int index = 1;
  float currentSumOfInputs = 0;

  foreach(float value in inputs)
  {
      currentSumOfInputs += value;

      if(index >= beginAvg)
      {
          results.Add(currentSumOfInputs / index);
      }

      index++;
    }

    return results;
  }

Обновление, основанное на нашем недавнем обнаружении того, что мне не хватает какой-то оригинальной логики.Этот перемещает окно и сохраняет усредненное количество элементов одинаковым:

public List<float> CalcRollingAverage(List<float> inputs, int windowSize)
{
    List<float> results = new List<float>();

    int index = 1;
    float currentSumOfInputs = 0;

    foreach(float value in inputs)
    {
        currentSumOfInputs += value;

        if(index == windowSize)
        {
            results.Add(currentSumOfInputs / windowSize);
            break;
        }                 
      index++;
    }

  if(inputs.Count > windowSize)
  {
      for(int i = index - windowSize + 1; i <= inputs.Count; i++;)
      {
        currentSumOfInputs = currentSumOfInputs - inputs(index - windowSize);
        currentSumOfInputs += inputs(index);
        results.Add(currentSumOfInputs / windowSize)
        index++;
      }
  }
}
...