Нахождение корня / нулей функции - PullRequest
0 голосов
/ 06 марта 2011

Я пытаюсь найти корень функции, используя метод деления пополам:

if f(a)*f(b) < 0 then a root exists, 
then you repeat with f(a)*f(c)<0 where c = (a+b)/2       

, но я не уверен, как исправить код, чтобы он работал правильно.Это мой код, но он не работает должным образом

from scipy import *
from numpy import *


def rootmethod(f, a, b, tol):


    x = a
    fa = sign(eval(f))

    x = b
    fb = sign(eval(f))

    c = a + b
    iterations = 0

    if fa == 0:
        return a
    if fb == 0:
        return b

    calls = 0         
    fx = 1

    while fx != 0:
        iterations = iterations + 1
        c *= 0.5
        x = a + c
        fc = sign(eval(f))
        calls = calls + 1

        if fc*fa >= 0:
            x = a
            fx = sign(eval(f))
        if fc == 0 or abs(sign(fc)) < eps:
            fx = sign(eval(f))
            return x, iterations, calls





print rootmethod("(x-1)**3 - 1", 1, 3, 10*e-15)

Новое редактирование .. но все еще не работает

   if fa*fb < 0:

        while fx != 0:
            iterations = iterations + 1
            c = (a + b)/2.0
            x =  c
            fc = sign(eval(f))
            calls = calls + 1

            if fc*fa >= 0:
                x = c
                fx = sign(eval(f))
            if fc == 0 or abs(sign(fc)) < tol:
                fx = sign(eval(f))
                return x, iterations, calls

Редактировать: Изменено c = (a + b) * 2 на c =(a + b) / 2 в описании метода.

Ответы [ 4 ]

0 голосов
/ 06 марта 2011

Обратите внимание, что в коде есть простой недостаток, вызванный ошибками округления

a=0.015707963267948963 
b=0.015707963267948967
c=(a+b)*.5 

c снова становится b (зацените!). Вы можете оказаться в бесконечном цикле в случае очень малого допуска, как 1e-16.

def FindRoot( fun, a, b, tol = 1e-16 ):
  a = float(a)
  b = float(b)
  assert(sign(fun(a)) != sign(fun(b)))  
  c = (a+b)/2
  while math.fabs(fun( c )) > tol:
    if a == c or b == c: 
      break
    if sign(fun(c)) == sign(fun(b)):
      b = c
    else:
      a = c
    c = (a+b)/2
  return c

Теперь вызывать eval снова и снова не очень эффективно. Вот что вы можете сделать вместо

expr = "(x-1.0)**3.0 - 1.0"
fn = eval( "lambda x: " + expr )
print FindRoot( fn, 1, 3 )

Или вы можете поместить определения eval и lambda в FindRoot.

Было ли это полезно?

    Reson
0 голосов
/ 06 марта 2011

Я полагаю, что ваш цикл должен быть примерно таким (в псевдокоде и не включать проверку):

before loop:
a is lower bound
b is upper bound
Establish that f(a) * f(b) is < 0

while True:
    c = (a+b)/2
    if f(c) is close enough to 0:
        return c
    if f(a) * f(c) > 0:
        a = c
    else
        b = c

Другими словами, если средняя точка не является ответом, то сделайте его одним из следующих:новые конечные точки в зависимости от знака.

0 голосов
/ 06 марта 2011

Честно говоря, ваш код был немного беспорядочным.Вот некоторые из них, которые работают.Прочитайте комментарии в цикле.(Кстати, решение для вашей данной функции 2, а не 3,75)

from scipy import *
from numpy import *


def rootmethod(f, a, b, tol):


  x = a
  fa = sign(eval(f))

  x = b
  fb = sign(eval(f))

  c = a + b
  iterations = 0

  if fa == 0:
    return a
  if fb == 0:
    return b

  calls = 0         
  fx = 1

  while 1:
    x = (a + b)/2
    fx = eval(f)

    if abs(fx) < tol:
      return x

    # Switch to new points.
    # We have to replace either a or b, whichever one will
    # provide us with a negative 
    old = b # backup variable
    b = (a + b)/2.0

    x = a
    fa = eval(f)

    x = b
    fb = eval(f)

    # If we replace a when we should have replaced b, replace a instead
    if fa*fb > 0:
      b = old
      a = (a + b)/2.0




print rootmethod("(x-1)**3 - 1", 1, 3, 0.01)
0 голосов
/ 06 марта 2011

Я думаю ваша одна проблема с:

    x = a + c

Поскольку c = (a + b)*.5, вам не нужно добавлять a здесь ...

Обновление

Вы, кажется, не проверяете, следует ли начинать fa * fb < 0, и я также не вижу, где вы сужаете свои границы: вам следует либо переназначить a или b до c в вашем цикле, а затем пересчитайте c.

Код Прошло довольно много времени с тех пор, как я последний раз играл с python, поэтому возьмите его с частичкойсоль ^ _ ^

x = a
fa = sign(eval(f))

x = b
fb = sign(eval(f))

iterations = 0

if fa == 0:
    return a
if fb == 0:
    return b

calls = 0         
fx = 1

while fa != fb:
    iterations += 1
    c = (a + b)/2.0
    x = c
    fc = eval(f)
    calls += 1

    if fc == 0 or abs(fc) < tol:
        #fx = fc not needed since we return and don't use fx
        return x, iterations, calls
    fc = sign(fc)
    if fc != fa:
        b = c
        fb = fc
    else
        a = c
        fa = fc
#error because no zero is expected to be found
...