【问题标题】:Decimal odds conversion timeout小数赔率转换超时
【发布时间】:2013-05-23 10:45:13
【问题描述】:

我正在尝试将运动赔率从小数转换为分数。我通过搜索找到了一个运行良好的 PHP 函数,但某些小数会导致问题,例如 2.1 会超出服务器:

    function dec2frac($dec) { 

        $decBase = --$dec; 

        $div = 1; 

        do { 

            $div++; 

            $dec = $decBase * $div; 

        } while (intval($dec) != $dec); 

        if ($dec % $div == 0) { 
            $dec = $dec / $div; 
            $div = $div / $div; 
        } 

        return $dec.'/'.$div; 

    } 

$decimal = 2.3;

echo $decimal.' --> '.dec2frac($decimal);

6 的小数赔率应该是 5/1。这被计算为 6-1=5 = 5/1

我发现 2.2 和 2.3 的十进制输入会使函数跳闸,但其他值似乎没问题。是什么导致了这个异常,有办法解决吗?

谢谢。

【问题讨论】:

  • 可能是while循环正在循环一个非常长的数字?
  • echo dec2frac(0.25); 给出-3/4,我非常怀疑这是正确的。
  • 5 --> 4/1 显然不止一个异常
  • 我应该补充一点,这是体育博彩赔率的转换。所以 5 -> 4/1 是正确的。 5-1 = 4 = 4-1 或 4/1
  • 您能否提供更多输入和预期输出? dec2frac(2.1) 的结果应该是什么?

标签: php algorithm decimal fractions


【解决方案1】:

这个问题由两个独立的步骤组成

  • 从十进制数创建分数
  • 投注赔率和分数之间的转换

让我们从后者开始:5 的投注赔率意味着,每投资 1 美元,如果您赢了,您将获得 5 美元。由于您投资了 1 美元,因此您的实际赢利仅为 4 美元。所以赔率是 4-1 或4/1

2.5 的类似投注赔率意味着,您每投资 1 美元,您赢取 1.5 美元,赔率为 1.5-1 或 3-2 或 3/2

这使我们得出结论,我们需要的是($odds-1)的分数

下一部分:分馏。我没有分析给定的算法,但写了一个非常糟糕(但易于阅读)的算法:

function dec2frac($val) {
     //first pump denominator up
     $tmp=strstr("$val",'.');
     if ($tmp) $tmp=strlen($tmp)-1;
     else $tmp=0;
     $n=$val;
     $d=1;
     for (;$tmp>0;$tmp--) {
       $n*=10;
       $d*=10;
     }
     $n=intval(round($n));
     $d=intval(round($d));

     //Now shorten the fraction

     //Find limit for pseudoprime search
     $min=$n; 
     if ($d<$n) $min=$d;
     $min=ceil($min/2);
     if (ceil($d/2)>$min) $min=ceil($d/2);
     if (ceil($n/2)>$min) $min=ceil($n/2);

     $pseudoprime=2;
     while ($pseudoprime<=$min) {
          //Shorten by current pseudoprime as long as possible
          while (true) {
               $nn=$n/$pseudoprime;
               if ($nn!=round($nn)) break;
               $dd=$d/$pseudoprime;
               if ($dd!=round($dd)) break;
               $n=intval($nn);
               $d=intval($dd);
          }
          //Move on to next pseudoprime
          $pseudoprime+=($pseudoprime==2)?1:2;
          if ($pseudoprime>3) 
            if (($pseudoprime/3)==floor($pseudoprime/3)) $pseudoprime+=2;
     }
     return "$n/$d";
}

经过测试,它适用于值 0.25、2.5、3.1、3.14、3.141、3.1415、3.14159 和 3.141592。

算法非常未经优化的性质是一个不太重要的限制,因为投注赔率往往没有很多小数位。

一起

function odds2fract($odds) {
    return dec2frac($odds-1);
}

从其他步骤导出,我们成功转换

5 --> 4/1
2.5 --> 3/2
2.1 --> 11/10
2.2 --> 6/5

编辑

原版在搜索限制计算中存在错误,导致部分分数(例如完全可缩短)未能缩短。更新版本解决了这个问题。

编辑 2

又是一个已修复的错误:未能在intval() 处理它们之前的第一步中获得的值round() 会在分数上给出错误的结果,这些分数在浮点方面的保真度非常差。通过应用缺少的round() 来修复

【讨论】:

  • 不错,也许你可以优化,所以odds2fract(1.5) 返回1/2 而不是5/10
  • 问题:2.3 也返回 6/5
  • 哇 - 谢谢你完美的工作。如果我担心它没有优化,这不是我自己能够做的事情!
  • @AlanA 我们说的是多少个十进制数字? 1.2 或 1.23 或 1.234 或更高?如果它只是 1.23,我一点也不在乎。
  • 只保留两位小数...我还看到 2.3 的问题,当它应该是 13/10 时也给出 6/5
【解决方案2】:

立即触发的第一件事是您在其中使用了“.1”。这表明,对我来说,我们正在处理浮点问题......检查你的代码......是的,对我来说看起来像浮点问题。这里的问题是您的数字以二进制形式存储,并且二进制使用大量十进制值没有方便的方法。当您开始使用简单的数学运算时,您并没有得到准确的结果,而是得到了快捷的估计。

Floating Point numbers in PHP

该链接应为您提供有关问题所在的详细说明、指向更多页面的链接(可为您提供更多信息)以及指向现有库的链接以修复它。

【讨论】:

  • (点头)这是处理浮点问题的方法之一。很高兴你能找到办法!
猜你喜欢
  • 2020-10-10
  • 1970-01-01
  • 2017-08-16
  • 2014-12-08
  • 1970-01-01
  • 1970-01-01
  • 2015-11-18
  • 1970-01-01
  • 2019-11-28
相关资源
最近更新 更多