【问题标题】:How can I optimize this 'lottery' function in PHP?如何在 PHP 中优化这个“彩票”功能?
【发布时间】:2012-07-26 18:57:46
【问题描述】:

之前我在 Matlab 中为这种彩票功能编写了一个代码,只是为了测试它是否可能。但是,我实际上在 PHP 中需要它,所以我刚刚重写了代码,它似乎确实可以工作,但由于它涉及大量循环,我想确保我尽可能高效地完成它。

代码的作用:

你可以调用函数$lotto -> type($users,$difficulty),它会返回两个数字。这是解释,$users 是在网站上注册的用户数,即可能会买票的人。 $difficulty 是一个介于 1 和 10 之间的数字,其中 5 是正常的,1 是容易的,10 是困难的。这里的难度意味着匹配彩票上的所有数字有多难。

那么函数返回的数字是多少?那将是$n$r$n 是彩票上的号码数量,$r 是您可以从彩票中选择的号码数量。例如,在英国,一张国家彩票有 49 个号码,如果您选择 6 个号码。即$n = 49$r = 6

函数如何计算这两个数字?在英国国家彩票中,有 13,983,816 种可能的彩票组合。如果我运行$lotto -> type(13983816,1),它将返回array(49,6)。基本上,它试图做到这一点,所以有注册用户的数量就有尽可能多的票组合。

tl;博士,这是代码:

<?php
class lotto {
    public function type($users,$difficulty){
        $current_r = $r = 2;
        $current_n = 0;
        $difficulty = ($difficulty + 5) / 10; // sliding scale from 1 - 10
        $last_tickets_sold = 200; // tickets sold in last lotto
        $last_users = 100; // how many users there were in the last lotto
        $last_factor = $last_tickets_sold / $last_users; // tickets per user
        $factor = $last_factor * $difficulty;
        $users *= $factor;
        while($r <= 10){
            $u = 0;
            $n = $r;
            while($u < $users && $n < 50){
                $u = $this -> nCr(++$n,$r);
            }
            if($r == 2){
                $current_n = $n;
            } elseif(abs($this -> nCr($n,$r) - $users) < abs($this -> nCr($current_n,$current_r) - $users)){
                // this is a better match so update current n and r
                $current_r = $r;
                $current_n = $n;
            }
            $r++;
        }
        return array($current_n,$current_r);
    }
    private function nCr($n,$r){
        return $this -> factorial($n) / (
            $this -> factorial($r) * $this -> factorial($n - $r)
        );
    }
    private function factorial($x){
        $f = $x;
        while(--$x){
            $f *= $x;
        }
        return $f;
    }
}
$lotto = new lotto;
print_r($lotto -> type(1000,5));
?>

【问题讨论】:

  • 一个小的优化是将阶乘的先前结果存储在一个数组中,这样您就不必每次都进行长时间的计算。这将映射输入 => 输出的映射。这应该会大大减少阶乘的处理时间。您还可以存储最后一个已知值,因为您有两个始终递增的数字。

标签: php optimization combinations


【解决方案1】:

我进行了快速扫描,发现了一些可以进一步优化的地方。

组合
你的算法是蛮力的,可以进一步优化

private function nCr($n,$r){
    return $this -> factorial($n) / (
        $this->factorial($r) * $this->factorial($n - $r)
    );
}

function nCr($n,$r) {
    $top = 1;
    $sub = 1;

    for($i = $r+1; $i <= $n; $i++)
        $top *= $i;

    $n -= $r;
    for($i = 2; $i <= $n; $i++)
        $sub *= $i;

    return $top / $sub;
}

组合计算过多
计算组合很昂贵。

$u = 0;
$n = $r;
while($u < $users && $n < 50){
    $u = $this -> nCr(++$n,$r);
}

$n = $r + 1;
$u = nCr($n, $r);

while ($u < $users && $n < 50) {
    $n++;
    $u *= $n;
    $u /= ($n - $r);
}

【讨论】:

  • 没有机会检查这些输出以与 OP 的代码一起使用,但这是该问题的唯一答案。
  • 可爱,刚刚对之前和之后进行了基准测试:100x 原始代码 ~= .159 秒 100x 新代码 ~= .0174 秒 显着的改进,非常感谢 :),我赞成你的评论。跨度>
【解决方案2】:

立即观察到,您可能会出现除以 0 的错误

$last_factor = $last_tickets_sold / $last_users;

可以通过在其周围放置一个简单的 if 语句来解决

$last_factor = ($last_users == 0) ? 0 : $last_tickets_sold / $last_users;

【讨论】:

  • 但是$last_users = 100;,一个静态定义的值。如果这会产生除以 0 的错误,那将是一个史诗般的 PHP 错误。
  • 同意,但我假设(是的,我知道,假设是错误的),这些值将针对从数据库中提取的动态值进行更改。但是,是的,鉴于当前的代码是不可能的。只是观察
  • 您的假设是正确的,它将从数据库中提取,但是有可能0个用户上次使用了彩票,所以感谢您的建议。
【解决方案3】:

无论对您的代码进行详细检查,您确定您的循环不需要继续或中断吗?

【讨论】:

  • 绝对不是,它们只达到 n = 50 和 r = 10,我需要尝试每种可能性以获得更好的匹配。
【解决方案4】:

您的算法中 factorial() 的范围是 [0,50],那么为什么不直接静态地预先计算呢?

private static $factorial=array(1);

private static genFactorial($max) {
    if( count( self::$factorial ) > $max ) return;
    foreach ( range(count(self::$factorial), $max) as $n ) {
        self::$factorial[$n] = $i*self::$factorial[$n-1];
    }
}

现在将self::genFactorial(50); 添加到__construct()type(),并将对$this -&gt; factorial($n) 的引用替换为self::$factorial[$n]

这只是一个快速的代码转储;甚至没有进行编译检查,因此请原谅任何拼写错误等,但这样做的作用是用数组元素获取替换函数调用(包括 while 循环)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多