Octave fplot abs 看起来很奇怪

Octave fplot abs looks very strange

f = @(x)(abs(x))
fplot(f, [-1, 1]

全新安装的 Octave,未编辑任何配置。它产生了下图,它看起来好像在 0 附近有一段时间是恒定的,看起来更像 \_/ 而不是 \/:

为什么它看起来与通常的绝对值接近 0 的图如此不同?如何解决?

文档说:“fplot 最适合连续函数。具有不连续性的函数不太可能很好地绘制。将来可能会删除此限制。”所以它可能是函数本身的一个bug/feature,只有开发人员才能修复。普通的 plot() 函数工作正常:

x = [-1 0 1];
y = abs(x);
plot(x, y);

奇怪的形状来自采样率,即在多少点评估函数。这是由 fplot 的参数 N 控制的 默认调用似乎不小心跳过了 x=0,并且使用 fplot(@abs, [-1, 1], N=5) 我得到了和你一样有趣的形状:

但是,尝试 N 的不同值可以产生正确的形状,例如尝试fplot(@abs, [-1, 1], N=6):

虽然一般来说我会建议使用更高的数字,比如 N=100

由于fplot是用Octave写的,所以相对容易阅读。可以使用 which 命令找到它的位置。在我的系统上,这给出了:

octave:1> which fplot
'fplot' is a function from the file /usr/share/octave/5.2.0/m/plot/draw/fplot.m

检查 fplot.m 表明要绘制的函数 f(x) 在给定限制之间的 n 个等距点处进行计算。确定n的算法从第192行开始,可以总结如下:

  1. n 最初选择为 8(除非用户另有指定)
  2. 使用 n/2 + 1 点的较粗网格构建参数向量: x0 = linspace (limits(1), limits(2), n/2 + 1)' (linspace 函数将接受一个非整数值作为点数,它向下舍入)
  3. 计算相应的值: y0 = f(x0)
  4. 使用 n 个点的网格构造参数向量: x = linspace (limits(1), limits(2), n)'
  5. 计算相应的值: y = f(x0)
  6. 构造一个向量,其值对应于 x 的成员,但使用函数 interp1() 通过线性插值从 x0 和 y0 计算得出: yi = interp1 (x0, y0, x, "linear")
  7. 使用以下公式计算错误度量: err = 0.5 * max (abs ((yi - y) ./ (yi + y + eps))(:)) 也就是说,err 与计算值和线性插值之间的最大差值成正比。
  8. 如果 err 大于 tol(2e-3,除非用户指定)然后输入 n = 2*(n-1) 并重复。否则 plot(x,y).

因为 abs(x) 本质上是一对直线,如果 x0 包含零,那么线性插值将始终与它们对应的计算值完全匹配,而 err 将恰好为零,所以上面算法将在第一次迭代结束时终止。如果 x 不包含零,那么 plot(x,y) 将在一组不包括函数 'cusp' 的点上调用,并且会出现奇怪的行为。

如果限制在零的任一侧等距且 floor(n/2 + 1) 为奇数,默认值 (limits = [-5, 5], n = 8).

可以通过选择 nlimits 的组合来避免出现以下情况之一: a) m = floor(n/2 + 1) 等距点的集合不包括零或 b) n 个等距点的集合确实包含零。

例如,在零和奇数 n 的任一侧等间距的极限将正确绘制。不过,这对 n=5 不起作用,因为奇怪的是,如果用户输入 n=5,fplot.m 会用 8 代替它(我不确定为什么这样做,我认为这可能是一个错误).所以 fplot(@abs, [-1, 1], 3)fplot(@abs, [-1, 1], 7) 会正确绘制,但 fplot(@abs, [-1, 1], 5) 不会。

(n/2 + 1) 是奇数,因此 x0 包含对称极限的零,仅对于每个第 2 个偶数 n。这就是它使用 n=6 正确绘制的原因,因为对于该值 n/2 + 1 = 4,所以 x0 不包含零。 n=10、14、18等也是如此。

选择稍微不对称的限制也可以,试试:fplot(@abs, [-1.1, 1.2])