那些有意思又好用的小算法

我认为再简单的算法,只要能解决实际问题,提高解决问题的效率,都是好算法。下面介绍一些有意思又好用的算法,希望对大家有所帮助。

1. 线性归一化

1.1 简介

线性归一化是一种常用的数据预处理方法,主要用于将数据缩放到一个特定的范围,通常是[0, 1]。在嵌入式中常用于ADC或其他类型传感器的数据处理,将获取到的待测数值 映射到[0%, 100%]之间,方便后续处理。

公式1:

X=Xminmaxmin×100%X'=\frac{X-min}{max-min}\times100\%

公式1
X’:计算得到的归一化百分比结果
X:待测数值
min:标定的最小值
max:标定的最大值

以上公式1待测数值与结果是正比关系,即待测数值越大,结果百分比越大。但实际情况中,呈现反比关系的传感器并不少见,这时需要对公式1进行变形才能得到反比公式。反比关系无非就是计算100%-X’,即用100%减去正比的百分比结果,最后得到的就是反比结果,公式变形过程如下:

Y=100%XY'=100\%-X'

=100%Xminmaxmin×100%=100\%-\frac{X-min}{max-min}\times100\%

=(maxmin)(Xmin)maxmin×100%=\frac{(max-min)-(X-min)}{max-min}\times100\%

最终得到公式2:

Y=maxXmaxmin×100%Y'=\frac{max-X}{max-min}\times100\%

公式2
Y’:反比的百分比结果

1.2 代码演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <stdio.h>

typedef struct {
int min; // 标定值最小值
int max; // 标定值最大值
int adc_value; // adc值
float value_z; // 正比百分比值
float value_f; // 反比百分比值
} PERCENT;

/**
* @brief 通过线性归一化计算ADC值对应的百分比,常用于传感器百分比数据转换
* @param PERCENT PERCENT结构体指针
*/
void percent_computed(PERCENT *percent)
{
// X' = ((X - Xmin) / (Xmax - Xmin)) * 100% // (原公式)
percent->value_z = (float)(percent->adc_value - percent->min) / (percent->max - percent->min) * 100; // (正比)
percent->value_f = (float)(percent->max - percent->adc_value) / (percent->max - percent->min) * 100; // (反比)
}

int main()
{
PERCENT percent = {0};
percent.min = 2000; // 设定标定值最小值
percent.max = 3500; // 设定标定值最大值
percent.adc_value = 2680; // 设定待测值

percent_computed(&percent); // 计算百分比

printf("value_z: %.2f%%\n", percent.value_z); // 输出正比百分比值 value_z: 45.33%
printf("value_f: %.2f%%\n", percent.value_f); // 输出反比百分比值 value_f: 54.67%

return 0;
}

2. 一阶低通

2.1 简介

一阶低通滤波器通常使用递归的方式来平滑信号。其基本形式可以表示为:

y[n]=αx[n]+(1α)y[n1]y[n] = \alpha x[n] + (1 - \alpha) y[n-1]

其中:
y[n] 是当前输出,
x[n] 是当前输入,
alpha 是平滑因子(0 < alpha < 1),决定了滤波器的响应速度。

特点

  • 对高频噪声有较好的抑制效果。
  • 适合处理连续信号。
  • 计算简单,实时性强。

该算法通常用来处理不稳定的信号,使信号变得平滑。平滑因子需要根据实际情况设定,不宜过大也不宜过小,过大会导致效果大大减弱,而过小则太过于偏向于上一次输出的值,会增加输出迟滞性。

2.2 代码演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <stdio.h>

#define DATA_SIZE 10

int main()
{
int n;
float alpha = 0.6; // 平滑因子
float raw_signal[DATA_SIZE] = {1.1, 1.4, 1.8, 2.6, 2.9, 3.7, 4.1, 4.4, 5.3, 5.5}; // 原始信号数据
float filtered_signal[DATA_SIZE]; // 过滤后信号数据
filtered_signal[0] = raw_signal[0]; // 初始化第一个输出

for (n = 1; n < DATA_SIZE; n++)
{
filtered_signal[n] = alpha * raw_signal[n] + (1 - alpha) * filtered_signal[n - 1];
}

// 输出结果
printf("时间点 | 原始信号 | 过滤后信号\n");
for (n = 0; n < DATA_SIZE; n++)
{
printf("%d | %.2f | %.2f\n", n, raw_signal[n], filtered_signal[n]);
}
return 0;
}

编译,查看输出结果:

1
2
3
4
5
6
7
8
9
10
11
12
PS E:\project\C_C++\adc> gcc t2.c -o t2; ./t2
时间点 | 原始信号 | 过滤后信号
0 | 1.10 | 1.10
1 | 1.40 | 1.28
2 | 1.80 | 1.59
3 | 2.60 | 2.20
4 | 2.90 | 2.62
5 | 3.70 | 3.27
6 | 4.10 | 3.77
7 | 4.40 | 4.15
8 | 5.30 | 4.84
9 | 5.50 | 5.24