6
\documentclass{standalone}
\usepackage{tikz}

\begin{document}
\begin{tikzpicture}
    \draw (-1,0) -- (5,0);
    \draw (0,-1) -- (0,3);
    \draw[red, thick, samples=100] plot[domain=0.35:4.2]
    (\x, {0.6*cos((4.5*(\x-4)+2.1) r)-1.2*sin((\x-4) r)+0.1*\x+0.2});
\end{tikzpicture}
\end{document}

enter image description here

2

3 Answers 3

10

Without digging too deep into the underlying code of TikZ, you could do the following (but note that the aim of this solution is rather to mark the maximum on the path and not to calculate its exact coordinate for which other software is better suited than TeX):

  1. Name the path whose maximum you are looking for using name path global and place it into a scope to define a local bounding box.
  2. Draw a path from the upper left to the upper right corner of this bounding box and name this path. (To find the minimum, draw the path from the lower left to the lower right corner.)
  3. Find the intersection of the two named paths (and get the coordinates).

(I only later found that Henri Menke also used this approach to come up with a nice alternative solution.)

Full example:

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{intersections, calc}

\begin{document}
\begin{tikzpicture}

    \draw (-1,0) -- (5,0);
    \draw (0,-1) -- (0,3);
    
    \begin{scope}[local bounding box=myplotbox]
        \draw[name path global=myplot, red, thick, samples=100] plot [domain=0.35:4.2]
            (\x, {0.6*cos((4.5*(\x-4)+2.1) r)-1.2*sin((\x-4) r)+0.1*\x+0.2});
    \end{scope}

    \path[name path=myplotmax] (myplotbox.north west) -- (myplotbox.north east);
    \fill[name intersections={of=myplot and myplotmax, by={mymax}}]
        let \p1=(mymax) in (mymax) circle (2pt) node[above] {\x1, \y1};
            
\end{tikzpicture}
\end{document}

enter image description here


Edit:

From this, you can create a custom macro that can simplify things. It automatically creates two coordinates plot X max and plot X min where you can later attach things to.

\documentclass[tikz, border=10pt]{standalone}
\usetikzlibrary{intersections, calc}

\NewDocumentCommand{\plotwithminmax}{ m O{} O{} m }{
    \begin{scope}[local bounding box=plotbox #1]
        \draw[name path global=plot #1, #2] plot [#3] #4;
    \end{scope}

    \path[name path=plot #1 maxline] 
        (plotbox #1.north west) -- (plotbox #1.north east);
    \path[name intersections={of={plot #1} and {plot #1 maxline},     
        by={plot #1 max}}];
        
    \path[name path=plot #1 minline] 
        (plotbox #1.south west) -- (plotbox #1.south east);
    \path[name intersections={of={plot #1} and {plot #1 minline},     
        by={plot #1 min}}];
}

\begin{document}
\begin{tikzpicture}

    \draw (-1,0) -- (5,0);
    \draw (0,-1) -- (0,3);

    \plotwithminmax{A}[thick, red, samples=100][domain=0.35:4.2]{
        (\x, {0.6*cos((4.5*(\x-4)+2.1) r)-1.2*sin((\x-4) r)+0.1*\x+0.2}) 
    }
    \fill let \p1=(plot A max) in 
        (plot A max) circle (2pt) node[above] {\x1, \y1};

\end{tikzpicture}

\begin{tikzpicture}

    \draw (-1,0) -- (5,0);
    \draw (0,-1) -- (0,3);

    \plotwithminmax{B}[thick, dotted, samples=100][domain=-0.35:3.2]{ 
        (\x, {0.6*cos((4.5*(\x-4)+2.1) r)-1.2*sin((\x-4) r)+0.1*\x+0.2}) 
    }
    \node[draw=blue, label={below:min}] at (plot B min) {};
    \node[draw=blue, label={above:max}] at (plot B max) {};

    \draw[orange] (plot B min) -| (plot B max); 
    
\end{tikzpicture}
\end{document}

enter image description here



Edit (maybe rather a separate answer)

You could also use Lua to compute the maxima and minima. As the other answers already gave nice approaches about how to to this computationally, I tought that adding this to the above answer could maybe improve it. Since Lua is part of LuaLaTeX, you can run the code below without the need to use any other additional programming languages.

This is, however, only a rough approach. A major drawback is that you would need to insert the plot function twice. Maybe someone wants to improve this approach in this regard.

\documentclass[border=10pt]{standalone}
\usepackage{tikz}
\usepackage{luacode}

\tikzset{
    max/.style={
        circle, 
        inner sep=2pt,
        fill=red
    },
    min/.style={
        circle, 
        inner sep=2pt,
        fill=blue
    }
}

\begin{document}

\begin{tikzpicture}
    \draw (-1,0) -- (5,0);
    \draw (0,-1) -- (0,3);
    \draw[red, thick, samples=100] plot[domain=0.35:4.2]
        (\x, {0.6*cos((4.5*(\x-4)+2.1) r)-1.2*sin((\x-4) r)+0.1*\x+0.2});

\begin{luacode}

function f(x) 
    return 0.6*math.cos(4.5*(x-4)+2.1)-1.2*math.sin(x-4)+0.1*x+0.2
end

a = 0
b = 4

xcoords = {}
for i = a, b, .01 do
    xcoords[#xcoords+1] = i
end

localmin = {}
localmax = {}

for i = 2, (#xcoords-2) do
    if (f(xcoords[i-1]) < f(xcoords[i]) and f(xcoords[i]) > f(xcoords[i+1])) then
        localmax[#localmax+1] = {xcoords[i], f(xcoords[i])}
    end
    if (f(xcoords[i-1]) > f(xcoords[i]) and f(xcoords[i]) < f(xcoords[i+1])) then
        localmin[#localmin+1] = {xcoords[i], f(xcoords[i])}
    end
end

for i = 1, #localmax do
    tex.print('\\node[max] at (' .. localmax[i][1] ..',' .. localmax[i][2] .. ') {};') 
end

for i = 1, #localmin do
    tex.print('\\node[min] at (' .. localmin[i][1] ..',' .. localmin[i][2] .. ') {};') 
end

\end{luacode}

\end{tikzpicture}

\end{document}

enter image description here

9
  • Thank you, but I have to do this many times for each point and in this case I prefer to find the coordinates from another program e.g. geogebra.
    – Fotis K
    Aug 10, 2022 at 20:59
  • +1 For me there is some compilation error - works with \fill ... circle[radius=2pt] ... Aug 10, 2022 at 21:02
  • @hpekristiansen Yes, I also just discovered this. Changed already. Thanks! Aug 10, 2022 at 21:11
  • 1
    @FotisK you'd better explain your needs more clearly. If you described what you're gonna do with x or y coordinate, the answer could be more satisfying.
    – antshar
    Aug 10, 2022 at 22:14
  • 1
    I need to find the coordinates of all local vertices and use them for nodes, lines... that will depend on these coordinates. @JasperHabicht's answer is very enlightening, but I think there is a very small deviation from the actual values, maybe not.
    – Fotis K
    Aug 11, 2022 at 0:59
7

Making use the high accuracy of Asymptote, I program for automatically finding all local maximal/minimal points on the graph of given function. The idea/algorithm is direct and simple: we divide the graph into small pieces by the array of points y[n], then compare y[k] > y[k-1] and y[k] > y[k+1], etc.. The local maximal points are dotted with red. The local minimal points are dotted with blue.

Notice that in this critical (in any sense) case, we can not use the built-in path routines mintimes, maxtimes, times.

enter image description here

// http://asymptote.ualberta.ca/
// Find all local maximal.minimal points
unitsize(1cm);
import graph;
real f(real x){return (.6*cos(4.5*(x-4)+2.1)-1.2*sin(x-4)+.1*x+.2);}

//real f(real x){return .5+sin(3x);}
real xmin=-2;
real xmax=10.2;
xaxis("$x$",xmin-.5,xmax+.5);
yaxis("$y$");
guide p=graph(f,xmin,xmax,Spline);
draw(p,darkgreen+.6pt); 
int n=200;
real h=(xmax-xmin)/n;
real[] x,y;
for (int k=0; k<n; ++k) {
  x.push(xmin+h*k);   
  y.push(f(xmin+h*k)); 
}
//write(x,y);

for (int k=2; k<n-2; ++k){
if ( (y[k] > y[k-1]) & (y[k] > y[k+1]) ) dot( (x[k],y[k]),red);
if ( (y[k] < y[k-1]) & (y[k] < y[k+1]) ) dot( (x[k],y[k]),blue);
}

shipout(bbox(5mm,invisible));

For f(x)=.5+sin(3x), we get

enter image description here

Even more, this procedure can be used to find all intersection point of the graph with the horizontal line through a given point A. In other words, more or less does the job for the following routines

enter image description here

enter image description here

// http://asymptote.ualberta.ca/
// Find all intersection points with horizontal line through given point A
unitsize(1cm);
import graph;
real f(real x){return .5+sin(3x);}
real xmin=-2;
real xmax=10.2;
xaxis("$x$",xmin-.5,xmax+.5);
yaxis("$y$");
guide p=graph(f,xmin,xmax,Spline);
draw(p,darkgreen+.6pt); 
pair A=(1,.6);
draw((xmin,A.y)--(xmax,A.y),blue);

int n=5000;
real h=(xmax-xmin)/n;
real[] x,y;
for (int k=0; k<n; ++k) {
  x.push(xmin+h*k);   
  y.push(f(xmin+h*k)); 
}

for (int k=2; k<n-2; ++k)
if ( (y[k]-A.y)*(y[k+1]-A.y)<0) 
  dot( ((x[k]+x[k])/2,A.y),red);

shipout(bbox(5mm,invisible));
5

There's a quick and easy approach using the sagetex package. It gives you access to a CAS, called Sage and the Python programming language. Copy/paste the code below into the Sage Cell Server.

f = 0.6*cos((4.5*(x-4)+2.1))-1.2*sin((x-4))+0.1*x+0.2
a = 0.35
b = 4.2
xcoords = [i for i in srange(a,b,.01)]
local_min = []
local_max = []
for i in range(1,len(xcoords)-2):
    if f(x=xcoords[i-1])<f(x=xcoords[i]) and    f(x=xcoords[i])>f(x=xcoords[i+1]):
        local_max+=[[xcoords[i],f(x=xcoords[i])]]
    if f(x=xcoords[i-1])>f(x=xcoords[i]) and f(x=xcoords[i])<f(x=xcoords[i+1]):
        local_min+=[[xcoords[i],f(x=xcoords[i])]]
    
if len(local_min)>0:
    for i in range(0,len(local_min)):
        print(local_min[i][0],local_min[i][1])
if len(local_max)>0:
    for i in range(0,len(local_max)):
        print(local_max[i][0],local_max[i][1])
    
print("minima at ",local_min)
print("maxima at ",local_max)

Now press Enter to get:

enter image description here The basic idea is that a local max is defined when the point on the left and right have a smaller function value. The code starts at .35 and goes to 4.2 in steps of .01. Whenever it encounters a local max (min) it adds it to the list. I've printed out the list of maxima (minima) at the end. These x values (y values) can be easily inserted into your document (via the \sage{} command) or used in your pictures as I've illustrated in the LaTeX code below:

\documentclass[border=2mm]{standalone}
\usepackage{tikz,sagetex}
\usepackage{pgfplots}
\pgfplotsset{compat=1.16}
\begin{document}
\begin{sagesilent}
f = 0.6*cos((4.5*(x-4)+2.1))-1.2*sin((x-4))+0.1*x+0.2
a = 0.35
b = 4.2
xcoords = [t for t in srange(a,b,.01)]
ycoords = [f(x=t).n(digits=6) for t in srange(a,b,.01)]
local_min = []
local_max = []
for i in range(1,len(xcoords)-2):
    if f(x=xcoords[i-1])<f(x=xcoords[i]) and f(x=xcoords[i])>f(x=xcoords[i+1]):
        local_max+=[[xcoords[i],f(x=xcoords[i])]]
    if f(x=xcoords[i-1])>f(x=xcoords[i]) and f(x=xcoords[i])<f(x=xcoords[i+1]):
        local_min+=[[xcoords[i],f(x=xcoords[i])]]

output = r"\begin{tikzpicture}"
output += r"\begin{axis}[xmin=%f,xmax=%f,ymin= %f,ymax=%f]"%(a,b,-1,3)
output += r"\addplot[thin, red, unbounded coords=jump] coordinates {"
for i in range(0,len(xcoords)-1):
    if (ycoords[i])<-1 or (ycoords[i])>3:
        output += r"(%f , inf) "%(xcoords[i])
    else:
        output += r"(%f , %f) "%(xcoords[i],ycoords[i])
output += r"};"

if len(local_min)>0:
    output+= r"\addplot[black,only marks,mark options={mark size=.75pt}] coordinates {"
    for i in range(0,len(local_min)):
        output += r"(%f , %f) "%(local_min[i][0],local_min[i][1])
    output += r"};"

if len(local_max)>0:
    output+=r"\addplot[green,only marks,mark options={mark size=.75pt}] coordinates {"
    for i in range(0,len(local_max)):
        output += r"(%f , %f) "%(local_max[i][0],local_max[i][1])
    output += r"};"
output += r"\end{axis}"
output += r"\end{tikzpicture}"
\end{sagesilent}
\sagestr{output}
\end{document}

The output from Cocalc is below: enter image description here

Note, in the code above

if len(local_min)>0:
        output+= r"\addplot[black,only marks,mark options={mark size=.75pt}] coordinates {"
        for i in range(0,len(local_min)):
            output += r"(%f , %f) "%(local_min[i][0],local_min[i][1])
        output += r"};"

which is saying if there is at least 1 local minimum then add the coordinate as a black dots to the picture. The local maxima are in green. In Python, the first item in a list is the 0th item. So local_min[0][0] and local_min[0][1] will give the x coordinate and y coordinate of the first local minimum.

Since Sage is not part of LaTeX you will need to install it on your computer or, even better, open a free Cocalc account. You can be up and running in 5 to 10 minutes. Just copy/paste the LaTeX code into a LaTeX document and press Build to compile. Changing the function will allow you to quickly generate new graphs with min (max) plotted.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .