* 온라인 이차방정식 풀이
* 온라인 이차방정식 풀이 (2)
* 온라인 방정식 풀이 (3)
이차방정식
하지만
이런 경우 (수치적 오차를 줄일 수 있는) 보다 안전한 해법을 생각해보자. 단, 수치적 계산에서 기계입실론 수(즉 유효숫자 개수의 한계로 인해
유효숫자 개수를 16개로 한정한 근사값 계산에서
다시 원래의 이차방정식
판별식
근의 공식을 쓰면
우선 유효숫자 개수가 16개가 되도록 반올림하여서
이므로, 근사직으로 두 근은
과
이디.
그런데 수치적으로 계산할 때는 이 두 번 째 근사적 근
라는 계산과는 다르게 진행된다.
참고로, 주어진 이차방정식은 계수의 대칭성 때문에 만일
그러므로
이차방정식
처럼
(참고로, 복소수 계수의 이차방정식의 (오차를 줄이기 위한) 수치적 해법은 이것과 조금 다르다.
* 위의 과정을 확인햐기 위해 Python 2.7 인터프리터로 계산 과정을 추적해 보았다.
(아래에서 두 번 째 근 x_2 가 0 이 아니라 -1.11022302463e-08 로 출력되었다.)
>>> a = 1e-8
>>> b = 2.0
>>> c = 1e-8
>>> print a, b, c
1e-08 2.0 1e-08
>>> d = b*b - 4.0*a*c
>>> print d
4.0
>>> print sqrt(d)
2.0
>>> q1 = -b - sqrt(d)
>>> q2 = -b + sqrt(d)
>>> print q1, q2
-4.0 -2.22044604925e-16
>>> x1 = q1/(2.0*a)
>>> x2 = q2/(2.0*a)
>>> print x1, x2
-200000000.0 -1.11022302463e-08
* 왜 위와 같은 결과가 나왔는지 알아보기 위해 이번에는 다소 다르게 테스트해보았다.
>>> t = 3.9999999999999996
>>> t
3.9999999999999996
>>> u = sqrt(t)
>>> u
1.9999999999999998
>>> -2 + u
-2.220446049250313e-16
>>> (-2 + u)/2e-8
-1.1102230246251565e-08
* 다음은 C 언어로 작성해본 것인데 위의 Python 인터프리터로 알아본 결과와 비슷하다.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
struct ROOTS {
double re1;
double im1;
double re2;
double im2;
};
void printRoot(const char *msg, struct ROOTS *data, int ndx) {
printf(msg);
if (ndx == 0) {
if (data->im1 == 0.0)
printf("%e\n", data->re1);
else if (data->re1 == 0.0)
printf("%e j\n", data->im1);
else
printf("%e + %e j\n", data->re1, data->im1);
}
else {
if (data->im2 == 0.0)
printf("%e\n", data->re2);
else if (data->re2 == 0.0)
printf("%e j\n", data->im2);
else
printf("%e + %e j\n", data->re2, data->im2);
}
}
struct ROOTS *solveQuadraticPlain(double a, double b, double c) {
struct ROOTS *roots = (struct ROOTS *)calloc(sizeof(struct ROOTS), 1);
double d = b*b - 4.0*a*c;
double r, x1, x2;
if (d == 0.0) {
x1 = -b/(2.0*a);
roots->re1 = x1;
roots->im1 = 0.0;
roots->re2 = x1;
roots->im2 = 0.0;
}
else if (d > 0.0) {
r = sqrt(d);
x1 = (-b - r)/(2.0*a);
x2 = (-b + r)/(2.0*a);
roots->re1 = x1;
roots->im1 = 0.0;
roots->re2 = x2;
roots->im2 = 0.0;
}
else {
r = sqrt(-d);
x1 = -b/(2.0*a);
x2 = r/(2.0*a);
roots->re1 = x1;
roots->im1 = -x2;
roots->re2 = x1;
roots->im2 = x2;
}
return roots;
}
int main() {
struct ROOTS *sol = (struct ROOTS *)solveQuadraticPlain(1.0, 2.0, 1.0);
printf("Use the plain quadratic root fomula:\n");
printf(" The solutions of the equation x^2 + 2x + 1 = 0 are\n");
printRoot(" x1 = ", sol, 0);
printRoot(" x2 = ", sol, 1);
free(sol);
sol = (struct ROOTS *)solveQuadraticPlain(1.0e-8, 2.0, 1.0e-8);
printf("\n");
printf(" The solutions of the equation 10^(-8) x^2 + 2x + 10^(-8) = 0 are\n");
printRoot(" x1 = ", sol, 0);
printRoot(" x2 = ", sol, 1);
free(sol);
return 0;
}
/*
Execute Result:
Use the plain quadratic root fomula:
The solutions of the equation x^2 + 2x + 1 = 0 are
x1 = -1.000000e+000
x2 = -1.000000e+000
The solutions of the equation 10^(-8) x^2 + 2x + 10^(-8) = 0 are
x1 = -2.000000e+008
x2 = -1.110223e-008
*/
* Java 언어로 작성해 보아도 마찬가지이다.
import java.util.*;
public class SolveQuadraticEqPlain_01 {
public static void printRoot(String msg, double[] data, int ndx) {
System.out.printf(msg);
if (ndx == 0) {
if (data[1] == 0.0)
System.out.printf("%e\n", data[0]);
else if (data[0] == 0.0)
System.out.printf("%e j\n", data[1]);
else
System.out.printf("%e + %e j\n", data[0], data[1]);
}
else {
if (data[3] == 0.0)
System.out.printf("%e\n", data[2]);
else if (data[2] == 0.0)
System.out.printf("%e j\n", data[3]);
else
System.out.printf("%e + %e j\n", data[2], data[3]);
}
}
public static double[] solveQuadraticPlain(double a, double b, double c) {
double[] roots = new double[4];
double d = b*b - 4.0*a*c;
double r, x1, x2;
if (d == 0.0) {
x1 = -b/(2.0*a);
roots[0] = x1;
roots[1] = 0.0;
roots[2] = x1;
roots[3] = 0.0;
}
else if (d > 0.0) {
r = Math.sqrt(d);
x1 = (-b - r)/(2.0*a);
x2 = (-b + r)/(2.0*a);
roots[0] = x1;
roots[1] = 0.0;
roots[2] = x2;
roots[3] = 0.0;
}
else {
r = Math.sqrt(-d);
x1 = -b/(2.0*a);
x2 = r/(2.0*a);
roots[0] = x1;
roots[1] = -x2;
roots[2] = x1;
roots[3] = x2;
}
return roots;
}
public static void main(String[] args) {
double[] sol = solveQuadraticPlain(1.0, 2.0, 1.0);
System.out.printf("Use the plain quadratic root fomula:\n");
System.out.printf(" The solutions of the equation x^2 + 2x + 1 = 0 are\n");
printRoot(" x1 = ", sol, 0);
printRoot(" x2 = ", sol, 1);
sol = solveQuadraticPlain(1.0e-8, 2.0, 1.0e-8);
System.out.printf("\n");
System.out.printf(" The solutions of the equation 10^(-8) x^2 + 2x + 10^(-8) = 0 are\n");
printRoot(" x1 = ", sol, 0);
printRoot(" x2 = ", sol, 1);
}
}
/*
Execute Result:
Use the plain quadratic root fomula:
The solutions of the equation x^2 + 2x + 1 = 0 are
x1 = -1.000000e+00
x2 = -1.000000e+00
The solutions of the equation 10^(-8) x^2 + 2x + 10^(-8) = 0 are
x1 = -2.000000e+08
x2 = -1.110223e-08
*/
* 참고로 아래는 Octave 로 이차방정식
( -2.0000e+008 의 역수 -5.0000e-009 도 근으로 출력되었다. )
octave.exe:1> c = [1 2 1];
octave.exe:2> roots(c)
ans =
-1
-1
octave.exe:3> c = [1e-8 2 1e-8];
octave.exe:4> roots(c)
ans =
-2.0000e+008
-5.0000e-009
* 또 아래는 Python 의 numpy 를 이용하여 이차방정식
( -200000000.0 의 역수 -5.0000000000000001e-09 도 근으로 출력되었다. )
from numpy import poly1d
P = poly1d([1e-8, 2.0, 1e-8], variable = 'x')
print("Polynomial p(x) =")
print(P)
print("\nThe complex roots of p(x) = 0 are as a numpy array:")
print(P.r)
print("\nThe complex roots of p(x) = 0 are as a python list:")
print(list(P.r))
"""
Output:
Polynomial p(x) =
2
1e-08 x + 2 x + 1e-08
The complex roots of p(x) = 0 are as a numpy array:
[ -2.00000000e+08 -5.00000000e-09]
The complex roots of p(x) = 0 are as a python list:
[-200000000.0, -5.0000000000000001e-09]
"""
아래의 두 그림은 각각 Mathematica 와 Maxima 를 써서 이차방정식을 풀어본 결과이다.
* Mathematica 로 이차방정식 풀이
( -5.×10^{-8} 이 근으로 출력되었다. )
* Maxima 로 이차방정식 풀이
( Maxima 에서는 0 이 근으로 출력되었다. )
* 또 아래 그림은 온라인 이차방정식 풀이 계산기 math.com 을 사용한 결과이다.
* 이차방정식의 근을 (수치적으로 안전하게) 구하기 위해 다시 작성된 C 언어 소스
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
struct ROOTS {
double re1;
double im1;
double re2;
double im2;
};
void printRoot(const char *msg, struct ROOTS *data, int ndx) {
printf(msg);
if (ndx == 0) {
if (data->im1 == 0.0)
printf("%e\n", data->re1);
else if (data->re1 == 0.0)
printf("%e j\n", data->im1);
else
printf("%e + %e j\n", data->re1, data->im1);
}
else {
if (data->im2 == 0.0)
printf("%e\n", data->re2);
else if (data->re2 == 0.0)
printf("%e j\n", data->im2);
else
printf("%e + %e j\n", data->re2, data->im2);
}
}
struct ROOTS *solveQuadraticPlain(double a, double b, double c) {
struct ROOTS *roots = (struct ROOTS *)calloc(sizeof(struct ROOTS), 1);
double d = b*b - 4.0*a*c;
double r, x1, x2;
if (d == 0.0) {
x1 = -b/(2.0*a);
roots->re1 = x1;
roots->im1 = 0.0;
roots->re2 = x1;
roots->im2 = 0.0;
}
else if (d > 0.0) {
r = sqrt(d);
x1 = (-b - r)/(2.0*a);
x2 = (-b + r)/(2.0*a);
roots->re1 = x1;
roots->im1 = 0.0;
roots->re2 = x2;
roots->im2 = 0.0;
}
else {
r = sqrt(-d);
x1 = -b/(2.0*a);
x2 = r/(2.0*a);
roots->re1 = x1;
roots->im1 = -x2;
roots->re2 = x1;
roots->im2 = x2;
}
return roots;
}
struct ROOTS *solveQuadraticSafe(double a, double b, double c) {
struct ROOTS *roots = (struct ROOTS *)calloc(sizeof(struct ROOTS), 1);
double d = b*b - 4.0*a*c;
double r = sqrt(b*b - 4.0*a*c);
double q, x1, x2;
if (d == 0.0) {
x1 = -b/(2.0*a);
roots->re1 = x1;
roots->im1 = 0.0;
roots->re2 = x1;
roots->im2 = 0.0;
}
else if (d > 0.0) {
r = sqrt(d);
if (b > 0.0) {
q = -b - r;
}
else {
q = -b + r;
}
x1 = q/(2.0*a);
x2 = (2.0*c)/q;
roots->re1 = x1;
roots->im1 = 0.0;
roots->re2 = x2;
roots->im2 = 0.0;
}
else {
r = sqrt(-d);
x1 = -b/(2.0*a);
x2 = r/(2.0*a);
roots->re1 = x1;
roots->im1 = -x2;
roots->re2 = x1;
roots->im2 = x2;
}
return roots;
}
int main() {
struct ROOTS *sol= (struct ROOTS *)solveQuadraticPlain(1.0, 2.0, 1.0);
printf("Use the plain quadratic root fomula:\n");
printf(" The solutions of the equation x^2 + 2x + 1 = 0 are\n");
printRoot(" x1 = ", sol, 0);
printRoot(" x2 = ", sol, 1);
free(sol);
sol= (struct ROOTS *)solveQuadraticPlain(1.0e-8, 2.0, 1.0e-8);
printf("\n");
printf(" The solutions of the equation 10^(-8) x^2 + 2x + 10^(-8) = 0 are\n");
printRoot(" x1 = ", sol, 0);
printRoot(" x2 = ", sol, 1);
free(sol);
sol= (struct ROOTS *)solveQuadraticSafe(1.0, 2.0, 1.0);
printf("\n");
printf("Use the safe quadratic root fomula:\n");
printf(" The solutions of the equation x^2 + 2x + 1 = 0 are\n");
printRoot(" x1 = ", sol, 0);
printRoot(" x2 = ", sol, 1);
free(sol);
sol= (struct ROOTS *)solveQuadraticSafe(1.0e-8, 2.0, 1.0e-8);
printf(" \n");
printf("The solutions of the equation 10^(-8) x^2 + 2x + 10^(-8) = 0 are\n");
printRoot(" x1 = ", sol, 0);
printRoot(" x2 = ", sol, 1);
free(sol);
return 0;
}
/*
Execute Result:
Use the plain quadratic root fomula:
The solutions of the equation x^2 + 2x + 1 = 0 are
x1 = -1.000000e+000
x2 = -1.000000e+000
The solutions of the equation 10^(-8) x^2 + 2x + 10^(-8) = 0 are
x1 = -2.000000e+008
x2 = -1.110223e-008
Use the safe quadratic root fomula:
The solutions of the equation x^2 + 2x + 1 = 0 are
x1 = -1.000000e+000
x2 = -1.000000e+000
The solutions of the equation 10^(-8) x^2 + 2x + 10^(-8) = 0 are
x1 = -2.000000e+008
x2 = -5.000000e-009
*/
'학습 > 수학' 카테고리의 다른 글
온라인 복소수 계산기 소개 (0) | 2012.03.29 |
---|---|
cos 함수의 가법정리(덧셈정리)를 증명하는 그림과 애니메이션 (0) | 2012.03.22 |
(사칙연산과 근호만을 사용해서) 삼차방정식과 사차방정식을 푸는 대수적 해법 (0) | 2012.02.28 |
Euler의 분할함수(partition function) 계산기 (0) | 2012.02.21 |
온라인 다항식 계산기 (0) | 2012.02.16 |