[英]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*2
且x>=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.