대수식을 심볼릭 프로그래밍으로 풀기 위해
자료 구조로 만들기 진행
제곱, 수, 심볼 변수 정의하기
설명할 내용없이 단순함
class Power():
def __init__(self, base, exponent):
self.base = base
self.exponent = exponent
class Number():
def __init__(self, number):
self.number = number
class Variable():
def __init__(self, symbol):
self.symbol = symbol
Power(Variable("x"), Number(2))
곱 클래스 만들기
실제 연산하는 내용은 없지만 단순하게 이것도 구현
이건 3 x^2
class Product():
def __init__(self, exp1, exp2):
self.exp1 = exp1
self.exp2 = exp2
Product(Number(3), Power(Variable("x"), Number(2)))
약간 복잡한 함수 정의하기
Function은 함수
Apply는 함수와 해당 함수 인자
다음 함수식은
(3x^2 + x) * sin(x)
class Sum():
def __init__(self, *exps):
self.exps = exps
class Function():
def __init__(self, name):
self.name = name
class Apply():
def __init__(self, function, argument):
self.function = function
self.argument = argument
f_expression = Product(
Sum(
Product(
Number(3),
Power(
Variable("x"),
Number(2)
)
),
Variable("x")
),
Apply(
Function("sin"),
Variable('x')
)
)
cos(x^3 - 5)는 다음과 같이 표현
Apply(
Function("cos"),
Sum(
Power(
Variable("x"),
Number("3")
),
Number(-5)
)
)
로그 연산 표현해보기
ln(y^z)는 원래 쉽게 표현할수 있으나
지금 구현한 함수로는 다음과 같이 표현가능
from math import log
def f(y, z):
return log(y ** z)
Apply(
Function("ln"),
Power(
Variable("y"),
Variable("z")
)
)
나눗셈 구현하기
(a + b) / 2같은 경우는 다음과 같이 표현
class Quotient():
def __init__(self, numerator, denominator):
self.numerator = numerator
self.denominator = denominator
Quotient(
Sum(
Variable("a"),
Variable("b")
),
Number(2)
)
차 연산 구현해서 이차 방정식 판별식 구현하기
이차 방정식 y = ax^2 + bx + c의 판별식 D = b ^ 2 - 4 ac를 다음과 같이 표현 가능
class Difference():
def __init__(self, exp1, exp2):
self.exp1 = exp1
self.exp2 = exp2
Difference(
Power(
Variable("b"),
Number(2)
),
Product(
Number(4),
Product(
Variable("a"),
Variable("c")
)
)
)
차연산 표현하기
차연산도 간단하게 이런식으로
class Negative():
def __init__(self, exp):
self.exp = exp
Negative(
Sum(
Power(
Variable("x"),
Number(2)
),
Variable("y")
)
)
2차 방정식 근의 공식 표현하기
근의 공식은 길어지지만 이런식으로
A = Variable('a')
B = Variable('b')
C = Variable('c')
Sqrt = Function('sqrt')
Quotient(
Sum(
Negative(B),
Apply(
Sqrt,
Difference(
Power(
B, Number(2)
),
Product(
Number(4),
Product(A, C)
)
)
)
),
Product(
Number(2),
A
)
)
식에 있는 모든 변수 찾아내기
def f(x):
return x^3
기존의 파이썬 함수는 결과만 반환하지 안에 뭐가 들어있는지 모르므로
앞서 동작은 안하지만 대수 방정식 구조를 만듬
일단 기존에 만든 것에 어떤 변수가 있는지 반환하는 함수 만듬
일단 변수만 찾아내는 함수니까 넣은 변수는 잘 찾는거같다.
def distinct_variables(exp):
if isinstance(exp, Variable):
return (set(exp.symbol))
elif isinstance(exp, Number):
return set()
elif isinstance(exp, Sum):
return set().union(*[distinct_variables(exp) for exp in exp.exps])
elif isinstance(exp, Product):
return distinct_variables(exp.exp1).union(distinct_variables(exp.exp2))
elif isinstance(exp, Power):
return distinct_variables(exp.base).union(distinct_variables(exp.exponent))
elif isinstance(exp, Apply):
return distinct_variables(exp.argument)
else:
raise TypeError("Not a valid expression")
print(distinct_variables(Variable("z")))
print(distinct_variables(Number(3)))
print(distinct_variables(Sum(Number(3), Number(4))))
print(distinct_variables(Sum(Number(3), Variable("x"))))
print(distinct_variables(Sum(Variable("x"), Variable("y"))))
print(distinct_variables(Power(Variable("x"), Number(3))))
print(distinct_variables(Product(Power(Variable("x"), Number(3)), Variable("y"))))
방정식 동작하게 하기
아까 구현한 변수, 곱같은 클래스에 evaluate 함수 추가
evaluate로 변수를 바인딩해서 계산 수행
from abc import ABC, abstractmethod
class Expression(ABC):
@abstractmethod
def evaluate(self, **bindings):
pass
class Number(Expression):
def __init__(self, number):
self.number = number
def evaluate(self, **bindings):
return self.number
class Variable(Expression):
def __init__(self, symbol):
self.symbol = symbol
def evaluate(self, **bindings):
try:
return bindings[self.symbol]
except:
raise KeyError("Variable '{}' is not bound.".format(self.symbol))
class Product(Expression):
def __init__(self, exp1, exp2):
self.exp1 = exp1
self.exp2 = exp2
def evaluate(self, **bindings):
return self.exp1.evaluate(**bindings) * self.exp2.evaluate(**bindings)
Product(Variable('x'), Variable('y')).evaluate(x=2, y=5)
타 연산 구현하기
방금 변수와 곱을 구현했고
나머지 연산들을 구현해보자
sin, cos, ln 같은 초월함수는 따로 Apply에서 바인딩 되도록 하고
합, 제곱, 차, 나눗셈 연산은 기본 연산대로 evaluate 추가
import math
from math import sin, cos, log
_function_bindings = {
"sin":math.sin,
"cos":math.cos,
"ln":math.log
}
class Apply(Expression):
def __init__(self, function, argument):
self.function = function
self.argument = argument
def evaluate(self, **bindings):
return _function_bindings[self.function.name](self.argument.evaluate(**bindings))
class Sum(Expression):
def __init__(self, *exps):
self.exps = exps
def evaluate(self, **bindings):
return sum([exp.evaluate(**bindings) for exp in self.exps])
class Power(Expression):
def __init__(self, base, exponent):
self.base = base
self.exponent = exponent
def evaluate(self, **bindings):
return self.base.evaluate(**bindings) ** self.exponent.evaluate(**bindings)
class Difference(Expression):
def __init__(self, exp1, exp2):
self.exp1 = exp1
self.exp2 = exp2
def evaluate(self, **bindings):
return self.exp1.evaluate(**bindings) - self.exp2.evaluate(**bindings)
class Quotient(Expression):
def __init__(self, numerator, denominator):
self.numerator = numerator
self.denominator = denominator
def evaluate(self, **bindings):
return self.numerate.evaluate(**bindings) / self.denominator.evaluate(**bindings)
(3 x ^2 + x) * sin(x)
이 식을 심볼릭 방법으로 한것과
그냥 파이썬 함수에 대입한 결과를 보면 동일하게 잘 연산되었음
f_expression = Product(
Sum(
Product(
Number(3),
Power(
Variable("x"),
Number(2)
)
),
Variable("x")
),
Apply(
Function("sin"),
Variable("x")
)
)
print(f_expression.evaluate(x=5))
def f(x):
return (3 * x ** 2 + x) * sin(x)
print(f(5))
표현식 전개하기
지금까지 구한 표현식을 그대로 출력하면 클래스랑 주소를 알려주는데
내용을 전개해주면 좋으니 우선 추상 클래스 expression을 다음과 같이 구현
class Expression(ABC):
@abstractmethod
def evaluate(self, **bindings):
pass
@abstractmethod
def expand(self):
pass
@abstractmethod
def display(self):
pass
def __repr__(self):
return self.display()
지금까지 구현한
숫자, 변수, 연산들을 다시 구현해보자
먼저 가장 간단한 숫자와 변수
아까와는 달리 주소가 아니라 클래스명과 값이 잘 나온다
class Number(Expression):
def __init__(self, number):
self. number = number
def evaluate(self, **bindings):
return self.number
def expand(self):
return self
def display(self):
return "Number({})".format(self.number)
class Variable(Expression):
def __init__(self, symbol):
self.symbol = symbol
def evaluate(self, **bindings):
return bindings[self.symbol]
def expand(self):
return self
def display(self):
return "Variable(\"{}\")".format(self.symbol)
print(Number(4))
print(Variable("x"))
합, 차연산 구현하고 동작시켜보면 이런식
class Sum(Expression):
def __init__(self, *exps):
self.exps = exps
def evaluate(self, **bindings):
return sum([exp.evaluate(**bindings) for exp in self.exps])
def expand(self):
return (Sum(*[exp.expand() for exp in self.exps]))
def display(self):
return "Sum({})".format(",".join([e.display() for e in self.exps]))
class Difference(Expression):
def __init__(self, exp1, exp2):
self.exp1 = exp1
self.exp2 = exp2
def evaluate(self, **bindings):
return self.exp1.evaluate(**bindings) - self.exp2.evaluate(**bindings)
def expand(self):
return self
def display(self):
return "Difference({},{})".format(self.exp1.display(), self.exp2.display())
print(Sum(Number(3), Number(5)))
print(Sum(Variable("x"), Number(3)))
print(Difference(Number(3), Number(5)))
print(Difference(Variable("x"), Number(3)))
print(Difference(Variable("x"), Number(3)).evaluate(x=4))
print(Sum(Variable("x"), Difference(Number(3), Number(5))))
tmp_exp = Sum(Variable("x"), Number(3))
print(tmp_exp)
tmp_exp.expand()
나머지 연산도 구현
class Product(Expression):
def __init__(self, exp1, exp2):
self.exp1 = exp1
self.exp2 = exp2
def evaluate(self, **bindings):
return self.exp1.evaluate(**bindings) * self.exp2.evaluate(**bindings)
def expand(self):
expanded1 = self.exp1.expand()
expanded2 = self.exp2.expand()
if isinstance(expanded1, Sum):
return Sum(*[Product(e, expanded2).expand() for e in expanded1.exps])
elif isinstance(expanded2, Sum):
return Sum(*[Product(expanded1, e) for e in expanded2.exps])
else:
return Product(expanded1, expanded2)
def display(self):
return "Product({},{})".format(self.exp1.display(), self.exp2.display())
class Quotient(Expression):
def __init__(self, numerator, denominator):
self.numerator = numerator
self.denominator = denominator
def evaluate(self, **bindings):
return self.numerator.evaluate(**bindings) / self.denominator.evaluate(**bindings)
def expand(self):
return self
def display(self):
return "Quotient({},{})".format(self.numerator.display(),self.denominator.display())
class Negative(Expression):
def __init__(self, exp):
self.exp = exp
def evaluate(self, **bindings):
return - self.exp.evaluate(**bindings)
def expand(self):
return self
def display(self):
return "Negative({})".format(self.exp.display())
class Power(Expression):
def __init__(self, base, exponent):
self.base = base
self.exponent = exponent
def evaluate(self, **bindings):
return self.base.evaluate(**bindings) ** self.exponent.evaluate(**bindings)
def expand(self):
return self
def display(self):
return "Power({},{})".format(self.base.display(),self.exponent.display())
class Function():
def __init__(self, name, make_latex=None):
self.name = name
self.make_latex = make_latex
def latex(self, arg_latex):
if self.make_latex:
return self.make_latex(arg_latex)
else:
return " \\operatorname{{ {} }} \\left( {} \\right)".format(self.name, arg_latex)
class Apply(Expression):
def __init__(self, function, argument):
self.function = function
self.argument = argument
def evaluate(self, **bindings):
return _function_bindings[self.function.name](self.argument.evaluate(**bindings))
def expand(self):
return Apply(self.function, self.argument.expand())
def display(self):
return "Apply(Function(\"{}\"),{})".format(self.function.name, self.argument.display())
식 간단하게 만들고 그냥 출력했을때랑
전개했을때 차이는 이런식
(a + b) * (y + z)
= ay + az + by + bz
출력 결과는 길어서 좀 그렇긴한데 결과도 잘 나오긴 함
Y = Variable('y')
Z = Variable('z')
A = Variable('a')
B = Variable('b')
Product(Sum(A, B), Sum(Y, Z))
Product(Sum(A, B), Sum(Y, Z)).expand()
f_expression = Product(
Sum(
Product(
Number(3),
Power(
Variable("x"),
Number(2)
)
),
Variable("x")
),
Apply(
Function("sin"),
Variable("x")
)
)
f_expression.expand()
미분, 적분 내용까지 가긴 너무 힘드니 대충 sympy로 마무리
Sympy
기호로 수학 푸는 파이썬 lib
지금까지 구현한거랑 거의 비슷
숫자 그대로 써도되고, 심볼 쓸수있고
아래 내용은 방정식에 대입, 미분, 적분 결과
from sympy import *
from sympy.core.core import *
Mul(Symbol('y'), Add(3, Symbol('x')))
y = Symbol('y')
x = Symbol('x')
print(y * (3+x))
print(y * (3+x).subs(x, 1)) # substitude
print((x**2).diff(x)) # derivative
print((3*x**2).integrate(x)) # integrate
'컴퓨터과학 > 응용수학' 카테고리의 다른 글
파이썬 수학 - 10. 발사체 궤적에서 보는 최적화 (0) | 2023.10.02 |
---|---|
파이썬 수학 - 9. 장 시뮬레이션 (0) | 2023.10.01 |
파이썬 수학 - 7. 오일러 방법으로 게임 개선 (0) | 2023.09.29 |
파이썬수학 - 6. 변화율기초 (0) | 2023.09.29 |
파이썬수학 - 5.게임과 연립일차식 풀기 (0) | 2023.09.25 |