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

时间:2015-09-01 15:34:20

标签: php algorithm discrete-mathematics

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

基于算法,​​当$search正好为1时会发生“有趣”的事情,因为它会触及有时(但并非总是)最有利的变量配置。一些代码依赖于$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。它是这样的:

  • 0.99569478115682
  • 0.99866447159913
  • 1.0016341620414
  • 1.0046038524837
  • 1.0075735429261
  • ...

注意在上面的循环中如何跳过1。为了论证,让我们说最有利的位置发生在1.003000。该值(1.003000)也将被跳过。

问题

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

4 个答案:

答案 0 :(得分:1)

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

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

在数学上,您希望在函数f: search -> power的给定间隔内找到计算给定power参数的某个search值的最大值。请注意,这通常更容易使您的函数f更顺畅。为了了解f的外观,您可以根据循环中计算的值绘制函数。

如果你的功能表现良好并且说是单峰(只有一个“驼峰”),那么例如一个简单的golden section search就会有效。

答案 1 :(得分:1)

这是一个快速的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/

如果最大值位于初始低位和低位+步之间,则上述方法不会起作用,因为递归是通过达到峰值然后从其下降来触发的。如果发生这种情况,您可能需要通过增加两个除以(高 - 低)的功率来使变量更小。

答案 2 :(得分:0)

浮点数的精度有限(它们是谨慎的),期望偏差。

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

您可以尝试arbitrary precision extension

答案 3 :(得分:0)

当前方向

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