c语言下求平均值的几种思路

本文探讨了计算两个整数平均值的不同方法,包括传统除法、差值平分及位运算技巧。位运算方法尤为巧妙,利用按位与和按位异或实现了无需担心溢出的高效计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

给出两个数a和b,求a和b的平均值(只考虑两数的平均数仍为整数的情况)

思路1

题目看上去很简单,直接给出第一种思路
int a = 3;
int b = 5;
int avg = (a + b) / 2;
看上去没什么问题,但是一旦整形溢出,这个求法就会出现不可预料的错误
所以下面有另一种求法
int avg = a / 2 + b / 2;
但是这两种求法都是非常简单的,也不是我想分享的,否则这篇博客也没有存在的意义

思路2

假设有两桶水,一桶比另一桶多,我们可以取出多的那一桶少的那一桶多出来的部分,将那一部分多的均分放入两桶,两桶就有相同的水了
代码实现也很简单,如下:
int avg = b + (a -b) / 2;
//假设a比b大

思路3

下面介绍一种比较巧妙的求法,上课时候老师提到的一种求法
先看代码:
int avg = (a & b) + ((a ^ b) >> 1);
解释一下这个求法:
一个数和另一个数与的部分(a & b),在二进制序列里面就是两个数都为1的部分
这样的话我们直接与取出来的部分就是原来两个数二进制序列相加进一后移一位的部分
而((a ^ b) >> 1)则是两个数二进制序列不同的部分,这部分是需要除以2的,除以2以后均分到两个数,所以可以写成((a ^ b) / 2),效果相同
两部分相加,就得到我们的平均数,可以说这个求法是非常巧了

本文为学习过程中碰到的比较有意思的题目,也欢迎各路高手提出更好的解决方法在下方评论

也欢迎各位到我的github下下载更多练习代码,如果对上述有疑问可以在下方评论

<think>我们之前已经讨论过流量计常用的滤波算法,包括递推平均滤波(滑动平均)、带通滤波、卡尔曼滤波和一阶滞后滤波。根据用户提供的引用,特别是引用[1]中提到的几种常用滤波方法,以及用户明确希望用C语言实现代码,我将针对流量计信号处理,提供几种滤波算法的C语言实现代码。 用户提供的引用中提到了: - 引用[1]:介绍了一阶滞后滤波法(即指数加权平均滤波)和中位值滤波法。 - 引用[3]:提到了自适应滤波器算法。 - 引用[4]:提到了互补滤波算法。 结合之前讨论的推荐算法和用户引用,我将提供以下滤波方法的C语言代码: 1. 递推平均滤波法(滑动平均滤波)——之前已经讨论过,但用户要代码,所以再提供一次。 2. 一阶滞后滤波法(指数加权平均滤波)——引用[1]中提到了。 3. 中位值滤波法——引用[1]中提到。 4. 自适应滤波法——引用[3]中提到了自适应滤波器,但自适应滤波算法较为复杂,这里提供一个简单的LMS自适应滤波示例。 5. 互补滤波法——引用[4]中提到,常用于融合多个传感器数据,在流量计中可能用于融合不同传感器的流量信号。 注意:由于流量计信号处理的具体应用场景和信号特性不同,请根据实际情况调整参数。 以下为C语言实现代码: ### 1. 递推平均滤波法(滑动平均滤波) ```c #define FILTER_N 12 // 滑动平均滤波的窗口大小 int moving_average_filter(int new_sample) { static int filter_buf[FILTER_N]; // 存储队列 static int index = 0; int sum = 0; // 更新队列:新数据入队,覆盖最旧的数据 filter_buf[index] = new_sample; index = (index + 1) % FILTER_N; // 计算队列中所有数据的和 for (int i = 0; i < FILTER_N; i++) { sum += filter_buf[i]; } return sum / FILTER_N; // 返回平均值 } ``` ### 2. 一阶滞后滤波法(指数加权平均滤波) 根据引用[1]中的描述,实现如下: ```c float first_order_lag_filter(float new_sample, float a) { static float last_value = 0; // 上一次滤波结果 float new_value; // 计算本次滤波结果: new_value = a * new_sample + (1-a) * last_value new_value = a * new_sample + (1 - a) * last_value; last_value = new_value; // 更新上一次滤波结果为本次结果 return new_value; } ``` 注意:参数`a`为平滑系数,取值范围0~1,值越小,滤波效果越强(滞后越明显)。 ### 3. 中位值滤波法 引用[1]中提到中位值滤波,适用于去除脉冲干扰。基本思路:连续采样N次(N取奇数),然后排序,取中间值作为本次滤波结果。 ```c #define MEDIAN_FILTER_N 5 // 中位值滤波的采样次数,取奇数 int median_filter(int new_sample) { static int samples[MEDIAN_FILTER_N]; // 存储最近N次采样值 static int count = 0; int temp_array[MEDIAN_FILTER_N]; // 将新采样值存入数组 samples[count] = new_sample; count = (count + 1) % MEDIAN_FILTER_N; // 复制数组用于排序(避免修改原始数据) for (int i = 0; i < MEDIAN_FILTER_N; i++) { temp_array[i] = samples[i]; } // 使用冒泡排序对数组进行排序(从小到大) for (int i = 0; i < MEDIAN_FILTER_N - 1; i++) { for (int j = 0; j < MEDIAN_FILTER_N - 1 - i; j++) { if (temp_array[j] > temp_array[j+1]) { int temp = temp_array[j]; temp_array[j] = temp_array[j+1]; temp_array[j+1] = temp; } } } // 返回中位值(中间位置的值) return temp_array[MEDIAN_FILTER_N / 2]; // 整数除法,取中间索引 } ``` ### 4. 自适应滤波法(LMS算法) 引用[3]提到了自适应滤波器。这里提供一个简单的LMS自适应滤波实现。注意:自适应滤波通常需要参考噪声信号,但在流量计中可能不直接适用,这里仅作为示例。 ```c #define FILTER_LENGTH 5 // 滤波器长度 #define MU 0.01 // 步长因子,影响收敛速度 float lms_filter(float new_sample, float noise_sample) { static float w[FILTER_LENGTH] = {0}; // 滤波器权重 static float x[FILTER_LENGTH] = {0}; // 输入信号历史 float y = 0; // 滤波器输出 float e; // 误差 // 更新输入信号历史(将新噪声样本加入) for (int i = FILTER_LENGTH - 1; i > 0; i--) { x[i] = x[i-1]; } x[0] = noise_sample; // 计算滤波器输出 for (int i = 0; i < FILTER_LENGTH; i++) { y += w[i] * x[i]; } // 计算误差:期望信号(实际采样值)减去滤波器输出 e = new_sample - y; // 更新权重 for (int i = 0; i < FILTER_LENGTH; i++) { w[i] = w[i] + MU * e * x[i]; } return y; // 返回滤波后的信号 } ``` 注意:这个自适应滤波器需要一个参考噪声信号(`noise_sample`),在流量计应用中,如果没有单独的噪声参考,可能不适用。实际使用中可能需要根据应用场景调整。 ### 5. 互补滤波法 引用[4]提到互补滤波常用于融合多个传感器数据(如加速度计和陀螺仪)。在流量计中,如果存在两个传感器(如一个高频噪声大但响应快,一个低频稳定但响应慢),可以用互补滤波融合。 ```c float complementary_filter(float high_freq_sample, float low_freq_sample, float alpha) { // alpha: 高频通道的权重,通常取0.98(表示信任高频信号98%,信任低频信号2%) // 互补滤波公式:输出 = alpha * 高频信号 + (1 - alpha) * 低频信号 return alpha * high_freq_sample + (1 - alpha) * low_freq_sample; } ``` ### 总结 - 对于流量计信号,**递推平均滤波**和**一阶滞后滤波**是最常用且易于实现的算法,推荐优先尝试。 - **中位值滤波**适用于存在脉冲干扰的场合。 - **自适应滤波**在噪声特性复杂且可获取参考噪声时适用,但实现复杂。 - **互补滤波**适用于融合多个传感器的流量信号。 请根据实际流量计信号特性和系统资源选择合适的滤波算法。调试时建议先通过采集实际信号进行离线测试,确定参数后再部署到嵌入式系统。 §§相关问题§§ 1. 如何选择一阶滞后滤波的平滑系数 $\alpha$? 2. 中位值滤波的窗口大小为什么通常取奇数? 3. 自适应滤波算法在流量计信号处理中的实际应用案例? 4. 互补滤波中如何确定两个信号的权重系数? 5. 滤波算法对实时性要高的流量计系统有何影响?</think>以下是根据您提供的引用和流量计滤波需,用C语言实现的几种常用滤波算法代码。每种算法都包含详细注释,便于您在实际嵌入式系统中应用: ### 1. 递推平均滤波法(滑动平均滤波) ```c #define FILTER_N 12 // 推荐窗口大小(根据引用[1][3]) int moving_average_filter(int new_sample) { static int filter_buf[FILTER_N]; // 数据存储队列 static int index = 0; // 当前队列位置 int sum = 0; // 更新队列:新数据入队尾,旧数据出队首 filter_buf[index] = new_sample; index = (index + 1) % FILTER_N; // 循环队列索引 // 计算队列平均值 for (int i = 0; i < FILTER_N; i++) { sum += filter_buf[i]; } return sum / FILTER_N; // 返回滤波值 } ``` > **适用场景**:周期性干扰抑制(如泵机振动)[^3] ### 2. 一阶滞后滤波法(指数加权平均) ```c float first_order_lag_filter(float new_sample, float alpha) { static float last_value = 0; // 保存上次滤波值 // 计算:yₙ = α·xₙ + (1-α)·yₙ₋₁ float result = alpha * new_sample + (1 - alpha) * last_value; last_value = result; // 更新状态 return result; } ``` > **参数说明**: > - `alpha`:平滑系数(0~1),值越小滤波越强[^1] > **适用场景**:高频波动信号处理[^1] ### 3. 中位值滤波法(抗脉冲干扰) ```c #define MEDIAN_SIZE 5 // 推荐奇数窗口(3/5/7) int median_filter(int new_sample) { static int buffer[MEDIAN_SIZE]; static int index = 0; int temp[MEDIAN_SIZE]; // 更新采样窗口 buffer[index] = new_sample; index = (index + 1) % MEDIAN_SIZE; // 复制数据并排序 for(int i=0; i<MEDIAN_SIZE; i++) temp[i] = buffer[i]; bubble_sort(temp, MEDIAN_SIZE); // 实现冒泡排序 return temp[MEDIAN_SIZE/2]; // 返回中位值 } // 辅助排序函数 void bubble_sort(int arr[], int n) { for(int i=0; i<n-1; i++) { for(int j=0; j<n-i-1; j++) { if(arr[j] > arr[j+1]) { int temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; } } } } ``` > **适用场景**:消除突发脉冲干扰[^1] ### 4. 互补滤波法(多传感器融合) ```c typedef struct { float k; // 融合系数(0~1) float integral; // 积分项 } CompFilter; float complementary_filter(CompFilter* f, float primary, float secondary) { // 融合公式:输出 = k*主数据 + (1-k)*辅助数据 float output = f->k * primary + (1 - f->k) * secondary; // 积分抗漂移(引用[4]) f->integral += 0.02 * output; // 积分系数根据采样率调整 return output + f->integral; } ``` > **使用示例**: > ```c > CompFilter flow_filter = {.k=0.92, .integral=0}; // 初始化 > float filtered = complementary_filter(&flow_filter, adc_value, sensor2_value); > ``` ### 5. 自适应滤波(LMS算法) ```c #define FILTER_LEN 4 // 滤波器阶数 #define MU 0.005 // 收敛步长(引用[3]) float adaptive_lms_filter(float new_sample) { static float w[FILTER_LEN] = {0}; // 权重向量 static float x[FILTER_LEN] = {0}; // 输入历史 float y = 0; // 滤波器输出 float e; // 误差 // 更新输入历史 for(int i=FILTER_LEN-1; i>0; i--) x[i] = x[i-1]; x[0] = new_sample; // 计算输出:yₙ = wᵀ·xₙ for(int i=0; i<FILTER_LEN; i++) y += w[i] * x[i]; // 计算误差:eₙ = dₙ - yₙ (dₙ为期望信号) e = get_desired_signal() - y; // 需实现期望信号获取 // 权重更新:wₙ₊₁ = wₙ + μ·eₙ·xₙ for(int i=0; i<FILTER_LEN; i++) w[i] += MU * e * x[i]; return y; } ``` > **适用场景**:时变噪声环境[^3] ### 算法选择建议 1. **基础应用**:优先使用`递推平均滤波`,资源消耗最低 2. **脉冲干扰**:选择`中位值滤波`抗突发干扰 3. **动态响应**:采用`一阶滞后滤波`调整平滑系数 4. **多传感器**:使用`互补滤波`融合不同来源数据 5. **复杂噪声**:`自适应滤波`处理时变系统 > **重要提示**: > - 采样率应大于信号最高频率的2倍(奈奎斯特定律) > - 滤波器参数需通过实际信号测试调整 > - 电磁流量计建议配合硬件带通滤波使用[^2]
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值