本地最小功能陷入困境

时间:2017-07-09 05:45:25

标签: javascript bezier

摘要
我执行对局部最小值的二进制搜索的简单函数通常返回对应于搜索的恰好¼,½和¾的虚假值。为什么会这样?

/**
 * minX  : the smallest input value
 * maxX  : the largest input value
 * ƒ     : a function that returns a value `y` given an `x`
 * ε     : how close in `x` the bounds must be before returning
 * return: the `x` value that produces the smallest `y`
 */
function localMinimum(minX, maxX, ƒ, ε) {
    if (ε===undefined) ε=1e-15;
    let m=minX, n=maxX, k;
    while ((n-m)>ε) {
        k = (n+m)/2;
        if (ƒ(m)<ƒ(n)) n=k;
        else           m=k;
    }
    return k;
}

现场演示http://phrogz.net/svg/closest-point-on-bezier_broken.html

该演示可让您调整立方贝塞尔曲线,移动鼠标,并尝试在最靠近鼠标位置的曲线上找到一个点。在下面的屏幕截图中,您可以看到曲线(黑色),鼠标的位置(以灰色圆圈为中心),曲线上的位置(红点),从光标到光标的距离的强力图表曲线上的每个点(右下角)和 t 参数计算为局部最小值(红色文本)。

S-shaped curve stuck at 0.25 C-shaped curve stuck at 0.75 C-shaped curve stuck at 0.5

这些截图代表了最糟糕的情况。在它们中,鼠标位置非常接近线,“局部最小”函数返回的点不是局部最小值。如果您试验演示,您会发现在许多情况下,本地最小功能正在按预期工作。只是...不接近一些早期的二元搜索范围。

Local minimum correctly identified

守则指南
localMinimum()函数(第225行)由closestPoint()函数调用(第194行):

/**
 * out   : A vmath vector to modify with the point value
 * curve : Array of vectors as control points for a Bézier curve (any degree)
 * pt    : Object with .x/.y to find the point closest to
 * return: A parameter t representing the location of `out`
 */
function closestPoint(out, curve, pt, tmps) {
    let vec=vmath[ 'w' in curve[0] ? 'vec4' : 'z' in curve[0] ? 'vec3' : 'vec2' ];
    return localMinimum(0, 1,
      t => vec.squaredDistance(pt, bézierPoint(out, curve, t)));
}

bézierPoint()函数(第207行)使用De Casteljau算法计算 t 参数化的曲线上的点。此功能按预期工作,独立于问题的其余部分进行测试。

squaredDistance()函数是vmath库的一部分,并且为simple as you would imagine

我错过了什么?我的本地最小功能如何失败?

2 个答案:

答案 0 :(得分:1)

我遇到了同样的问题,使用一组不同的函数来找到曲线上的最近点。

渐进式搜索

function localMinimum(minX, maxX, ƒ, ε) {
    if (ε===undefined) ε=1e-15;
    let m=minX, n=maxX, k;
    while ((n-m)>ε) {
        k = (n+m)/2;
        if (ƒ(m)<ƒ(n)) n=k;
        else           m=k;
    }
    return k;
}

有错,因为它会在错误的本地关闭。从记忆中,它是曲线的导数(或二阶导数),即距离点的距离,该点将显示您得到错误结果的拐点。换句话说,在曲线的那一部分,它越来越靠近你的点,然后开始移开。搜索功能假设此拐点是您要查找的点。

解决方案是系统搜索(在某种分辨率下),然后逐步搜索最终结果。

答案 1 :(得分:0)

正如经常发生的那样,当你花时间写一个问题时,答案就变得很明显了。我的localMinimum()功能存在缺陷。

如果我们查看上面第三个例子的图表并从最大点画一条线,我们就会看到它位于右侧:

enter image description here

算法(如上所示)的编写方式,它会立即抛弃图形的右半部分并聚焦在左侧。从那时起,它就注定了。它一直向右移动左边界0.5,但永远不允许再向右移动。

这是localMinimum()函数的固定版本,使用了一些黑客攻击。给定任何边界,它会在中间找到点,然后向左和向右“略微”(由ε参数确定),并根据该值确定保留哪一半。

/**
 * minX  : the smallest input value
 * maxX  : the largest input value
 * ƒ     : a function that returns a value `y` given an `x`
 * ε     : how close in `x` the bounds must be before returning
 * return: the `x` value that produces the smallest `y`
 */
function localMinimum(minX, maxX, ƒ, ε) {
    if (ε===undefined) ε=1e-10;
    let m=minX, n=maxX, k;
    while ((n-m)>ε) {
        k = (n+m)/2;
        if (ƒ(k-ε)<ƒ(k+ε)) n=k;
        else               m=k;
    }
    return k;
}

我已将演示版的固定版本上传到:http://phrogz.net/svg/closest-point-on-bezier.html