【问题标题】:calculate moving average of an static array in c计算c中静态数组的移动平均值
【发布时间】:2021-08-11 11:14:49
【问题描述】:

我编写了这段代码来计算 c 中数组的移动平均值。

Array_MovingAverage(const int inputSeries[], 
                    size_t inputSize, 
                    size_t window, 
                    float output[],
                    size_t outputSize) {
  if (inputSeries && output != NULL){
    for(size_t i = 0; i < window ; i++)
      if (window < inputSize)
        if (inputSeries != NULL && output != NULL) {
          if(outputSize >= inputSize) {
            size_t inputSize[11] =  {1, 2, 2, 3, 6, 8, 9, 2, 1, 2, 1};
            const uint8_t window = 5;
            {
              const int inputSeries[] = {1, 2, 2, 3, 6, 8, 9, 2, 1, 2,1};

              double window = 5;
              double c = 2.0;
              double d = 2.0;

              for(int i = 0; i < window; i++)
              {
                c += inputSeries[i];
                d = c / window;
              }

              return true;
            }
          }
        }
    }
  }

我一直在尝试计算 C 中数组的移动平均值并获得所需的输出,但它似乎不起作用。您能否给我一个建议,如何计算 C 中静态数组的移动平均值?

输出应该是:

Moving Average: 1 2 2 3 6 8 9 2 1 2 1
                0 0 0 0 3 4 6 6 5 4 3

【问题讨论】:

  • 你为那个函数提供了什么数据,你怎么称呼它。你得到什么输出而不是你显示的预期输出?
  • 为什么要用局部变量覆盖参数?为什么你要在一个循环中进行所有这些if 检查?进入函数时执行一次。您还使用 2 个嵌套的 for 循环,都使用 i 作为计数器,并且都从 0 .. window 计数。
  • if (inputSeries &amp;&amp; output != NULL) 太棒了!它技术上是正确的,但是误导性太强了!我喜欢它!
  • 如果你不打印任何东西,你为什么期望任何输出?您也没有为output 分配任何东西。此外,外循环完全没用,因为您在内循环之后立即返回。你应该在调试器中运行它,看看你的程序流在哪里。
  • 你只显示了预期的输出;实际输出是多少?为什么你认为前四个值应该为零?您似乎正在修改一些损坏的代码来做一些它不打算做的事情。无论如何,您开始使用的代码似乎都不正确。

标签: arrays c output moving-average


【解决方案1】:

让我们从头开始。您尝试调整此代码只会使您非常不清楚您要做什么,并且原始代码在任何情况下似乎都有缺陷。处理代码中的问题可能是徒劳的。

首先,对于移动平均值 N,您保留最后 N 个值的总和,对于每个新样本,您:

  1. 将样本 [n] 添加到总和
  2. 从总和中减去样本[n-N]
  3. 输出总和/N

采用您的接口,但省略多余的outputSize - 输出与输入的大小相同),实现可能如下所示:

void Array_MovingAverage( const int* inputSeries, 
                          size_t inputSize, 
                          size_t window, 
                          float* output ) 
{
    int sum = 0 ;

    if( inputSeries != NULL && output != 0 )
    {
        for( size_t i = 0; i < inputSize; i++ )
        {
            // Add newest sample
            sum += inputSeries[i] ;
            
            // Subtract oldest sample
            if( i >= window )
            {
                sum -= inputSeries[i - window] ;
            }
            
            output[i] = (float)sum / window ;
        }
    }
}

要使用它,您可能有:

int main()
{
    const int input[] = {1, 2, 2, 3, 6, 8, 9, 2, 1, 2, 1};
    const size_t size = sizeof(input) / sizeof(*input) ;
    float output[size] ;
    
    Array_MovingAverage( input, size, 5, output ) ;
    
    for( size_t i = 0; i < size; i++ )
    {
        printf( "%.2f\n", output[i]) ;
    }

    return 0;
}

对于您的样本数据{1, 2, 2, 3, 6, 8, 9, 2, 1, 2, 1},输出为:

{0.20, 0.60, 1.00, 1.60, 2.80, 4.20, 5.60, 5.60, 5.20, 4.40, 3.00}

现在您的问题尚不清楚,但是从其他 cmets 看来,您似乎希望破解此函数以忽略调用者提供的输入,因为您无法修改调用者。坦率地说,这很奇怪,但这是一种“安全”的方式。让我们假设outputSize 将被恢复,因为显然您需要它来避免超出调用者输出缓冲区。最简单的解决方案是将函数的整个主体包裹起来,是一个额外的大括号 {...} 外壳,允许您创建覆盖输入参数的影子变量,而其余代码保持不变:

void Array_MovingAverage( const int* inputSeries, 
                          size_t inputSize, 
                          size_t window, 
                          float* output,
                          size_t outputSize ) 
{
    // Prevent unused warnings
    (void)inputSeries ;
    (void)inputSize ;
    (void)window ;

    // Create block to allow variables to be "shadowed"
    {
        // Override inputs
        // NASTY HACK
        const size_t inputSize = 11 ;
        const int inputSeries[11] = {1, 2, 2, 3, 6, 8, 9, 2, 1, 2, 1};
        const size_t window = 5 ;
        
        int sum = 0 ;
    
        if( inputSeries != NULL && output != 0 )
        {
            for( size_t i = 0; i < inputSize; i++ )
            {
                // Add newest sample
                sum += inputSeries[i] ;
                
                // Subtract oldest sample
                if( i >= window )
                {
                    sum -= inputSeries[i - window] ;
                }
                
                // Only write to caller output if index in bounds
                if( i < outputSize )
                {
                    output[i] = (float)sum / window ;
                }
            }
        }
    }
}

当然,您可以简单地更改变量名称并忽略输入参数,但如果在任何复杂的现有工作代码中执行此操作,上述 hack 可能不太容易出错(无需重命名变量)。也就是说,这样的解决方案可能如下所示:

void Array_MovingAverage( const int* inputSeries, 
                          size_t inputSize, 
                          size_t window, 
                          float* output,
                          size_t outputSize ) 
{
    // Prevent unused warnings
    (void)inputSeries ;
    (void)inputSize ;
    (void)window ;

    // Local "input" data
    const int input[] = {1, 2, 2, 3, 6, 8, 9, 2, 1, 2, 1};
    const size_t size = sizeof(input) / sizeof(*input) ;
    const size_t width = 5 ;
 
    int sum = 0 ;

    if( input != NULL && output != 0 )
    {
        for( size_t i = 0; i < size; i++ )
        {
            // Add newest sample
            sum += input[i] ;
            
            // Subtract oldest sample
            if( i >= window )
            {
                sum -= input[i - width] ;
            }
            
            // Only write to caller output if index in bounds
            if( i < outputSize )
            {
                output[i] = (float)sum / window ;
            }
        }
    }
}

【讨论】:

    【解决方案2】:

    您的代码中有很多问题:

    Array_MovingAverage(const int inputSeries[], size_t inputSize, size_t window, float output[],
                            size_t outputSize) {
     if (inputSeries && output != NULL){
       for(size_t i = 0; i < window ; i++)
         if (window < inputSize)
           if (inputSeries != NULL && output != NULL) {  << This is same as if statemend 3 lines above
             if(outputSize >= inputSize) {
               size_t inputSize[11] =  {1, 2, 2, 3, 6, 8, 9, 2, 1, 2, 1}; //<< this hides parameter. And has totally different type.
               const uint8_t window = 5; // Also hiding parameter
               {
                 const int inputSeries[] = {1, 2, 2, 3, 6, 8, 9, 2, 1, 2,1}; // again hiding
    
                 double window = 5;  // more hiding of variables and parameters
                 double c = 2.0; // Where does this value come from?
                 double d = 2.0;
    
                 for(int i = 0; i < window; i++) // Same loop variable as outer loop.
                 {
                   c += inputSeries[i];
                   d = c / window;
                 }
    
                 return true;  // You return after first iteration of outer loop.
    // No printf so far
    // No value assigned to output[] so far.
    // The only result is return value true.
               }
             }
           }
       }
     }
    

    你用不同的类型隐藏了两次window。除非那是一些混淆竞赛,否则那是 NO NO! 其他参数也隐藏在不同类型的变量中。

    外部循环根本没有任何作用,因为它永远不会到达第二次迭代。

    一次又一次地检查循环内的参数(如果它被执行了多次)是浪费CPU时间。

    让我们重新排序你的代码并删除一些奇怪的东西(代码未经测试,任何发现拼写错误的人都可以保留它们。;))

    void Array_MovingAverage(const int inputSeries[], size_t inputSize, size_t window, float output[],
                             size_t outputSize) {
       if ( window < inputSize
         && inputSeries != NULL && output != NULL
         && outputSize >= inputSize ) {
    
          double c = 0.0;
          double avg;
          int i;
    
          // first fill partial window at begin
          for (int i = 0; i < window; i++)
          {
             c += inputSeries[i];
             avg = c / (i+1);
             output[i] = avg;
          }
    
          // Then handle full windows until we reach the end
          // c now contains sum of entries 0..window-1
          // i points to entry 'window'
          for ( ; i < inputSize; i++)
          {
             // Move the window by adding 1 new element and remove 1 old element
             c += inputSeries[i];
             c -= inputSeries[i-window]
             avg = c / window;
             output[i] = avg;
          }
       }
    }
    
    int main(void)
    {
       int    input[] = {1, 2, 2, 3, 6, 8, 9, 2, 1, 2,1};
       size_t Size = sizeof(input)/(input[0]);
       float  output[Size] = {0};
    
       Array_MovingAverage(intput, Size, 5, output, Size);
    
       printf("Moving avarage:\n");
       for (int i = 0; i < Size; i++)
       {
          printf("%d ", input[i]);
       }
    
       for (int i = 0; i < Size; i++)
       {
          printf("%f ", (int)(output[i]+0.5));
       }
    }
    

    【讨论】:

    • 谢谢,但问题是我不能在那个文件中定义一个 main 我正在实现这个函数我根本不能改变 main。我只能将它植入到所需的文件中。
    • 您的预填充循环以除以零错误开始(更改:output[i] = c / (i + 1) avg 可能有点多余?)。 0..window-1 的“预填充”值(如果更正)将导致“嘈杂”的初始数据。在某些应用程序(例如信号处理)中,假设先前的样本为零并将运行总和除以window 而不是i 可能会更好。然后你会得到一个“顺利”的启动。 OP 刚刚忽略了前四个值并将它们设置为零;这是另一种可能性,但会导致样本索引 output[window] 出现“步骤”。
    • ...另一种选择是省略第一个样本,例如outputSize = inputSize - window,以便第一个输出值是第一个window 样本的平均值。
    • @Clifford 感谢您指出该错误。什么样的启动最好,需要由 OP 指定,但看起来,正确和完整的描述并不是他们的强项。 ://
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-05-03
    • 2019-04-08
    • 1970-01-01
    • 2016-10-24
    • 2019-04-19
    • 2017-02-16
    • 2012-01-21
    相关资源
    最近更新 更多