728x90
반응형
728x170
▶ MathHelper.cs
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
namespace TestProject
{
/// <summary>
/// 수학 헬퍼
/// </summary>
public static class MathHelper
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Public
#region Y 구하기 - GetY(coefficientList, x)
/// <summary>
/// Y 구하기
/// </summary>
/// <param name="coefficientList">계수 리스트</param>
/// <param name="x">X</param>
/// <returns>Y</returns>
public static double GetY(List<double> coefficientList, double x)
{
double result = 0d;
double xFactor = 1d;
for(int i = 0; i < coefficientList.Count; i++)
{
result += xFactor * coefficientList[i];
xFactor *= x;
}
return result;
}
#endregion
#region 제곱 오차 구하기 - GetErrorSquared(pointList, coefficientList)
/// <summary>
/// 제곱 오차 구하기
/// </summary>
/// <param name="pointList">포인트 리스트</param>
/// <param name="coefficientList">계수 리스트</param>
/// <returns>제곱 오차</returns>
public static double GetErrorSquared(List<PointF> pointList, List<double> coefficientList)
{
double result = 0;
foreach(PointF point in pointList)
{
double error = point.Y - GetY(coefficientList, point.X);
result += error * error;
}
return result;
}
#endregion
#region 다항식 최소 제곱법 해 구하기 - GetPolynomialLeastSquaresFit(pointList, degree)
/// <summary>
/// 다항식 최소 제곱법 해 구하기
/// </summary>
/// <param name="pointList">포인트 리스트</param>
/// <param name="degree">차수</param>
/// <returns>다항식 최소 제곱법</returns>
public static List<double> GetPolynomialLeastSquaresFit(List<PointF> pointList, int degree)
{
double[,] coefficientArray = new double[degree + 1, degree + 2];
for(int i = 0; i <= degree; i++)
{
coefficientArray[i, degree + 1] = 0;
foreach(PointF point in pointList)
{
coefficientArray[i, degree + 1] -= Math.Pow(point.X, i) * point.Y;
}
for(int j = 0; j <= degree; j++)
{
coefficientArray[i, j] = 0;
foreach(PointF point in pointList)
{
coefficientArray[i, j] -= Math.Pow(point.X, j + i);
}
}
}
double[] resultArray = ProcessGaussianElimination(coefficientArray);
return resultArray.ToList<double>();
}
#endregion
//////////////////////////////////////////////////////////////////////////////// Private
#region 가우시안 제거 처리하기 - ProcessGaussianElimination(coefficientArray)
/// <summary>
/// 가우시안 제거 처리하기
/// </summary>
/// <param name="coefficientArray">계수 배열</param>
/// <returns>가우시안 제거 배열</returns>
private static double[] ProcessGaussianElimination(double[,] coefficientArray)
{
int maximumEquation = coefficientArray.GetUpperBound(0);
int maximumCoefficient = coefficientArray.GetUpperBound(1);
for(int i = 0; i <= maximumEquation; i++)
{
if(coefficientArray[i, i] == 0)
{
for(int j = i + 1; j <= maximumEquation; j++)
{
if(coefficientArray[j, i] != 0)
{
for(int k = i; k <= maximumCoefficient; k++)
{
double temporary = coefficientArray[i, k];
coefficientArray[i, k] = coefficientArray[j, k];
coefficientArray[j, k] = temporary;
}
break;
}
}
}
double coefficient_i_i = coefficientArray[i, i];
if(coefficient_i_i == 0)
{
throw new ArithmeticException
(
string.Format
(
"이 포인트들에 대한 유일한 해가 없습니다.",
coefficientArray.GetUpperBound(0) - 1
)
);
}
for(int j = i; j <= maximumCoefficient; j++)
{
coefficientArray[i, j] /= coefficient_i_i;
}
for(int j = 0; j <= maximumEquation; j++)
{
if(j != i)
{
double coefficient_j_i = coefficientArray[j, i];
for(int d = 0; d <= maximumCoefficient; d++)
{
coefficientArray[j, d] -= coefficientArray[i, d] * coefficient_j_i;
}
}
}
}
double[] solutionArray = new double[maximumEquation + 1];
for(int i = 0; i <= maximumEquation; i++)
{
solutionArray[i] = coefficientArray[i, maximumCoefficient];
}
return solutionArray;
}
#endregion
}
}
728x90
▶ 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 bool hasSolution = false;
/// <summary>
/// 포인트 리스트
/// </summary>
private List<PointF> pointList = new List<PointF>();
/// <summary>
/// 최적 계수 리스트
/// </summary>
private List<double> bestCoefficientList;
#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.coefficientTextBox.Clear();
this.errorTextBox.Clear();
Application.DoEvents();
int degree = (int)this.degreeNumericUpDown.Value;
this.bestCoefficientList = MathHelper.GetPolynomialLeastSquaresFit(this.pointList, degree);
this.hasSolution = true;
string coefficientList = string.Empty;
foreach(double coefficient in this.bestCoefficientList)
{
coefficientList += " " + coefficient.ToString();
}
this.coefficientTextBox.Text = coefficientList.Substring(1);
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.coefficientTextBox.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)
{
string[] coefficientList = this.coefficientTextBox.Text.Split();
this.bestCoefficientList = new List<double>();
foreach(string coefficient in coefficientList)
{
this.bestCoefficientList.Add(double.Parse(coefficient));
}
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;
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
DrawAxe(e.Graphics);
if(this.hasSolution)
{
using(Pen pen = new Pen(Color.Blue, 0))
{
const double xStep = 0.1;
double y0 = MathHelper.GetY(this.bestCoefficientList, X_MINIMUM);
for(double x = X_MINIMUM + xStep; x <= X_MAXIMUM; x += xStep)
{
double y1 = MathHelper.GetY(this.bestCoefficientList, x);
e.Graphics.DrawLine(pen, (float)(x - xStep), (float)y0, (float)x, (float)y1);
y0 = y1;
}
}
}
const float deltaX = (X_MAXIMUM - X_MINIMUM) / 200;
const 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.bestCoefficientList));
this.errorTextBox.Text = error.ToString();
}
#endregion
}
}
728x90
반응형
그리드형(광고전용)
'C# > WinForm' 카테고리의 다른 글
[C#/WINFORM] 에피트로코이드(Epitrochoid) 그리기 (0) | 2018.12.22 |
---|---|
[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] 선형 최소 제곱법(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 |
댓글을 달아 주세요