将FORTRAN函数转换为C#

时间:2018-05-18 21:25:47

标签: c# fortran

我正在查看以下fortran代码,该代码计算移动平均线并试图确定它是否为尾随,居中或前向平均值。

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等于x2*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 :(得分:0)

所以如果我把它正确拼凑起来......

此函数返回一组平均值。 (AVE)

该数组的第一个元素在此处确定:

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

它将(len)元素的数量相加,然后除以(len) - 实际上(flen),它只是转换为实数的int len(认为是c#的浮点数)。

以下条件实际上产生0到许多其他(ave)元素。 数字由(n)和(len)之间的差异决定。

它将每个连续值添加到先前计算的总和中,然后除以(k)得到新总数的平均值:以(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;  
}

请仔细检查数学,对于Fortran中C#vs 1基于数组的基于0的数组进行了大量调整。

此外,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++;
      }
  }
}