问题
I've solved an initial value differential equation and plotted the Y values given by ODE45. From the plot I can vaguely tell where the root should be, but in the given task I need to find it with great accuracy.
My first guess was to adjust a polynom to my X and Y values, and then solve the polynom equation. But I used polyfit and had 69 know values which gave me a polynom of the 68grade which I couldn't solve. So, does anyone know how I could find a "root" to a set of given Y values without knowing the actual equation? It's written in the task that interpolation should be used!
Thanks in advance!
回答1:
Given a vector of Y values (ordered in the sense that the corresponding X values are steadily increasing), you may find easily near which X values the roots are located. The roots are either where a Y value is zero or between two consecutive Y values that change sign. The idea is illustrated in this code snippet:
X = -1:0.1:1;
Y = X.*X - 0.4;
root_exact_pos = find(Y==0);
root_approx_pos = find(diff(sign(Y))~=0);
The roots are in the X values, either in X(root_exact_pos(k)), or between X(root_approx_pos(k)) and X(root_approx_pos(k)+1), k going from 1 to the number of elements of the respective root position array.
From here on you may apply whatever interpolation you'd like to find a better approximation of the root (I would go with linear, between the 2 points).
回答2:
You said that you need to find the root "with great accuracy." The answer by @CST-Link above is not how to do this if you're solving the differential equation numerically with ODE45. In fact it's a bad idea. It requires outputting your solution points at high resolution and introduces error, which can be particularly bad if the equations you are integrating are conservative (i.e., you will effectively add or subtract energy from a solution where the total energy should always remain constant). You need to use the event detection (zero crossing) functionality of Matlab's ODE solvers. This will generally be able to find your root with an accuracy close to machine epsilon, eps. Whereas the technique given by @CST-Link will only give an accuracy on the order of ODE45's step-size, which can be very large (interpolation can be used to help improve this, but you're not going to get close to eps unless you use tiny step sizes).
Looking at Matlab's help for ODE45, the events functionality can seem confusing, so I'll try to give a simpler example using code based on ballode (help ballode and edit ballode for more info), an example that Matlab includes to demonstrate events.
function eventsdemo
options = odeset('Events',@efun); % specify name of events function
[t,y,te,ye,ie] = ode45(@f,[0 10],[0;20],options); % integrate
figure
plot(t,y(:,1),'b',te,ye(:,1),'r.')
function dydt = f(t,y)
dydt = [y(2);-9.8]; % differential equation for ballistic motion
function [value,isterminal,direction] = efun(t,y)
value = y(1);
isterminal = 1;
direction = -1;
The example is just simple ballistic motion of a ball in the vertical direction: the function f. The goal is to accurately detect the velocity of the ball at the time it crosses the ground plane, y(1) == 0 line in the negative direction. If you detect contact too soon the ball's velocity will be less than reality and energy will be lost; too late and energy will be injected. The value output of efun defines the equation that must equal zero for the crossing. This can be as simple or as complex as needed, can depend on all of the state variables and time, and you can detect multiple crossings by specifying a vector. In your case, it sounds like you are interested in roots along the x-axis, so I imagine that you might have an identical definition of value. If you only want the first root or if there's only one, then isterminal will stop the integration when it is found. Finally, if you don't know the slope/gradient of your function at the root, you can set direction to zero. Try out this event function with the above code (ie can be used to tell which of the three events is triggered in the te and ye outputs):
function [value,isterminal,direction] = efun(t,y)
value = [y(2) y(1)-10 y(1)];
isterminal = [0 0 1];
direction = [-1 0 -1];
Some minor caveats. You need to set the final integration time to be sufficiently long. In other words, the event must occur between t0 and tf (see ballode for a scheme to iterate calls to ODE45 if you don't know where in time your events will occur). If you set isterminal to zero, the output t vector and y matrix will be trimmed to end at the time of the terminal event. Lastly, if you specify fixed step-size outputs, e.g., TSPAN = t0:dt:tf, the last output point will likely have a smaller step-size.
来源:https://stackoverflow.com/questions/15987947/matlab-find-a-root-of-y-values-given-by-a-differential-equation