Ответ 1
Как насчет SymPy? Их solver выглядит так, как вам нужно. Посмотрите их исходный код, если вы хотите сами построить библиотеку...
Скажем, у меня есть уравнение:
2x + 6 = 12
С алгеброй видно, что x = 3
. Как я могу сделать программу на Python, которая может решить для x
? Я новичок в программировании, и я смотрел на eval()
и exec()
, но я не могу понять, как заставить их делать то, что я хочу. Я не хочу использовать внешние библиотеки (например, SAGE), я хочу сделать это в простом Python.
Как насчет SymPy? Их solver выглядит так, как вам нужно. Посмотрите их исходный код, если вы хотите сами построить библиотеку...
Существует два способа подхода к этой проблеме: численно и символически.
Чтобы решить его численно, вы должны сначала закодировать его как функцию "runnable" - вставьте значение в значение, получите значение. Например,
def my_function(x):
return 2*x + 6
Весьма возможно проанализировать строку для автоматического создания такой функции; скажем, разберите 2x + 6
в список, [6, 2]
(где индекс списка соответствует мощности x - так 6 * x ^ 0 + 2 * x ^ 1). Тогда:
def makePoly(arr):
def fn(x):
return sum(c*x**p for p,c in enumerate(arr))
return fn
my_func = makePoly([6, 2])
my_func(3) # returns 12
Затем вам понадобится другая функция, которая повторно подключает значение x в вашу функцию, анализирует разницу между результатом и тем, что она хочет найти, и изменяет ее значение x (надеюсь) минимизирует разницу.
def dx(fn, x, delta=0.001):
return (fn(x+delta) - fn(x))/delta
def solve(fn, value, x=0.5, maxtries=1000, maxerr=0.00001):
for tries in xrange(maxtries):
err = fn(x) - value
if abs(err) < maxerr:
return x
slope = dx(fn, x)
x -= err/slope
raise ValueError('no solution found')
Здесь есть много потенциальных проблем - найти хорошее начальное значение x, считая, что функция фактически имеет решение (т.е. нет никаких однозначных ответов на x ^ 2 + 2 = 0), попав в пределы точность вычислений и т.д. Но в этом случае функция минимизации ошибок подходит и получается хороший результат:
solve(my_func, 16) # returns (x =) 5.000000000000496
Обратите внимание, что это решение не совсем, точно верно. Если вам нужно, чтобы это было идеально, или если вы хотите аналитически анализировать решения уравнений, вам нужно обратиться к более сложному зверю: символическому решателю.
Символьный решатель, такой как Mathematica или Maple, представляет собой экспертную систему с множеством встроенных правил ( "знаний" ) об алгебре, исчислении и т.д.; он "знает", что производная от sin is cos, что производная от kx ^ p равна kpx ^ (p-1) и т.д. Когда вы даете ему уравнение, он пытается найти путь, набор правил-приложений, откуда он (уравнение), туда, где вы хотите быть (простейшая форма уравнения, которое, мы надеемся, является решением).
Ваше примерное уравнение довольно простое; символическое решение может выглядеть так:
=> LHS([6, 2]) RHS([16])
# rule: pull all coefficients into LHS
LHS, RHS = [lh-rh for lh,rh in izip_longest(LHS, RHS, 0)], [0]
=> LHS([-10,2]) RHS([0])
# rule: solve first-degree poly
if RHS==[0] and len(LHS)==2:
LHS, RHS = [0,1], [-LHS[0]/LHS[1]]
=> LHS([0,1]) RHS([5])
и есть ваше решение: x = 5.
Я надеюсь, что это придает дух идеи; детали реализации (поиск хорошего, полного набора правил и принятие решения о применении каждого правила) могут легко потреблять много человеко-лет усилий.
Python может быть хорошим, но это не Бог...
Существует несколько различных способов решения уравнений. SymPy уже упоминался, если вы ищете аналитические решения.
Если вы счастливы просто иметь численное решение, у Numpy есть несколько подпрограмм, которые могут помочь. Если вас просто интересуют решения для полиномов, numpy.roots будет работать. В частности, для упомянутого вами случая:
>>> import numpy
>>> numpy.roots([2,-6])
array([3.0])
Для более сложных выражений посмотрите scipy.fsolve.
В любом случае вы не сможете избежать использования библиотеки.
Используйте другой инструмент. Что-то вроде Wolfram Alpha, Maple, R, Octave, Matlab или любой другой программный пакет алгебры.
Как новичок, вы, вероятно, не должны пытаться решить такую нетривиальную проблему.
Если вы хотите решить только ограниченную систему уравнений mx + c = y
для положительного целого m, c, y
, то это будет делать:
import re
def solve_linear_equation ( equ ):
"""
Given an input string of the format "3x+2=6", solves for x.
The format must be as shown - no whitespace, no decimal numbers,
no negative numbers.
"""
match = re.match(r"(\d+)x\+(\d+)=(\d+)", equ)
m, c, y = match.groups()
m, c, y = float(m), float(c), float(y) # Convert from strings to numbers
x = (y-c)/m
print ("x = %f" % x)
Некоторые тесты:
>>> solve_linear_equation("2x+4=12")
x = 4.000000
>>> solve_linear_equation("123x+456=789")
x = 2.707317
>>>
Если вы хотите распознавать и решать произвольные уравнения, например sin(x) + e^(i*pi*x) = 1
, тогда вам понадобится реализовать какой-то механизм символической математики, подобный maxima
, Mathematica
, MATLAB solve()
или Symbolic Toolbox и т.д. Как новичок, это выходит за рамки вашего ken.