插值项目

插值项目

一、项目说明

1、面向一维插值和线性拟合,含有牛顿插值、线性插值等四种插值方式;
2、设计了可用鼠标直接操作的GUI界面,比上次贪吃蛇项目的GUI更好;

二、代码说明

1、头文件

#include <graphics.h>              // 引用图形库头文件
#include <conio.h>                 //控制台输入输出
#include <stdio.h>                
#include <windows.h>				//用到了定时函数sleep()
#include <math.h>                  //数学函数库
#include <string.h>                //字符串处理函数

2、插值部分(只解释代码,原理部分的相关资料见文尾)

void nuton()//牛顿插值
{ 
   event=0;
   cleardevice();//清屏(取决于背景色)
   int k,m;
   //算出插商表
   for(int i=0;i<non;i++){
      numc[i][0]=numy[i];
   }
   for(k=1;k<non;k++)
	   for(m=k;m<non;m++)
	   {
		   numc[m][k]=(numc[m-1][k-1]-numc[m][k-1])/(numx[m-k]-numx[m-k+1]);
	   }
   for(int i=0;i<nan;i++)
   {
	   float t=1.0;
	    numry[i]=numc[0][0];
	   for(int j=1;j<=non;j++)
	   {   
		   t=t*(numr[i]-numx[j-1]);
		   numry[i]=numry[i]+numc[j][j]*t;
	   }
   }
   //此后为配合图形界面输出结果
   char msg[40]="所求的插值为";
   for(int i=0;i<nan;i++)
   {  
	   char temp[30];
	   sprintf(temp,"%f",numry[i]);
	   sprintf(msg,"%s%s ",msg,temp);
   }
   MessageBox(NULL,TEXT(msg),TEXT("结果"),MB_OK);
   exit(0);
};

可以直接通过牛顿插值公式写出代码。

void langrage()//拉格朗日插值
{
   cleardevice();//清屏(取决于背景色)
   int k,m;
   float a[30];
   for(int i=0;i<nan;i++){
      for(k=0;k<non;k++)
       {   
	      a[k]=numy[k];
	       for(m=0;m<non;m++)
	      {
		   if(m!=k) a[k]=a[k]*((numr[i]-numx[m])/(numx[k]-numx[m]));
	      }
	      numry[i]+=a[k];
        }
   }
   char msg[40]="所求的插值为";
   for(int i=0;i<nan;i++)
   {   char temp[30];
	   sprintf(temp,"%f",numry[i]);
	   sprintf(msg,"%s%s ",msg,temp);
   }
   MessageBox(NULL,TEXT(msg),TEXT("结果"),MB_OK);
   exit(0);
};

void fenduan()//分段线性
{
	for(int i=0;i<nan;i++)
	{
		for(int j=0;j<non;j++)
		{
			if(numr[i]>numx[j]&&numr[i]<numx[j+1])
				{
					numry[i]=numy[j]+((numy[j+1]-numy[j])/(numx[j+1]-numx[j]))*(numr[i]-numx[j]);
					break;
			}
		}
	}
   char msg[40]="所求的插值为";
   for(int i=0;i<nan;i++)
   {   char temp[30];
	   sprintf(temp,"%f",numry[i]);
	   sprintf(msg,"%s%s ",msg,temp);
   }
   MessageBox(NULL,TEXT(msg),TEXT("结果"),MB_OK);
   exit(0);

};

拉格朗日插值和分段线性插值代码结构与牛顿插值一致。

3、拟合部分

拟合采用了最小二乘法,对一维数据进行拟合

void nihe()//线性拟合
{
   float a,b;
   float up=0,down=0,sumx=0,sumy=0;
   for(int i=0;i<non;i++)
   {
	   sumx+=numx[i];
	   sumy+=numy[i];
	   up+=numx[i]*numy[i];
	   down+=numx[i]*numx[i];
   }
   b=(up-sumx*sumy/non)/(down-sumx*sumx/non);
   a=sumy/non-b*sumx/non;
   for(int i=0;i<nan;i++)
   {
	   numry[i]=numr[i]*b+a;
   }
   char msg[40];
   sprintf(msg,"所求的函数为Y=%fX+%f,所求的点的值为",b,a);
   for(int i=0;i<nan;i++)
   {   char temp[30];
	   sprintf(temp,"%f",numry[i]);
	   sprintf(msg,"%s%s ",msg,temp);
   }
   MessageBox(NULL,TEXT(msg),TEXT("结果"),MB_OK);
   exit(0);

};

公式如下:
在这里插入图片描述

4、界面部分(最为繁杂,作为重点讲解)

#define maxwidth 480
#define maxheight 360//规定界面的大小
int event=0;
int r1[4][4]={{40,10,440,50},{160,100,320,140},{160,160,320,200},{160,220,320,260}};
RECT R11={r1[0][0],r1[0][1],r1[0][2],r1[0][3]};
RECT R12={r1[1][0],r1[1][1],r1[1][2],r1[1][3]};
RECT R13={r1[2][0],r1[2][1],r1[2][2],r1[2][3]};
RECT R14={r1[3][0],r1[3][1],r1[3][2],r1[3][3]};//第一个界面的按钮位置参数

int r2[6][4]={{40,10,440,50},{40,100,200,140},{280,100,440,140},{40,200,200,240},{280,200,440,240}};
RECT R21={r2[0][0],r2[0][1],r2[0][2],r2[0][3]};
RECT R22={r2[1][0],r2[1][1],r2[1][2],r2[1][3]};
RECT R23={r2[2][0],r2[2][1],r2[2][2],r2[2][3]};
RECT R24={r2[3][0],r2[3][1],r2[3][2],r2[3][3]};
RECT R25={r2[4][0],r2[4][1],r2[4][2],r2[4][3]};


MOUSEMSG m;//鼠标指针
void win_inter();
void win_enter();
void win_enter1();
void nuton();//牛顿插值
void langrage();//拉格朗日插值
void fenduan();//分段线性插值
void nihe();//线性拟合

//构建了三个设置字体大小的函数,可以直接用,l-large;m-middle;s-small
void textl()
{
    LOGFONT f;//字体样式指针
    gettextstyle(&f);	//获取字体样式
	f.lfHeight=48;
	_tcscpy(f.lfFaceName,_T("宋体"));	//设置字体为宋体
	settextstyle(&f);                     // 设置字体样式
	settextcolor(BLACK);				//BLACK在graphic.h头文件里面被定义为黑色的颜色常量
	
}
void textm()
{
	LOGFONT f;//字体样式指针
    gettextstyle(&f);	//获取字体样式
	f.lfHeight=30;
	_tcscpy(f.lfFaceName,_T("宋体"));	//设置字体为宋体
	settextstyle(&f);                     // 设置字体样式
	settextcolor(BLACK);				//BLACK在graphic.h头文件里面被定义为黑色的颜色

}
void texts()
{
   LOGFONT f;//字体样式指针
    gettextstyle(&f);	//获取字体样式
	f.lfHeight=20;
	_tcscpy(f.lfFaceName,_T("宋体"));	//设置字体为宋体
	settextstyle(&f);                     // 设置字体样式
	settextcolor(BLACK);				//BLACK在graphic.h头文件里面被定义为黑色的颜色
}
//两个判断函数,用于分别在两个界面中判断鼠标是否移动到按钮内
int judge_b1(int x,int y)
{
	if(x>r1[1][0]&&x<r1[1][2]&&y>r1[1][1]&&y<r1[1][3])return 1;
	if(x>r1[2][0]&&x<r1[2][2]&&y>r1[2][1]&&y<r1[2][3])return 2;
	if(x>r1[3][0]&&x<r1[3][2]&&y>r1[3][1]&&y<r1[3][3])return 3;
	else return 0;
}
int judge_b2(int x,int y)
{
	if(x>r2[1][0]&&x<r2[1][2]&&y>r2[1][1]&&y<r2[1][3])return 1;
	if(x>r2[2][0]&&x<r2[2][2]&&y>r2[2][1]&&y<r2[2][3])return 2;
	if(x>r2[3][0]&&x<r2[3][2]&&y>r2[3][1]&&y<r2[3][3])return 3;
	if(x>r2[4][0]&&x<r2[4][2]&&y>r2[4][1]&&y<r2[4][3])return 4;
	else return 0;
}

void win_first()//第一个界面
{
	for(int i=0;i<256;i++)
	{
	  setbkcolor(RGB(i,i,i));
	  cleardevice();
	  Sleep(5);
	}
	textl();
	//现在规定的按钮区域中写出文字
	drawtext("Math Tool",&R11,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R1内输入文字,水平居中,垂直居中,单行显示
	textm();
	drawtext("插值",&R12,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R2内输入文字,水平居中,垂直居中,单行显示
	drawtext("拟合",&R13,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R3内输入文字,水平居中,垂直居中,单行显示
	drawtext("退出",&R14,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R3内输入文字,水平居中,垂直居中,单行显示
	setlinecolor(BLACK);
	//然后画出矩形,形成按钮
	rectangle(r1[1][0],r1[1][1],r1[1][2],r1[1][3]);
	rectangle(r1[2][0],r1[2][1],r1[2][2],r1[2][3]);
	rectangle(r1[3][0],r1[3][1],r1[3][2],r1[3][3]);
	while(true)
	{
		m=GetMouseMsg();//读取鼠标操作
		switch(m.uMsg)
		{
	    case WM_MOUSEMOVE:
			    setrop2(R2_XORPEN);
				setlinecolor(LIGHTCYAN);//线条颜色为亮青色
				setlinestyle(PS_SOLID, 3);//设置画线样式为实现,10磅
				setfillcolor(WHITE);//填充颜色为白色
				if(judge_b1(m.x,m.y)!=0)
				{
					if(event != judge_b1(m.x,m.y))
					{
						event =judge_b1(m.x,m.y);//记录这一次触发的按钮
						fillrectangle(r1[event][0],r1[event][1],r1[event][2],r1[event][3]);//有框填充矩形(X1,Y1,X2,Y2)
					}
				}
				else
				{
					if(event != 0)//上次触发的按钮未被修正为原来的颜色
					{
						fillrectangle(r1[event][0],r1[event][1],r1[event][2],r1[event][3]);//两次同或为原来颜色
						event = 0;
					}
				}
				break;
		case WM_LBUTTONDOWN:
				setrop2(R2_NOTXORPEN);//二元光栅——NOT(屏幕颜色 XOR 当前颜色)
				for(int i=0;i<=10;i++)//当鼠标点击时会产生水波特效
				{
					setlinecolor(RGB(25*i,25*i,25*i));//设置圆颜色
					circle(m.x,m.y,2*i);
					Sleep(30);//停顿30ms
					circle(m.x,m.y,2*i);//抹去刚刚画的圆
				}
				switch(judge_b1(m.x,m.y))//switch函数,规定每个按钮的去向
				{
				case 1:  win_inter();break;
				case 2:  win_enter1();
				         nihe();
					     break;
				case 3:  closegraph();exit(0);
				default:
				FlushMouseMsgBuffer();//清空鼠标消息缓存区
	            break;
				}
	    break;
	 }
	}
}

void win_inter()//第二个界面,与第一个界面基本一致
{  
	event=0;
	setbkcolor(RGB(255,255,255));
	cleardevice();//清屏(取决于背景色)
	textl();
	drawtext("插值",&R21,DT_CENTER|DT_VCENTER|DT_SINGLELINE);
	textm();
	drawtext("牛顿",&R22,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R2内输入文字,水平居中,垂直居中,单行显示
	drawtext("拉格朗日",&R23,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R3内输入文字,水平居中,垂直居中,单行显示
	drawtext("线性插值",&R24,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R3内输入文字,水平居中,垂直居中,单行显示
	drawtext("返回",&R25,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R2内输入文字,水平居中,垂直居中,单行显示

	setlinecolor(BLACK);
	rectangle(r2[1][0],r2[1][1],r2[1][2],r2[1][3]);
	rectangle(r2[2][0],r2[2][1],r2[2][2],r2[2][3]);
	rectangle(r2[3][0],r2[3][1],r2[3][2],r2[3][3]);
	rectangle(r2[4][0],r2[4][1],r2[4][2],r2[4][3]);
	while(true)
	{
		m=GetMouseMsg();//读取鼠标操作
		switch(m.uMsg)
		{
	    case WM_MOUSEMOVE:
			    setrop2(R2_XORPEN);
				setlinecolor(LIGHTCYAN);//线条颜色为亮青色
				setlinestyle(PS_SOLID, 3);//设置画线样式为实现,10磅
				setfillcolor(WHITE);//填充颜色为白色
				if(judge_b2(m.x,m.y)!=0)
				{
					if(event != judge_b2(m.x,m.y))
					{
						event =judge_b2(m.x,m.y);//记录这一次触发的按钮
						fillrectangle(r2[event][0],r2[event][1],r2[event][2],r2[event][3]);//有框填充矩形(X1,Y1,X2,Y2)
					}
				}
				else
				{
					if(event != 0)//上次触发的按钮未被修正为原来的颜色
					{
						fillrectangle(r2[event][0],r2[event][1],r2[event][2],r2[event][3]);//两次同或为原来颜色
						event = 0;
					}
				}
				break;
		case WM_LBUTTONDOWN:
				setrop2(R2_NOTXORPEN);//二元光栅——NOT(屏幕颜色 XOR 当前颜色)
				for(int i=0;i<=10;i++)
				{
					setlinecolor(RGB(25*i,25*i,25*i));//设置圆颜色
					circle(m.x,m.y,2*i);
					Sleep(30);//停顿30ms
					circle(m.x,m.y,2*i);//抹去刚刚画的圆
				}
				switch(judge_b2(m.x,m.y))
				{
				case 1:  win_enter();
					     nuton();
						 break;
				case 2:  win_enter();
					     langrage();
					     break;
				case 3:  win_enter();
					     fenduan();
					     break;
				case 4:  cleardevice();//清屏(取决于背景色)
					     event=0;
					     win_first();
				default:
				FlushMouseMsgBuffer();//清空鼠标消息缓存区
	            break;
				}
	    break;
	 }
	}
}

void win_enter()//输入参数时的界面
{
	InputBox(s,30,"请输入点的个数");//弹出对话框
	strcpy(c,s);
	non=c[0]-'0';
	char str[50];
	for(int i=0;i<non;i++)
	{ 
		sprintf(str,"请输入第%d个点的x坐标和y坐标,用空格隔开,支持浮点数",i+1);
		InputBox(s,30,str);
		sscanf(s,"%f%f",&numx[i],&numy[i]);
	}
	InputBox(s,30,"请输入想要计算的插值点的个数");
	strcpy(c,s);
	nan=c[0]-'0';
	for(int i=0;i<nan;i++)
	{ 
		sprintf(str,"请输入所要求的第%d个插值点的x坐标",i+1);
		InputBox(s,30,str);
		sscanf(s,"%f",&numr[i]);
	}

}

void win_enter1()
{
	InputBox(s,30,"请输入点的个数");
	strcpy(c,s);
	non=c[0]-'0';
	char str[50];
	for(int i=0;i<non;i++)
	{ 
		sprintf(str,"请输入第%d个点的x坐标和y坐标,用空格隔开,支持浮点数",i+1);
		InputBox(s,30,str);
		sscanf(s,"%f%f",&numx[i],&numy[i]);
	}
	InputBox(s,30,"请输入想要计算的点的个数");
	strcpy(c,s);
	nan=c[0]-'0';
	for(int i=0;i<nan;i++)
	{ 
		sprintf(str,"请输入所要求的第%d个点的x坐标",i+1);
		InputBox(s,30,str);
		sscanf(s,"%f",&numr[i]);
	}

}

三、所遇困难和现存不足

1、GUI仍然不够美观
2、只能处理一维数据
3、离群点未判断(难搞,放弃了)
4、最后显示数据的对话框不能弹出到最前,会被数据录入窗口遮挡,需要手动挪开第一个窗口才能看到数据输出。

四、参考资料

插值原理:https://www.cnblogs.com/duye/p/8671820.html
界面设计:https://blog.csdn.net/weixin_44044411/article/details/104276757
感谢所有为我提供帮助的人。

五、源代码

https://download.csdn.net/download/qzl19/12708237

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值