如何克服当前跳过循环内某个实数的数值算法的离散性?

How can I overcome discrete nature of a numerical algorithm that is currently skipping over a certain real number inside a loop?

我有一个相当复杂的算法来执行搜索,其中我使用 [0.25 到 1.75] 范围内的 $search 变量。

根据算法,当 $search 恰好为 1 时会发生 "interesting" 事情,因为它会遇到 有时 的变量配置(但不总是)最有利的。一些代码依赖于 $search 恰好为 1 来产生最有利的结果。

更具体地说,搜索范围内通常有一些特定值,这会产生最有利的结果,但我的算法布局方式通常会跳过该特定值。在这里,我举例说明了特定值(基于其他输入和配置)恰好是 1..

问题

从数学上讲,如果 $search 是连续的而不是离散的,我就不会有这个问题。我的问题是尝试使用离散数学收敛于最有利的变量配置。这里的问题是算法。需要注意的次要问题是浮点运算,但我认为这还不是这里的问题。

基本循环:

$maxPowerOut = 0 ; 
for ($increment = 0; $increment <= 500; $increment ++)
{
    //vars computed elsewhere, i.e:
    //MIN = 0.24651533;
    //STEP = 0.00196969
    $search = MIN + STEP * $increment;

    //compute several coefficients (returns an array)
    $coeff = $this->coefficient($search);  

    //design is a complex library function 
    list($a, $b) = $this->design($coeff); 

    $powerOut = $a * $b;

    //keep track of max power (and other params, not shown)
    if ($powerOut > $maxPowerOut)
        $maxPowerOut = $PowerOut;
}

//currently prints 899.993 instead of 900 as should be expected
print "Max Power is $maxPowerOut";

当然,$search 几乎永远不会是 1。它是这样的:

注意上面的循环是如何跳过 1 的。为了争论起见,我们假设最有利的位置发生在 1.003000。该值 (1.003000) 也将被跳过。

问题

我如何改进、重构、重新思考、重组、重写我的循环以避免此类问题?

浮点数的精度有限(它们很谨慎),预计会有偏差。

参见:http://php.net/manual/en/language.types.float.php

你可以试试arbitrary precision extension

当前方向

数字1.0似乎很重要,可能代表default。重新编写代码以将 1.0 作为 $search 的一部分包含在内,将其作为同一循环的一部分或作为单独的迭代注入。

一个简单的改进可能是使用迭代方法:

在您当前的循环中,您搜索区间 [0.25, 1.75] 中的 500 个值。假设您可以通过这种方式将最优值缩小到更小的区间 [0.995, 1.007]。然后再次将这个间隔分成 500 个值并重复你的循环。重复直到达到所需的精度。

从数学上讲,您想在函数 f: search -> power 的给定区间内找到最大值,该函数计算给定 search 参数的某些 power 值。请注意,您的函数 f 越平滑,这通常越容易。要了解 f 可能是什么样子,您可以根据您在循环中计算的值绘制函数。

如果你的函数表现良好并且是单峰的(只有一个 "hump"),那么例如一个简单的 golden section search 将是有效的。

这里有一个快速的 JavaScript 片段/伪代码,可以帮助您解决问题。基本上,如果您发现增量/斜率已从正切换为负,您的函数应该递归调用自身。

function findMax(low, high) {
    var maxOut = Number.MIN_VALUE;
    // Calculate a step based on the low and high
    // Using a power of 2 since the floating point numbers are represented by binary
    var step = Math.abs((high - low) / 128);
    // we'll be tracking the deltas of two test values
    var prevDelta;
    var delta;
    // loop and check two values at a time
    for(var i=low; i<=(high - step); i+=step) {
        // coef ...
        // design ...
        // for testing
        var out1 = Math.cos(i);
        var out2 = Math.cos(i + step);
        // update the max
        if(out1 > maxOut) maxOut = out1;
        if(out2 > maxOut) maxOut = out2;
        // calc delta
        delta = out2 - out1;
        if(prevDelta !== undefined) {
            // If one delta is going up and
            // another is going down...
            // Recursively call the function
            if(prevDelta > 0 && delta < 0) {
                var out3 = findMax(i - step, i + step);
                // update the max
                if(out3 > maxOut) maxOut = out3;
            }
        }
        prevDelta = delta;
    }
    return maxOut;
}
alert(findMax(-0.5, 0.5)); // returns 1

这是 JSFiddle http://jsfiddle.net/hw5f2o1s/

如果最大值位于初始低点和低点 + 步长之间,则上述方法将不起作用,因为递归是通过达到峰值然后从峰值下降来触发的。如果发生这种情况,您可能必须通过增加两个除法 (high - low).

的幂来减小 step 变量