簡體   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