采用微元思想在TikZ中绘制曲线指定点的切线

TiKZ-PGF 2019-12-11 11:17  浏览 :908
近期需要绘制**牛顿迭代数值计算示意图**,考虑用TikZ实现。其关键问题是如何在函数指定点绘制切线,在[www.latexstudio.net](www.latexstudio.net "www.latexstudio.net")检索到[使用 TikZ 绘制曲线的切线](https://www.latexstudio.net/archives/1713.html "使用 TikZ 绘制曲线的切线")的方法,但不能完全符合要求。 考虑到曲线上指定点的切线可以用该点附近的线段微元表示,为此,构造两个水平差为1pt的两条垂线,然后分别求曲线与这两条垂线的交点,连接这两个交点就可以表示该点的切线。然后,对该线段适当进行延长,就可以得到需要的切线。 ## 求解两个路径的交点 对于给定的两个路径,可以使用TikZ为路径提供的`name intersections`参数求解这两个路径的交点。为后续使用方便,可以定义如下求解两个路径交点的命令: ```tex % 求两个路径交点 % #1 第1路径名称 % #2 第2路径名称 % #3 交点名称,同时定义了一个全局宏\\InterNb记录了交点总数 \\newcommand{\\intersec}[3]{% \\path[name intersections={of=#1 and #2, by=#3, sort by=#1,total=\\t}] \\pgfextra{\\xdef\\internb{\\t}}; } ``` 注意,该命令只能在TikZ相关环境和命令中使用。 ## 求解切线端点 基于微元思想,可以定义如下求解曲线上指定点切线两个端点的命令: ```tex % 求路径上指定点的切线的两个端点 % #1 路径名称 % #2 路径上指定点名称 % #3 端点1名称 % #4 端点2名称 % 原理,用#2点附件(1pt)的一段直线微元表示切线 \\newcommand{\\tanterms}[4]{ % 过#2点的垂线 \\path[name path = l](#2|-current bounding box.south) -- (#2|-current bounding box.north); % 将过#2点的垂线水平偏移1pt \\coordinate (rd) at ($(#2) + (1pt, 0)$);% 可以考虑使用sp为单位,精度更高 \\path[name path = r](rd|-current bounding box.south) -- (rd|-current bounding box.north); % 求路径#1与路径l的交战,并记为#3 \\intersec{#1}{l}{#3} % 求路径#1与路径r的交战,并记为#4 \\intersec{#1}{r}{#4} } ``` 如果需要提高精度,可以考虑使用sp作为两个垂线的偏移量单位。 同样,该命令只能在TikZ相关环境和命令中使用。 ## 牛顿迭代示意图绘制 对于一个指定曲线,通过3次迭代进行牛顿迭代求解方程根的示意图的完整代码为: ```tex \\documentclass[margin=3pt, convert, convert={ outext=.png, command=\\unexpanded{ pdftocairo -r 300 -png \\infile % 将生成的pdf文件转换为png图像 } } ]{standalone} \\usepackage{tikz} \\usetikzlibrary{intersections, calc} % 求两个路径交点 % #1 第1路径名称 % #2 第2路径名称 % #3 交点名称,同时定义了一个全局宏\\InterNb记录了交点总数 \\newcommand{\\intersec}[3]{% \\path[name intersections={of=#1 and #2, by=#3, sort by=#1,total=\\t}] \\pgfextra{\\xdef\\internb{\\t}}; } % 求路径上指定点的切线的两个端点 % #1 路径名称 % #2 路径上指定点名称 % #3 端点1名称 % #4 端点2名称 % 原理,用#2点附件(1pt)的一段直线微元表示切线 \\newcommand{\\tanterms}[4]{ % 过#2点的垂线 \\path[name path = l](#2|-current bounding box.south) -- (#2|-current bounding box.north); % 将过#2点的垂线水平偏移1pt \\coordinate (rd) at ($(#2) + (1pt, 0)$);% 可以考虑使用sp为单位,精度更高 \\path[name path = r](rd|-current bounding box.south) -- (rd|-current bounding box.north); % 求路径#1与路径l的交战,并记为#3 \\intersec{#1}{l}{#3} % 求路径#1与路径r的交战,并记为#4 \\intersec{#1}{r}{#4} } \\begin{document} \\begin{tikzpicture}[ domain=1.5:4.1, samples=101, ] % 绘制命名曲线 \\draw[blue,thick, name path=graph] plot (\\x,{\\x^3-7.7*\\x^2+19.2*\\x-15.5}) node[right] {$f(x)$}; % 绘制x轴和y轴 \\draw[->, name path=x] (0,0) -- (5,0) node[right] {$x$}; \\draw[->] (0,-0.5) -- (0,3.5) node[left] {$y$}; % 绘制根的真值点 \\fill[fill=black, name intersections={of=graph and x}] (intersection-3) circle[radius = 0.02cm] node[red, scale = 0.6, left, shift={(4pt, 3pt)}, font=\\tiny]{$x^*$}; % 绘制起始点 \\fill[fill = black] (4, 0) coordinate (xs) circle[radius = 0.02cm] node[scale = 0.5, shift={(2pt, 0)},below, font = \\tiny] {$x_{0}$}; % 通过循环绘制3次迭代结果 \\foreach \\X/\\clr in {1/red, 2/green, 3/violet} { % 调整x轴点标记y方向偏移 \\ifodd\\X \\xdef\\yoff{-3pt} \\else \\xdef\\yoff{0pt} \\fi % 过xs的垂线与曲线graph的交点 \\path[name path=v](xs|-current bounding box.south) -- (xs|-current bounding box.north); \\intersec{graph}{v}{fx} \\fill[fill = black] (fx) circle[radius = 0.02cm] node[scale = 0.6, left, shift={(2pt,3pt)}, font = \\tiny] {$f(x_{\\X})$}; % 求切线端点 \\tanterms{graph}{fx}{li}{ri} % 定义切线路径,对得到的切线微元进行延长 % 延长倍数和方向需要手动调整(需要改进) \\path[name path=t](ri)--($(ri)!12!(li)$); \\intersec{t}{x}{tx} \\fill[fill = black] (tx) circle[radius = 0.02cm] node[scale = 0.5, shift={(2pt, \\yoff)}, below, font = \\tiny] {$x_{\\X}$}; % 绘制切线和垂线 \\draw[\\clr, dashed] (fx)--(tx) (fx)--(xs); % 下一次迭代 \\coordinate (xs) at (tx); } \\end{tikzpicture} \\end{document} ``` 该代码的绘制结果:
Happy LaTeXing!
发布评论
登录后方可评论!点击登录
全部评论 (0)
暂无评论, 快来抢沙发!