В этом сообщении обновляется мой вопрос в Решить находит неправильное решение?

Учитывая "простую" функцию в x:

f = (x + 3/10)^(1/2) - (9*(x + 3/10)^5)/5 - (x + 3/10)^6 + (x - 1)/(2*(x + 3/10)^(1/2));

Найдите нули по звонку

solve(f,x)

Это дает 3 нуля:

 ans =

 0.42846617518653978966562924618638
 0.15249587894102346284238111155954
 0.12068186494007759990714181154349

Простой взгляд на сюжет показывает, что третий корень - ерунда:

Function with 2 zeros

У меня серьезная проблема, потому что мне нужно получить наименьший ноль из приведенного выше вектора. Вызов min (ans) возвращает неверный ноль. Что я могу сделать, чтобы обойти эту проблему?

2
tw-S 5 Июл 2014 в 00:09

2 ответа

Лучший ответ

Это неполиномиальное уравнение, и оно, вероятно, вернется к числовому решатель (несимвольный). Так что могут быть числовые ошибки, или числовой алгоритм может застрять и сообщить ложные решения, я не уверен ...

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

% define function
syms x real
syms f(x)
xx = x+3/10;
f(x) = sqrt(xx) - 9/5*xx^5 - xx^6 + (x - 1)/(2*sqrt(xx));
pretty(f)

% find roots
sol = solve(f==0, x, 'Real',true)

% filter bad solutions
err = subs(f, x, sol)
sol = sol(abs(err) < 1e-9);  % this test removes the 2nd solution

% plot
h = ezplot(f, [0.1 0.5]);
line(xlim(), [0 0], 'Color','r', 'LineStyle',':')
xlabel('x'), ylabel('f(x)')

% programmatically insert data tooltips
xd = get(h, 'XData'); yd = get(h ,'YData');
[~,idx] = min(abs(bsxfun(@minus, xd, double(sol))), [], 2);
dcm = datacursormode(gcf);
pos = [xd(idx) ; yd(idx)].';
for i=1:numel(idx)
    dtip = createDatatip(dcm, h);
    set(get(dtip,'DataCursor'), 'DataIndex',idx(i), 'TargetPoint',pos(i,:))
    set(dtip, 'Position',pos(i,:))
end

У нас остались только два желаемых решения (одно отклонено нашим тестом):

                   /      3 \5
                 9 | x + -- |
    /      3 \     \     10 /    /      3 \6         x - 1
sqrt| x + -- | - ------------- - | x + -- |  + ----------------
    \     10 /         5         \     10 /          /      3 \
                                               2 sqrt| x + -- |
                                                     \     10 /
sol =
 0.42846617518653978966562924618638
 0.12068186494007759990714181154349       % <== this one is dropped
 0.15249587894102346284238111155954

err(x) =
 -9.1835496157991211560057541970488e-41
   -0.058517436737550288309001512815475   % <==
  1.8367099231598242312011508394098e-40

function_roots


Я также попытался использовать числовые решатели MATLAB, которые смогли найти два решения при разумных отправных точках:

(См. Этот связанный с этим вопрос)

fcn = matlabFunction(f);  % convert symbolic f to a regular function handle
opts = optimset('Display','off', 'TolFun',1e-9, 'TolX',1e-6);

% FZERO
sol2(1) = fzero(fcn, 0.1, opts);
sol2(2) = fzero(fcn, 0.5, opts);
disp(sol2)    % [0.1525, 0.4285]

% FSOLVE
sol3(1) = fsolve(fcn, 0.0, opts);
sol3(2) = fsolve(fcn, 1.0, opts);
disp(sol3)    % [0.1525, 0.4285]

Для сравнения я попробовал решить уравнение прямо в MuPAD, а также в Mathematica.

Mathematica 9.0

mma

MuPAD (R2014a)

mupad

Конечно, мы всегда можем напрямую вызывать MuPAD изнутри MATLAB:

% f is the same symbolic function we've built above

>> sol = evalin(symengine, ['numeric::solve(' char(f) ' = 0, x, AllRealRoots)'])
sol =
[ 0.15249587894102346284238111155954, 0.42846617518653978966562924618638]

Вышеупомянутый вызов эквивалентен поиску во всем диапазоне x = -infinity .. infinity (что может быть медленным!). Мы должны помочь numeric::solve, указав конкретный диапазоны поиска, когда это возможно:

>> sol = feval(symengine, 'numeric::solve', f==0, 'x = 0 .. 1', 'AllRealRoots')
sol =
[ 0.15249587894102346284238111155954, 0.42846617518653978966562924618638]
8
Community 23 Май 2017 в 14:53
1
Спасибо за отличный ответ! Пробовал вызывать MuPAD напрямую - и да, работает! Тем не менее, кажется, что это довольно медленно.
 – 
tw-S
5 Июл 2014 в 11:43
@tw-S: вызов можно сделать намного быстрее, если указать диапазон поиска x = 0 .. 1. Смотрите мое редактирование
 – 
Amro
5 Июл 2014 в 19:00

Одним из способов обхода может быть min (ans), а затем проверка того, что f (min (ans)) == 0. В противном случае используйте следующее меньшее значение.

0
Daniel Crane 5 Июл 2014 в 01:37