Group Abstract Group Abstract

Message Boards Message Boards

0
|
361 Views
|
11 Replies
|
15 Total Likes
View groups...
Share
Share this post:

Why is it impossible to find the zeros of a function in an interval after defining it?

Posted 22 days ago

As shown in the code below, a function is defined, but why can't we find its zeros over an interval?

f[x_] := x Log[2 - x] /; 0 <= x < 1
f[x_] := (x - 2) Log[x] /; 1 <= x <= 2
f[x_] := -f[-x] /; x < 0
f[x_] := f[x - 2] /; x > 2
Plot[{f[x]}, {x, 0, 10}]

enter image description here

Why is it impossible to find all zeros of a function within a given interval, and how can this problem be resolved?

f[x_] := x Log[2 - x] /; 0 <= x < 1
f[x_] := (x - 2) Log[x] /; 1 <= x <= 2
f[x_] := -f[-x] /; x < 0
f[x_] := f[x - 2] /; x > 2
Plot[{f[x]}, {x, 0, 10}]
Reduce[{f[x] == 0, 0 <= x <= 2026}, x, Reals]

enter image description here

POSTED BY: Bill Blair
11 Replies

Many years ago, working on the VisualDSolve package, I needed a FindAllRoots function, and also a FindAllRoots2D for 2x2 systems. My algorithm for the latter is cool -- making use of the data that ContourPlot provides us. But it does have difficulty with tangential roots. In 1 dimension a subdivision process leads to an algorithm, but again, tangential roots are a problem. Note also that Interval Arithmetic can be used, and such is included in Mathematica. E.g., an interval arithmetic approach to Problem 4 of the SIAM 100-Digit Challenge (WRI had a team, and I was on a Macalester College team) found 1000 or more roots of a complicated f(x,y) in a rectangle (and proved that they were all found; details in The SIAM 100-Digit Challenge book). Such an approach would work in 1 dimension as well (tangencies aside), and provide a proof of completeness. "Interval root-finding" its a well-studied field.

POSTED BY: Stan Wagon

There is still the old RootSearch package by Ted Ersek that works fine for your original definition of the function

https://library.wolfram.com/infocenter/Demos/4482

You simply call

RootSearch[f[x] == 0, {x, 0, 10}]
POSTED BY: Gianluca Gorni

I think the following is a cute approach, but it's not very efficient:

Solve[0 == 
  DSolveValue[{y'[x] == 
     D[Piecewise[{{t[x] Log[2 - t[x]], 
         0 <= t[x] < 1}}, (t[x] - 2) Log[t[x]]], t[x]], y[0] == 0,
    t'[x] == 1, t[0] == 0,
    WhenEvent[Mod[x, 2] == 0, t[x] -> t[x] - 2]},
   y[x], {x, 0, 100},
   Method -> {"Events", "MaxEvents" -> 1020} (*not needed unless x goes past 2000*)
   ],
 x
 ]
(*
{{x -> 0}, {x -> 2}, {x -> 4}, {x -> 6}, {x -> 8}, {x -> 10},
 {x -> 12}, {x -> 14}, {x -> 16}, {x -> 18}, {x -> 20},
 {x -> 22}, {x -> 24}, {x -> 26}, {x -> 28}, {x -> 30},
 {x -> 32}, {x -> 34}, {x -> 36}, {x -> 38}, {x -> 40},
 {x -> 42}, {x -> 44}, {x -> 46}, {x -> 48}, {x -> 50},
 {x -> 52}, {x -> 54}, {x -> 56}, {x -> 58}, {x -> 60},
 {x -> 62}, {x -> 64}, {x -> 66}, {x -> 68}, {x -> 70},
 {x -> 72}, {x -> 74}, {x -> 76}, {x -> 78}, {x -> 80},
 {x -> 82}, {x -> 84}, {x -> 86}, {x -> 88}, {x -> 90},
 {x -> 92}, {x -> 94}, {x -> 96}, {x -> 98}, {x -> 100}}
*)

It takes almost 5 seconds. The full 2026 takes 86 seconds (Macbook Pro M4 Max).

For some reason, WRI often plays method option values close to the chest.

POSTED BY: Michael Rogers
Posted 19 days ago

Thank you so much for your thorough and detailed explanations. I never expected that calculating the number of solutions and their specific values, which can be clearly observed from the graph, would require such intricate coding in Mathematica. Thanks again for your reply.

POSTED BY: Bill Blair

If you think plotting is "easy," then here's a plot-based approach:

pl = Plot[f[x], {x, -0.01, 10.01}];
pts = First@Cases[pl, Line[p_] :> p, Infinity];
brackets = (* find intervals where plot crosses x axis *)
  Extract[#, {{1, 2}, {2, 1}}] & /@ 
   Partition[SplitBy[pts, Sign@*Last][[All, {1, -1}, 1]], 2, 1];
FindRoot[f[x], {x, ##}, Method -> "Brent", (* polishes estimates from plot *)
   AccuracyGoal -> 50] & @@@ brackets
(*
{{x -> 0.}, {x -> 1.}, {x -> 2.}, {x -> 3.}, {x -> 4.}, {x -> 
   5.}, {x -> 6.}, {x -> 7.}, {x -> 8.}, {x -> 9.}, {x -> 10.}}
*)
POSTED BY: Michael Rogers

Here's a possible workaround:

To echo Gianluca's point from a different perspective, Reduce[] and other symbolic solvers work with the expression and not the definitions of the symbols in the expression — that is, with f[x] as shown in Out[403] in the screenshot, not with the definitions in Downvalues[f]. Of course, they have built-in rules for transforming expressions involving standard built-in functions, but those transformations are not part of the definitions of the functions.

POSTED BY: Michael Rogers

These internal functions do something similar. I don't know where the following functions are used internally or why they don't seem available in some System` level solver:

ClearAll[f];
f[x_] := x Log[2 - x] /; 0 <= x < 1
f[x_] := (x - 2) Log[x] /; 1 <= x <= 2
f[x_] := -f[-x] /; x < 0
f[x_] := f[x - 2] /; x > 2

With[{a = 0, b = 10,
  opts = Options[Visualization`Core`Plot]},
 WithCleanup[
  SetOptions[Visualization`Core`Plot, PlotPoints -> 2 b, 
   MaxRecursion -> 1],
  System`TRootsDump`GuessesToRoots[
    f,
    System`TRootsDump`GuessRealRoots[f, {a, b}],
    {a, b}, All, MachinePrecision][[All, 1]],
  SetOptions[Visualization`Core`Plot, opts]
  ]
 ]
(*  {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}  *)

For b = 2026, it takes about 85 seconds.

POSTED BY: Michael Rogers
Posted 22 days ago

Writing the function in the following way seems to work:

f[x_] := With[{t = Mod[x, 2]}, If[t < 1, t Log[2 - t], (t - 2) Log[t]]]
Reduce[{f[x] == 0, 0 <= x <= 2026}]
(* (C[1] \[Element] Integers && 0 <= C[1] <= 1013 && 
   x == 2 C[1]) || (C[1] \[Element] Integers && 0 <= C[1] <= 1012 && 
   x == 1 + 2 C[1]) *)
POSTED BY: Jim Baldwin
Posted 19 days ago

Thank you very much. Rewriting the functional form via alternative methods allows its solutions to be found effortlessly.

POSTED BY: Bill Blair

It seems that your definitions by condition /; supports numerical calculation but not symbolic manipulation. For example,

PiecewiseExpand[f[x], 0 < x < 10]

does nothing. Curiously, f'[1] turns back unevaluated, while f'[1.] gives a numerical value. This is numerical and it works:

FindRoot[f[x] == 0, {x, 3.5}]
POSTED BY: Gianluca Gorni
Posted 19 days ago

Thank you very much; this gives an excellent account of why this issue arises.

POSTED BY: Bill Blair
Reply to this discussion
Community posts can be styled and formatted using the Markdown syntax.
Reply Preview
Attachments
Remove
or Discard