繁体   English   中英

如何改善PHP代码以使其更高效?

[英]How can I improve my PHP code to make it more efficient?

我编写了一个简单的PHP脚本,该脚本检查随机值是否为有效的橄榄球联合得分。 它工作得很好,但效率不是特别高,因此欢迎提供任何改进建议。

$score = rand(0, 60);

/* Rugby Union
 * 
 * Try = 5 points
 * Conversion = 2 points
 * Penalty = 3 points
 * Drop goal = 3 points
 * 
 */

echo "<h1>Score: ".$score."</h1>";

for ($tries = 0; $tries <= 12; $tries++)
{
    for ($conversions = 0; $conversions <= 30; $conversions++)
    {
        for ($dropgoals = 0; $dropgoals <= 20; $dropgoals++)
        {
            if ($conversions > $tries)
            {
                //echo "<br />Illegal score";
            }
            else
            {
                $testscore = ($tries * 5) + ($conversions * 2) + ($dropgoals * 3);
                if ($testscore == $score)
                {
                    if ($dropgoals == 0)
                    {
                        echo "Found a way to achieve score with ".$tries." tries ".$conversions." conversions and ".$dropgoals." drop goals.<br />";
                    }
                    else
                    {
                        echo "Found a way to achieve score with ".$tries." tries ".$conversions." conversions and ".$dropgoals." drop goals or penalties.<br />";
                    }
                }
            }
        }
    }
}

好的,这是目前的修订解决方案,如果可能的话,减少嵌套的for循环数量会很不错。

echo "<h1>Score: ".$score."</h1>";

for ($tries = 0; $tries <= 12; $tries++) {
    for ($conversions = 0; $conversions <= $tries; $conversions++) {
        for ($dropgoals = 0; $dropgoals <= 20; $dropgoals++){
            if ($conversions <= $tries) {
                    $testscore = ($tries * 5) + ($conversions * 2) + ($dropgoals * 3);
                    if ($testscore == $score) {
                        echo "Found a way to achieve score with ".$tries." tries ".$conversions." conversions and ".$dropgoals.($dropgoals == 0 ? " drop goals.<br />" : " drop goals or penalties.<br />");
                    }
            }
        }
    }
}

好吧开始

for ($conversions = 0; $conversions <= 30; $conversions++)

可以更改为

for ($conversions = 0; $conversions <= $tries; $conversions++)

实际上,只有可能的分数是有限的,对吧? 快速的Google显示记录为164点。 因此,为什么不一次生成每个可能得分的列表,直到最大分数(300?500?),并在您的应用中对其进行硬编码。 然后,在运行时,只需检查提供的分数是否在列表中。 我认为,到目前为止,这将是最有效的解决方案。

编辑:如果您还想输出尝试次数,罚分次数和掉球目标-只需一次生成这些值,并将它们也保留在列表中(作为二维数组或关联数组)。

当您说“有效”时,请定义您的意思。

代码执行太慢吗? 现在它运行速度有多快,您需要它运行多快? 如果您不能定义“不够快”,那么您就没有目标。

在您猜出要加速什么之前,在这里所有的受访者都在鼓励您加快散布速度对您的伤害之前,您需要分析代码以了解大部分时间在哪里。

您的if else函数应该反转。 使最可能的情况出现在if(){}子句中,并在else {}中出现异常错误

if ($conversions < $tries)
                        {
                                $testscore = ($tries * 5) + ($conversions * 2) + ($dropgoals * 3);
                                if ($testscore == $score)
                                {
                                        if ($dropgoals == 0)
                                        {
                                                echo "Found a way to achieve score with ".$tries." tries ".$conversions." conversions and ".$dropgoals." drop goals.<br />";
                                        }
                                        else
                                        {
                                                echo "Found a way to achieve score with ".$tries." tries ".$conversions." conversions and ".$dropgoals." drop goals or penalties.<br />";
                                        }
                                }
                        }
                        else
                        {
                              // echo "illegal score";
                        }

这样可以清理一些东西。

echo "Found a way to achieve score with ".$tries." tries ".$conversions." conversions and ".$dropgoals.($dropgoals == 0 ? " drop goals.<br />" : " drop goals or penalties.<br />");

稍作修改的版本。 我希望我对橄榄球有所了解。

$score = rand(0, 60);

/* Rugby Union
 * 
 * Try = 5 points
 * Conversion = 2 points
 * Penalty = 3 points
 * Drop goal = 3 points
 * 
 */

echo "<h1>Score: ".$score."</h1>";

for ($tries = 0; $tries <= 12; $tries++){
    for ($conversions = 0; $conversions <= $tries; $conversions++){
        for ($dropgoals = 0; $dropgoals <= 20; $dropgoals++){
            else{
                $testscore = ($tries * 5) + ($conversions * 2) + ($dropgoals * 3);
                if ($testscore == $score){
                    if ($dropgoals == 0){
                        echo "Found a way to achieve score with ".$tries." tries ".$conversions." conversions and ".$dropgoals." drop goals.<br />";
                    }
                    else{
                        echo "Found a way to achieve score with ".$tries." tries ".$conversions." conversions and ".$dropgoals." drop goals or penalties.<br />";
                    }
                }
            }
        }
    }
    if ($conversions > $tries){
        echo "<br />Illegal score";
    }
}

找到解决方案后,您还可以跳出循环:

if ($testscore == $score) 
{
  echo "Found a way to achieve ....";
  break 3;
}

数指明循环的数量打破了,所以在写作时有3 for循环退出。

是的,对于这样的问题,3次循环似乎太多了。 但是,当您想找到x,y,z的组合,使得n = x*5 + y*3 + z*2x>=z ,我认为没有简单的解决方案。 但是,您可以减少迭代次数。
最好知道是要获得所有可能的组合还是只是想回答“是的,这是有效分数”。

无论如何,这是我的建议:

$score = rand(0, 60);

/* Rugby Union
 * 
 * Try = 5 points
 * Conversion = 2 points
 * Penalty = 3 points
 * Drop goal = 3 points
 * 
 */

// compute how often the points fit into the score
$maxTries = intval($score / 5);
$maxConversions = min(intval($score / 2),$maxTries);
$maxDrops = intval($score / 3);

$valid = false;
for ($tries = 0; $tries <= $maxTries; $tries++)
{   
    // this way, you avoid recomputing the value over and over again in the third loop 
    $scoreTries = $tries * 5;
    for ($conversions = 0; $conversions <= $maxConversions; $conversions++)
    {
        $scoreCons = $scoreTries  + $conversions * 2;
        for ($dropgoals = 0; $dropgoals <= $maxDrops; $dropgoals++)
        {
            $scoreTotal = $scoreCons + $dropgoals * 3
            if ($scoreTotal == $score)
            {
               echo 'Found a way to achieve score with '.$tries.' tries '.$conversions.' conversions and '.$dropgoals.' drop goals or penalties.<br />';
               $valid = true;
               // write 'break 3' here if you are satisfied with one answer                
            }
        }
    }
}

if (!$valid){
    echo "<br />Illegal score";
}

我不知道效率会提高多少,但是总的来说,如果使用“点”语法将字符串括在单引号(“字符串”)中总是很好的。 这样,PHP不会评估字符串以替换变量,我认为这是一种更干净的方法。

编辑:

哦,我不区分$dropgoals == 0 ,因为从逻辑上说,例如...and 0 drop goals.之间没有区别...and 0 drop goals. ...and 0 drop goals or penalties.

这与找出如何用给定面额的硬币组成零钱是一个同样的问题。 这是PHP的实现。 我怀疑它是否非常有效,但是比嵌套循环版本更通用。

<?php

f(
  30, // points scored in match
  array( // rugby union scoring structure
    "Converted Tries" => 7,
    "Unconverted Tries" => 5,
    "Drop Goals/Penalties" => 3,
  )
);

function f($points_left, $scoring_structure, $scores_so_far = array()){
  if($points_left==0){
    print_score($scores_so_far);
  }else if($points_left>0){
    if($scoring_structure){
      list($score_type, $points_for_score_type) =
        first_element($scoring_structure);

      // Option 1: Use a highest-denomination coin,
      // and make change for the rest.
      if($points_for_score_type <= $points_left){
        f(
          $points_left-$points_for_score_type,
          $scoring_structure,
          increment($scores_so_far,$score_type)
        );  
      }

      // Option 2: Attempt to make change for the full amount without
      // using the highest denomination coin at all.
      f(
        $points_left,
        all_except_first_element($scoring_structure),
        $scores_so_far
      );
    }
  }else{
    exit("Error: Should never reach here!\n");
  }
}

function increment($arr, $key){
 $arr[$key]++;
 return $arr;
}

function all_except_first_element($arr){
  list($k, $v) = first_element($arr);
  unset($arr[$k]);
  return $arr;
}

function first_element($arr){
  foreach($arr as $k=>$v){
    return array($k, $v);
  }
}

function print_score($scores_so_far){
  $parts = array();
  foreach($scores_so_far as $k=>$v){
    $parts[]= "$k: $v";
  }
  echo implode(", ", $parts), "\n";
}

您的方法可以进一步改进-而不是查看三个变量的所有组合-尝试,转换和dropgoals,您只能查看不超过$score那些组合。 尽管您仍然具有嵌套循环,但是循环内代码的执行次数减少了。 见下文。

echo "<h1>Score: ".$score."</h1>";

$triesScore = 5;
$conversionsScore = 2;
$dropgoalsScore = 3;

for ($tries = 0; $tries <= $score/$triesScore; $tries++) {
    for ($conversions = 0; $conversions <= ($score-$triesScore*$tries)/$conversionsScore; $conversions++) {
        for ($dropgoals = 0; $dropgoals <= ($score-$triesScore*$tries-$conversionsScore*$conversions)/$dropgoalsScore; $dropgoals++){
            $testscore = ($tries * $triesScore) + ($conversions * $conversionsScore) + ($dropgoals * $dropgoalsScore);
            if ($testscore == $score) {
                echo "Found a way to achieve score with ".$tries." tries ".$conversions." conversions and ".$dropgoals.($dropgoals == 0 ? " drop goals.<br />" : " drop goals or penalties.<br />");
            }
        }
    }
}

尽管实际上,最大得分为60,但改进很小,因此可能不会引起注意。

预计算! 以为我怀疑性能会杀死您的应用程序,但以防万一。 共有1911种可能的组合和141个有效分数,因此您可以轻松地预先计算数据,并将其存储在磁盘上并在需要时加载它。

预计算:

$scores = array();
for ($tries = 0; $tries <= 12; $tries++) {
    for ($conversions = 0; $conversions <= $tries; $conversions++) {
        for ($dropgoals = 0; $dropgoals <= 20; $dropgoals++){
            $score = ($tries * 5) + ($conversions * 2) + ($dropgoals * 3);
            if( !array_key_exists($score,$scores) ) $scores[$score] = array();
            $scores[$score][] = array( $tries, $conversions, $dropgoals );
        }
    }
}

echo "number of unique possible scores is " . count($scores) . "\n";
$number_combinations = 0;
foreach( $scores as $score => $combinations ) {
    echo "Score " . $score . " has " . count($combinations) . " combinations\n";
    $number_combinations += count($combinations);
}
echo "number of unique combinations is " . $number_combinations . "\n";

// store
file_put_contents("scores.txt",serialize($scores));

抬头:

$scores=unserialize(file_get_contents("scores.txt"))
$number_of_combinations_for_score_23 = array_key_exists(23,$scores) ? count($scores[23]) : 0;

您甚至可以减少分数数组,使其在右侧仅包含“有效或无效”布尔值。 这样可以节省一些查找时间和空间。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM