■ 선형 최소 제곱법(Linear Least Squares Method) 사용하기
------------------------------------------------------------------------------------------------------------------------
▶ MathHelper.cs
using System; using System.Collections.Generic; using System.Drawing;
namespace TestProject { /// <summary> /// 수학 헬퍼 /// </summary> public class MathHelper { //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Static //////////////////////////////////////////////////////////////////////////////// Public
#region 제곱 오차 구하기 - GetErrorSquared(pointList, a, b)
/// <summary> /// 제곱 오차 구하기 /// </summary> /// <param name="pointList">포인트 리스트</param> /// <param name="a">A</param> /// <param name="b">B</param> /// <returns>제곱 오차</returns> public static double GetErrorSquared(List<PointF> pointList, double a, double b) { double result = 0;
foreach(PointF point in pointList) { double error = point.Y - (a * point.X + b);
result += error * error; }
return result; }
#endregion #region 선형 최소 제곱법 해 구하기 - GetLinearLeastSquaresFit(pointList, a, b)
/// <summary> /// 선형 최소 제곱법 해 구하기 /// </summary> /// <param name="pointList">포인트 리스트</param> /// <param name="a">A</param> /// <param name="b">B</param> /// <returns>오차</returns> public static double GetLinearLeastSquaresFit(List<PointF> pointList, out double a, out double b) { double count = pointList.Count; double xSummary = 0; double ySummary = 0; double xxSummary = 0; double xySummary = 0;
foreach(PointF point in pointList) { xSummary += point.X; ySummary += point.Y; xxSummary += point.X * point.X; xySummary += point.X * point.Y; }
a = (xySummary * count - xSummary * ySummary) / (xxSummary * count - xSummary * xSummary); b = (xySummary * xSummary - ySummary * xxSummary) / (xSummary * xSummary - count * xxSummary);
return Math.Sqrt(GetErrorSquared(pointList, a, b)); }
#endregion } }
|
▶ MainForm.cs
using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Drawing2D; using System.Windows.Forms;
namespace TestProject { /// <summary> /// 메인 폼 /// </summary> public partial class MainForm : Form { //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary> /// X 최소값 /// </summary> private const float X_MINIMUM = -10.0f;
/// <summary> /// X 최대값 /// </summary> private const float X_MAXIMUM = 10.0f;
/// <summary> /// Y 최소값 /// </summary> private const float Y_MINIMUM = -10.0f;
/// <summary> /// Y 최대값 /// </summary> private const float Y_MAXIMUM = 10.0f;
/// <summary> /// 그리기 변환 /// </summary> private Matrix drawingTransform;
/// <summary> /// 반전 변환 /// </summary> private Matrix inverseTransform;
/// <summary> /// 포인트 리스트 /// </summary> private List<PointF> pointList = new List<PointF>();
/// <summary> /// 솔루션 여부 /// </summary> private bool hasSolution = false;
/// <summary> /// 최적 A /// </summary> private double bestA;
/// <summary> /// 최적 B /// </summary> private double bestB;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - MainForm()
/// <summary> /// 생성자 /// </summary> public MainForm() { InitializeComponent();
#region 이벤트를 설정한다.
Load += Form_Load; this.calculateButton.Click += calculateButton_Click; this.clearButton.Click += clearButton_Click; this.graphButton.Click += graphButton_Click; this.graphPictureBox.MouseClick += graphPictureBox_MouseClick; this.graphPictureBox.Paint += graphPictureBox_Paint;
#endregion }
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Private //////////////////////////////////////////////////////////////////////////////// Event
#region 폼 로드시 처리하기 - Form_Load(sender, e)
/// <summary> /// 폼 로드시 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void Form_Load(object sender, EventArgs e) { RectangleF worldRectangle = new RectangleF ( X_MINIMUM, Y_MINIMUM, X_MAXIMUM - X_MINIMUM, Y_MAXIMUM - Y_MINIMUM );
PointF[] pointArray = { new PointF(0, graphPictureBox.ClientSize.Height), new PointF(graphPictureBox.ClientSize.Width, graphPictureBox.ClientSize.Height), new PointF(0, 0), };
this.drawingTransform = new Matrix(worldRectangle, pointArray);
this.inverseTransform = this.drawingTransform.Clone();
this.inverseTransform.Invert(); }
#endregion #region 계산하기 버튼 클릭시 처리하기 - calculateButton_Click(sender, e)
/// <summary> /// 계산하기 버튼 클릭시 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void calculateButton_Click(object sender, EventArgs e) { Cursor = Cursors.WaitCursor;
this.aTextBox.Clear(); this.bTextBox.Clear(); this.errorTextBox.Clear();
Application.DoEvents();
MathHelper.GetLinearLeastSquaresFit(this.pointList, out this.bestA, out this.bestB);
this.hasSolution = true;
this.aTextBox.Text = this.bestA.ToString(); this.bTextBox.Text = this.bestB.ToString();
ShowError();
this.hasSolution = true;
this.graphPictureBox.Refresh();
Cursor = Cursors.Default; }
#endregion #region 지우기 버튼 클릭시 처리하기 - clearButton_Click(sender, e)
/// <summary> /// 지우기 버튼 클릭시 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void clearButton_Click(object sender, EventArgs e) { this.pointList = new List<PointF>();
this.hasSolution = false;
this.graphPictureBox.Refresh();
this.aTextBox.Clear(); this.bTextBox.Clear(); this.errorTextBox.Clear(); }
#endregion #region 그래프 버튼 클릭시 처리하기 - graphButton_Click(sender, e)
/// <summary> /// 그래프 버튼 클릭시 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void graphButton_Click(object sender, EventArgs e) { this.bestA = double.Parse(this.aTextBox.Text); this.bestB = double.Parse(this.bTextBox.Text);
ShowError();
this.graphPictureBox.Refresh(); }
#endregion #region 그래프 픽처 박스 마우스 클릭시 처리하기 - graphPictureBox_MouseClick(sender, e)
/// <summary> /// 그래프 픽처 박스 마우스 클릭시 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void graphPictureBox_MouseClick(object sender, MouseEventArgs e) { PointF[] pointArray = { new PointF(e.X, e.Y) };
this.inverseTransform.TransformPoints(pointArray);
this.pointList.Add(pointArray[0]);
this.graphPictureBox.Refresh(); }
#endregion #region 그래프 픽처 박스 페인트시 처리하기 - graphPictureBox_Paint(sender, e)
/// <summary> /// 그래프 픽처 박스 페인트시 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void graphPictureBox_Paint(object sender, PaintEventArgs e) { e.Graphics.Transform = this.drawingTransform;
DrawAxe(e.Graphics);
if(this.hasSolution) { using(Pen pen = new Pen(Color.Blue, 0)) { double y0 = this.bestA * X_MINIMUM + this.bestB; double y1 = this.bestA * X_MAXIMUM + this.bestB;
e.Graphics.DrawLine ( pen, (float)X_MINIMUM, (float)y0, (float)X_MAXIMUM, (float)y1 ); } }
float deltaX = (X_MAXIMUM - X_MINIMUM) / 200; float deltaY = (Y_MAXIMUM - Y_MINIMUM) / 200;
using(Pen pen = new Pen(Color.Black, 0)) { foreach(PointF point in this.pointList) { e.Graphics.FillRectangle ( Brushes.White, point.X - deltaX, point.Y - deltaY, 2 * deltaX, 2 * deltaY );
e.Graphics.DrawRectangle ( pen, point.X - deltaX, point.Y - deltaY, 2 * deltaX, 2 * deltaY ); } } }
#endregion
//////////////////////////////////////////////////////////////////////////////// Function
#region 축 그리기 - DrawAxe(graphics)
/// <summary> /// 축 그리기 /// </summary> /// <param name="graphics">그래픽스</param> private void DrawAxe(Graphics graphics) { using(Pen pen = new Pen(Color.Black, 0)) { const float xThick = 0.2f; const float yThick = 0.2f;
graphics.DrawLine(pen, X_MINIMUM, 0, X_MAXIMUM, 0);
for(float x = X_MINIMUM; x <= X_MAXIMUM; x += 1.0f) { graphics.DrawLine(pen, x, -yThick, x, yThick); }
graphics.DrawLine(pen, 0, Y_MINIMUM, 0, Y_MAXIMUM);
for(float y = Y_MINIMUM; y <= Y_MAXIMUM; y += 1.0f) { graphics.DrawLine(pen, -xThick, y, xThick, y); } } }
#endregion #region 오차 보여주기 - ShowError()
/// <summary> /// 오차 보여주기 /// </summary> private void ShowError() { double error = Math.Sqrt(MathHelper.GetErrorSquared(this.pointList, this.bestA, this.bestB));
this.errorTextBox.Text = error.ToString(); }
#endregion } }
|
------------------------------------------------------------------------------------------------------------------------
'C# > WinForm' 카테고리의 다른 글
[C#/WINFORM] TabControl 클래스 : 사용자 정의 그리기 (0) | 2018.12.22 |
---|---|
[C#/WINFORM] 실시간 그래프 그리기 (0) | 2018.12.21 |
[C#/WINFORM] 타원과 타원 교차점 구하기 (0) | 2018.12.21 |
[C#/WINFORM] PictureBox 클래스 : SizeMode 속성에 따라 이미지 구하기 (0) | 2018.12.19 |
[C#/WINFORM] 다항식 최소 제곱법(Polynomial Least Squares Method) 사용하기 (0) | 2018.12.19 |
[C#/WINFORM] 선형 최소 제곱법(Linear Least Squares Method) 사용하기 (0) | 2018.12.19 |
[C#/WINFORM] ColorDialog 클래스 : 커스텀 색상 사용하기 (0) | 2018.12.19 |
[C#/WINFORM] ImageAttributes 클래스 : 무지개 색상 이미지 구하기 (0) | 2018.12.19 |
[C#/WINFORM] ImageAttributes 클래스 : 색상 이미지 구하기 (0) | 2018.12.19 |
[C#/WINFORM] ImageAttributes 클래스 : 채널 이미지 구하기 (0) | 2018.12.18 |
[C#/WINFORM] ImageAttributes 클래스 : 세피아 이미지 구하기 (0) | 2018.12.18 |
댓글을 달아 주세요