【血压】 最小二乘法高斯拟合 c语言实现
基础原理
最小二乘法&高斯函数变形
GuassFitTest.h
#ifndef GUASSFITTEST_H
#define GUASSFITTEST_H
#include "ALG.h"
/*该工程作为单片机最小二乘法高斯拟合的测试工程*/
//变量声明放在c文件,函数声明放在h文件
//高斯公式原理可参考:
//https://blog.csdn.net/dingzj2000/article/details/103719368
//数值计算方法教材 103-108页
// fai0 = 1 fai1 = x fai2 = x^2
#endif
GuassFitTest.c
/**************************************
2021 04 10
作者:SZU guangjie2333
功能:c语言实现最小二乘法高斯拟合
*************************************/
/*头文件引用*/
#include "GuassFitTest.h"
/*内部变量定义*/
//拟合曲线的袖带压AD
static int cuffPressAD[14] = { 2863,2717,2586,2461,2346,2220,2117,
2053,1950,1882,1807,1718,1643,1557 };
//拟合曲线的脉搏波AD
static int paluseAD[14] = { 5622,4007,3004,4229,8464,9874,12125,
10680,8704,6426,4256,4114,3555,3243 };
//放气阶数
int s_iPluseStepCnt = 14;
/*主函数*/
void main()
{
B_parameter B;
Gaussian_parameter guassian;
float Z[14]; //高斯函数两边取对数,变形,得到一个二次函数。 Z[i] = ln y[i] = ln paluseAD[i]
for (int i = 0; i < s_iPluseStepCnt; i++)
{
cuffPressAD[i] =(int)(cuffPressAD[i] * 0.097 - 108); //AD-真实值 线性转换公式
paluseAD[i] =(int)(paluseAD[i] / 16); //脉搏波AD放大了16倍
Z[i] = log(paluseAD[i]);
printf("cuffPressAD[%d] = %d \n paluseAD[%d] = %d \n Z[%d] = %f \n",i, cuffPressAD[i], i,paluseAD[i],i,Z[i]);
}
//计算最小二乘法的B向量
B = GaussianElimination(Z, cuffPressAD,s_iPluseStepCnt);
//计算高斯曲线的参数
guassian = CalcGaussianParameter(B);
//0~250点进行高斯拟合
GuassFit(guassian);
printf(" \n\n b0 = %f b1 = %f b2 = %f \n\n",B.b0,B.b1,B.b2);
printf(" \n\n a = %f b = %f c = %f \n\n",guassian.a, guassian.b, guassian.c);
}
ALG.h
#ifndef ALG_H
#define ALG_H
/*该文件是算法函数的头文件*/
#include "stdio.h"
#include "stdlib.h"
#include "math.h"
typedef struct
{
float b0;
float b1;
float b2;
}B_parameter;
typedef struct
{
float a;
float b;
float c;
}Gaussian_parameter;
typedef struct
{
float value;
int index;
}matrixData;
//高斯列主元素消元法,返回b0,b1, b2
B_parameter GaussianElimination(float* y, int* x, int s_iPluseStepCnt);
//计算高斯曲线参数
Gaussian_parameter CalcGaussianParameter(B_parameter B);
//0~250高斯拟合
void GuassFit(Gaussian_parameter gaussian);
#endif // !ALG
## ALG.c
#include"ALG.h"
/*该文件用于高斯拟合最小二乘法算法的c语言实现*/
/*************************************
* 宏定义区
**************************************/
#define ROW 3 //行
#define LINE 4 //列
/*************************************
* 内部变量定义区
**************************************/
//权重(由于前面的点会由于刚刚关阀的影响,脉搏波波动大,故权重为0)
w[14] = {0,0,0,1,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 };
/*************************************
* 内部函数定义区
**************************************/
//(fai0,fai0)的内积,返回1 求和
static float xfun0(int s_iPluseStepCnt)
{
float sum = 0;
for (int i = 0; i < s_iPluseStepCnt; i++)
{
sum += 1 * w[i];
}
return sum;
}
//(fai0,fai1)的内积,返回x 求和
static float xfun1(int* x, int s_iPluseStepCnt)
{
float sum = 0;
for (int i = 0;i < s_iPluseStepCnt ; i++)
{
sum += x[i] * w[i];
}
return sum;
}
//(fai0,fai2) || (fai1,fai1)的内积,返回x^2 求和
static float xfun2(int* x, int s_iPluseStepCnt)
{
float sum = 0;
for (int i = 0; i < s_iPluseStepCnt; i++)
{
sum += x[i]*x[i] * w[i];
}
return sum;
}
//(fai1,fai2)的内积,返回x^3 求和
static float xfun3(int* x, int s_iPluseStepCnt)
{
float sum = 0;
for (int i = 0; i < s_iPluseStepCnt; i++)
{
sum += x[i] * x[i] * x[i] * w[i];
}
return sum;
}
//(fai2,fai2)的内积,返回x^4 求和
static float xfun4(int* x, int s_iPluseStepCnt)
{
float sum = 0;
for (int i = 0; i < s_iPluseStepCnt; i++)
{
sum += x[i] * x[i] * x[i] * x[i] * w[i];
}
return sum;
}
//(y,fai0)的内积,返回y*1 求和
static float yfun0(float* y, int* x ,int s_iPluseStepCnt)
{
float sum = 0;
for (int i = 0; i < s_iPluseStepCnt; i++)
{
sum += y[i] * 1 * w[i];
}
return sum;
}
//(y,fai1)的内积,返回y*x 求和
static float yfun1(float* y, int* x, int s_iPluseStepCnt)
{
float sum = 0;
for (int i = 0; i < s_iPluseStepCnt; i++)
{
sum += y[i] * x[i] * w[i];
}
return sum;
}
//(y,fai2)的内积,返回y*1 求和
static float yfun2(float* y, int* x, int s_iPluseStepCnt)
{
float sum = 0;
for (int i = 0; i < s_iPluseStepCnt; i++)
{
sum += y[i] * x[i] * x[i] * w[i];
}
return sum;
}
//选第k次交换的列最大值当主元,返回所在行
static matrixData row_k_max(float matrix[ROW][LINE],int k)
{
matrixData line[ROW] ;
matrixData Max;
//初始化
for (int i = 0; i < ROW; i++)
{
line[i].value = 0;
line[i].index = i;
}
//赋初值
for (int i = k; i < ROW; i++)
{
line[i].value = matrix[i][k];
}
//比较大小
Max.value = 0;
for (int i = k; i < ROW; i++)
{
if (fabs(line[i].value) > fabs(Max.value))
{
Max = line[i];
}
}
return Max;
}
//交换两行
static void swapRow(float matrix[ROW][LINE],int k, int maxRowE)
{
float temp = 0;
for (int i = 0; i < LINE; i++)
{
temp = matrix[k][i];
matrix[k][i] = matrix[maxRowE][i];
matrix[maxRowE][i] = temp;
}
}
static void printfMatrix(float matrix[ROW][LINE])
{
for (int i = 0; i < ROW; i++)
{
for (int j = 0 ; j < LINE; j++)
{
printf(" %f ", matrix[i][j]);
}
printf("\n");
}
}
/*
外部函数调用
*/
//高斯列主元素消元法,返回b0,b1, b2
B_parameter GaussianElimination(float* y, int* x, int s_iPluseStepCnt)
{
B_parameter B; //最小二乘法的待求解向量
matrixData Max; //第k次交换,列的最大值和索引
float m;
//计算增广矩阵中的每个值
float matrix[ROW][LINE];
for (int i = 0; i < ROW; i++)
{
for (int j = 0; j < LINE; j++)
{
if (LINE-1 == j)
{
//计算最后一列即Z向量的值
switch (i)
{
case 0: matrix[i][j] = yfun0(y, x, s_iPluseStepCnt); break;
case 1: matrix[i][j] = yfun1(y, x, s_iPluseStepCnt); break;
case 2: matrix[i][j] = yfun2(y, x, s_iPluseStepCnt); break;
default: break;
}
}
else
{
//计算x矩阵的每个值
switch (i+j)
{
case 0: matrix[i][j] = xfun0(s_iPluseStepCnt); break;
case 1: matrix[i][j] = xfun1(x, s_iPluseStepCnt); break;
case 2: matrix[i][j] = xfun2(x, s_iPluseStepCnt); break;
case 3: matrix[i][j] = xfun3(x, s_iPluseStepCnt); break;
case 4: matrix[i][j] = xfun4(x, s_iPluseStepCnt); break;
default: break;
}
}
}
}
printf("打印初始的矩阵\n\n");
printfMatrix(matrix);
//进行列主元素消元
for (int i = 0; i < ROW-1; i++) //i代表第i次交换
{
//选主元
Max = row_k_max(matrix,i);
//交换行
if (Max.index != i)
{
swapRow(matrix,i,Max.index);
}
//消主元
for (int j = i+1; j < ROW; j++)
{
m = matrix[j][i] / matrix[i][i]; //小除大,避免小值当分母
for (int k = i; k < LINE ; k++)
{
matrix[j][k] = matrix[j][k] - matrix[i][k] * m; //消元
}
}
printf("\n\n打印第%d次变化后的矩阵\n\n",i);
printfMatrix(matrix);
}
//计算最小二乘法的B列向量
B.b2 = matrix[ROW - 1][LINE - 1] / matrix[ROW - 1][LINE - 2];
B.b1 = (matrix[ROW - 2][LINE - 1] - B.b2 * matrix[ROW - 2][LINE - 2])/ matrix[ROW - 2][LINE - 3];
B.b0 = (matrix[ROW - 3][LINE - 1] - B.b2 * matrix[ROW - 3][LINE - 2] - B.b1 * matrix[ROW - 3][LINE - 3])/matrix[ROW - 3][LINE - 4];
return B;
}
//计算高斯拟合参数a,b,c
Gaussian_parameter CalcGaussianParameter(B_parameter B)
{
Gaussian_parameter gaussian;
gaussian.a = exp((B.b0 - (B.b1*B.b1/(4*B.b2))));
gaussian.b = -1 / B.b2;
gaussian.c = -B.b1 / (2 * B.b2);
return gaussian;
}
//从0到250 进行高斯拟合
void GuassFit(Gaussian_parameter gaussian)
{
float y[250];
for (int i = 0; i<250;i++)
{
y[i] = gaussian.a * exp(-(i-gaussian.c)* (i - gaussian.c) /gaussian.b);
printf("%f \n",y[i]);
}
}