我根据分数计算了一次计算。
例如,如果分数当前为140,则该类返回一个包含可能抛出的集合的数组:
[10] => Array
(
[0] => T18
[1] => T18
[2] => D16
)
[11] => Array
(
[0] => T18
[1] => T16
[2] => D19
)
[13] => Array
(
[0] => T17
[1] => T17
[2] => D19
)
[14] => Array
(
[0] => 50
[1] => 50
[2] => D20
但是计算这些东西的速度很慢。有什么方法可以优化这个课程吗?
<?php
/**
* PHP Dartgame calculating class
* @author Youri van den Bogert
*/
class Darts {
/**
* @var string
*/
public static $notation_triple = 'T';
/**
* @var string
*/
public static $notation_double = 'D';
/**
* @var int
*/
private static $maxCheckout = 170;
/**
* @var string
*/
private static $doubleBull = 'Bull';
/**
* @var string
*/
private static $singleBull = 'Single';
/**
* @var array
*/
private static $scoreSheet = array('1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '25', '50');
/**
* Get a total thrown score
* @param $score1
* @param $score2
* @param $score3
* @return array
*/
public static function getTotalScore ($score1, $score2, $score3) {
return array(
'dart1' => self::getScoreOfDart($score1),
'dart2' => self::getScoreOfDart($score2),
'dart3' => self::getScoreOfDart($score3),
'total' => self::getScoreOfDart($score1) + self::getScoreOfDart($score2) + self::getScoreOfDart($score3)
);
}
/**
* Get score of a single dart
* @param $score
* @return mixed
*/
public static function getScoreOfDart ($score) {
if (is_numeric($score)) {
return $score;
}
if ($score[0] == self::$notation_triple) {
$multiplier = 3;
} elseif ($score[0] == self::$notation_double) {
$multiplier = 2;
} else {
$multiplier = 1;
}
$correctScore = filter_var($score, FILTER_SANITIZE_NUMBER_INT);
return ($correctScore * $multiplier);
}
public static function getScoreSheet () {
return self::$scoreSheet;
}
public static function calculatePossibleCheckout ($currentScore) {
// We cant checkout higher then $maxCheckout
if ($currentScore > self::$maxCheckout || $currentScore == 1) {
return false;
}
// Return bull
if ($currentScore == 50) {
return array(
'dart1' => self::$doubleBull
);
}
if ($currentScore == self::$maxCheckout) {
return array(
'dart1' => self::$notation_triple . '20',
'dart2' => self::$notation_triple . 'T20',
'dart3' => 'Bull'
);
}
$lastScore = $currentScore;
$lastPossibleThrow = 0;
$checkOut = array();
// Can current score be checked out?
if (self::canScore($currentScore) == true) {
return array(
'dart1' => self::$notation_double . ($currentScore / 2)
);
// Current score can't be checked out - calculate what to throw
} else {
for ($x=60; $x >= 0; --$x) {
if ($x <= 20 || $x == 50 || $x == 25 || ($x % 3 == 0) || ($x <= 40 && ($x % 2 == 0))) {
for ($xx=60; $xx >= 0; --$xx) {
if ($x <= 20 || $x == 50 || $x == 25 || ($x % 3 == 0) || ($x <= 40 && ($x % 2 == 0))) {
for ($xxx=50; $xxx > 0; $xxx = $xxx - 2) {
if ($xxx == 48) {
$xxx = 40;
}
if (self::checkIfScoreExists($xxx) == true && self::checkIfScoreExists($xx) == true && self::checkIfScoreExists($x) == true && ($xxx + $xx + $x) == $currentScore) {
$score_1 = self::getCorrectDartName($xxx);
$score_2 = self::getCorrectDartName($xx);
$score_3 = self::getCorrectDartName($x, true);
if ($score_1[0] == 'D' || $score_2[0] == 'D' || $score_3[0] == 'D') {
$nextKey = (count($checkOut)+1);
if ($xxx != 0) $checkOut[$nextKey][] = $score_1;
if ($xx != 0) $checkOut[$nextKey][] = $score_2;
if ($x != 0) $checkOut[$nextKey][] = $score_3;
usort($checkOut[$nextKey], function($a, $b) {
if (is_int($a) || is_float($a)) {
if (is_int($b) || is_float($b)) {
return $a - $b;
}
else
return -1;
}
elseif (is_int($b) || is_float($b)) {
return 1;
}
else {
return strcmp($b, $a);
}
});
}
}
}
}
}
}
}
}
return array_unique($checkOut, SORT_REGULAR);
}
public static function getCorrectDartName ($total, $isLast = false) {
if ($total == 25 || $total == 50) {
return $total;
}
if ($total < 20 && $isLast == false) {
return $total;
}
if ($total %3 == 0) {
return self::$notation_triple . ($total/3);
} elseif ($total %2 == 0) {
return self::$notation_double . ($total/2);
}
return $total;
}
/**
* Check if score exists
* @param $score
* @return bool
*/
public static function checkIfScoreExists ($score) {
if ($score == 50 || $score == 25 || $score == 0) return true;
$possibleScores = array_merge(range(1,20));
foreach ($possibleScores as $posScore) {
if ($score == self::getScoreOfDart(self::$notation_double . $posScore) || $score == self::getScoreOfDart(self::$notation_triple . $posScore) || $score == $posScore) {
return true;
}
}
return false;
}
/**
* Check if a specific score can be thrown by one dart
* @param $score
* @return bool
*/
public static function canScore ($score) {
if ($score == 50) {
return true;
} elseif ($score < 40 || $score == 40) {
if ($score % 2 == 0) {
return true; // Score is even - so its possible to throw
} else {
return false;
}
}
return false;
}
}
答案 0 :(得分:7)
我使用了基本排列生成,它超快(0.06秒)。它当然可以优化,但我认为没有意义,因为它已经如此之快。
<?php
class DartUtils {
private static $possible_points;
public static function getPossibleThrowsForScore($score) {
// generate all possible single throws and their score
// I didn't want to write them all out
// but it's certainly an option (and faster)
self::$possible_points = array();
for ($i = 1; $i <= 20; $i += 1) {
self::$possible_points["S" . $i] = $i; // S = single
self::$possible_points["D" . $i] = $i * 2;
self::$possible_points["T" . $i] = $i * 3;
}
self::$possible_points["bull"] = 25;
self::$possible_points["double bull"] = 50;
// self::$possible_points["miss"] = 0;
$throws = self::findSatisfyingThrowsForScore($score, 3, array());
foreach ($throws as $i => $serialized_throw) {
$throws[$i] = unserialize($serialized_throw);
}
return $throws;
}
private static function findSatisfyingThrowsForScore($score, $num_throws, $base_notation) {
$possible_throws = array();
foreach (self::$possible_points as $notation => $value) {
if ($num_throws === 1) { // we've done all throws
if ($score - $value === 0) { // we satisfied the score
$throw = array_merge($base_notation, array($notation));
sort($throw);
$possible_throws[] = serialize($throw);
}
} else {
// so long as there are num_throws, recurse with all possible throws
$possible_throws = array_merge($possible_throws,
self::findSatisfyingThrowsForScore($score - $value, $num_throws - 1, array_merge($base_notation, array($notation))));
}
}
$possible_throws = array_unique($possible_throws);
sort($possible_throws);
return $possible_throws;
}
}
var_dump(DartUtils::getPossibleThrowsForScore(140));
输出结果为:
array(21) {
[0]=>
array(3) {
[0]=>
string(3) "D10"
[1]=>
string(3) "T20"
[2]=>
string(3) "T20"
}
[1]=>
array(3) {
[0]=>
string(3) "D13"
[1]=>
string(3) "T18"
[2]=>
string(3) "T20"
}
[2]=>
array(3) {
[0]=>
string(3) "D13"
[1]=>
string(3) "T19"
[2]=>
string(3) "T19"
}
[3]=>
array(3) {
[0]=>
string(3) "D15"
[1]=>
string(3) "T20"
[2]=>
string(11) "double bull"
}
[4]=>
array(3) {
[0]=>
string(3) "D16"
[1]=>
string(3) "T16"
[2]=>
string(3) "T20"
}
[5]=>
array(3) {
[0]=>
string(3) "D16"
[1]=>
string(3) "T17"
[2]=>
string(3) "T19"
}
[6]=>
array(3) {
[0]=>
string(3) "D16"
[1]=>
string(3) "T18"
[2]=>
string(3) "T18"
}
[7]=>
array(3) {
[0]=>
string(3) "D18"
[1]=>
string(3) "T18"
[2]=>
string(11) "double bull"
}
[8]=>
array(3) {
[0]=>
string(3) "D19"
[1]=>
string(3) "T14"
[2]=>
string(3) "T20"
}
[9]=>
array(3) {
[0]=>
string(3) "D19"
[1]=>
string(3) "T15"
[2]=>
string(3) "T19"
}
[10]=>
array(3) {
[0]=>
string(3) "D19"
[1]=>
string(3) "T16"
[2]=>
string(3) "T18"
}
[11]=>
array(3) {
[0]=>
string(3) "D19"
[1]=>
string(3) "T17"
[2]=>
string(3) "T17"
}
[12]=>
array(3) {
[0]=>
string(3) "D20"
[1]=>
string(11) "double bull"
[2]=>
string(11) "double bull"
}
[13]=>
array(3) {
[0]=>
string(3) "D20"
[1]=>
string(3) "D20"
[2]=>
string(3) "T20"
}
[14]=>
array(3) {
[0]=>
string(3) "S20"
[1]=>
string(3) "T20"
[2]=>
string(3) "T20"
}
[15]=>
array(3) {
[0]=>
string(3) "T10"
[1]=>
string(3) "T20"
[2]=>
string(11) "double bull"
}
[16]=>
array(3) {
[0]=>
string(3) "T11"
[1]=>
string(3) "T19"
[2]=>
string(11) "double bull"
}
[17]=>
array(3) {
[0]=>
string(3) "T12"
[1]=>
string(3) "T18"
[2]=>
string(11) "double bull"
}
[18]=>
array(3) {
[0]=>
string(3) "T13"
[1]=>
string(3) "T17"
[2]=>
string(11) "double bull"
}
[19]=>
array(3) {
[0]=>
string(3) "T14"
[1]=>
string(3) "T16"
[2]=>
string(11) "double bull"
}
[20]=>
array(3) {
[0]=>
string(3) "T15"
[1]=>
string(3) "T15"
[2]=>
string(11) "double bull"
}
}
我添加的投注是:1-20单,双或三,公牛和双公牛。如果有更多或特殊投掷,您可以添加它们。
sort + serialization是快速删除重复项的技巧。对于抛出验证目的,将重复项留在其中可能实际上是有益的。
您可以考虑将表示法接受为字符串,例如:S10D20BULL
或DBULLDBULLT20
。如果你为单身人士介绍S
,你永远不会有困惑。 D112T20
是不明确的,是D11S2T20
还是D1S12T20
?字符串更容易使用,因此您甚至可以获得性能。将字符串符号拆分回它的部分有点棘手但可行。
请注意,我没有为>170
或1
添加特殊检查,因为列表只是空的。这是您可以应用的优化。
您可以选择添加 miss
投掷,评分为0
。
我不太了解你的代码,它太复杂了。我认为你正在失去很多时间进行排序和表示法之间的转换。正如您在我的解决方案中所看到的,我快速构建结果集并同时进行分数计算和符号生成。
我还应该提到我对飞镖规则不太熟悉。我假设 bull 和双牛但是如果单牛和 bull 是准确的,请随时纠正我。
答案 1 :(得分:2)
但这让我感兴趣所以我制作了自己的课程版本来解决这个问题。正如你现在给出的现有问题看起来有点复杂。只是想让它变得简单,而且简单也应该很快。
我从一开始做了一些假设:
我用数组设置属性,该数组包含所有可能的射击和他们的分数。并制定方法返回给定分数的所有可能的抛出。
我为我的课程设置了一个测试,为你提供一些性能数字。我已经在循环中逐个运行所有得分值1到170,以获得每个可能的结帐结果。
以下是我机器上的结果:
平均每秒42.5分,平均每秒62506.5分结果。
因此它返回所有可能的结果,并且速度更快。希望这会有所帮助。如果没有别的可能会让你知道如何提高你的课程。
<?php
$darts = new Darts();
$result = $darts->possibleCheckout(60); //gives 3767 results
//var_dump($result);
echo count($result) . "\n";
/**
* PHP Dartgame calculating class
* @author Aleksandar Popovic
*/
class Darts
{
/**
* All possible shoots and score per each
* @var array
*/
private $shoot_score = array(
'miss' => 0,
'1' => 1,
'2' => 2,
'3' => 3,
'4' => 4,
'5' => 5,
'6' => 6,
'7' => 7,
'8' => 8,
'9' => 9,
'10' => 10,
'11' => 11,
'12' => 12,
'13' => 13,
'14' => 14,
'15' => 15,
'16' => 16,
'17' => 17,
'18' => 18,
'19' => 19,
'20' => 20,
'D1' => 2,
'D2' => 4,
'D3' => 6,
'D4' => 8,
'D5' => 10,
'D6' => 12,
'D7' => 14,
'D8' => 16,
'D9' => 18,
'D10' => 20,
'D11' => 22,
'D12' => 24,
'D13' => 26,
'D14' => 28,
'D15' => 30,
'D16' => 32,
'D17' => 34,
'D18' => 36,
'D19' => 38,
'D20' => 40,
'T1' => 3,
'T2' => 6,
'T3' => 9,
'T4' => 12,
'T5' => 15,
'T6' => 18,
'T7' => 21,
'T8' => 24,
'T9' => 27,
'T10' => 30,
'T11' => 33,
'T12' => 36,
'T13' => 39,
'T14' => 42,
'T15' => 45,
'T16' => 48,
'T17' => 51,
'T18' => 54,
'T19' => 57,
'T20' => 60,
'Signle-Bull' => 25,
'Double-Bull' => 50
);
/**
* Maximum score
* @var int
*/
private $max_score = 170; // 3 x T20 is max right?
/**
* Return all possible checkouts for given score
* @param int $current_score
* @return array
*/
public function possibleCheckout($current_score)
{
if ($current_score > $this->max_score || $current_score < 1)
{
return false;
}
$checkout = array();
foreach ($this->shoot_score as $shoot1 => $score1)
{
if ($score1 > $current_score)
{
continue;
}
foreach ($this->shoot_score as $shoot2 => $score2)
{
if ($score1 + $score2 > $current_score)
{
continue;
}
foreach ($this->shoot_score as $shoot3 => $score3)
{
if ($score1 + $score2 + $score3 == $current_score)
{
$checkout[] = array($shoot1, $shoot2, $shoot3);
continue;
}
}
}
}
return $checkout;
}
}
答案 2 :(得分:2)
如果您希望获得额外的性能提升,则必须保存数据库或文件中所有可能性的结果,只需读取值而不是每次都计算它们。
我会建议以下结构
Table (checkout)
sum (int) | dart1 (int) | dart2 (int) | dart3 (int)
60 | 20 | 20 | 31
指向下表的所有飞镖
Table (dart)
pk (int) | bez (varchar)
20 | 20
31 | D10
答案 3 :(得分:1)
不确定你的错误的性质是什么,因为我不熟悉飞镖的规则,但第129行肯定是错的:
if ($x <= 20 || $x == 50 || $x == 25 || ($x % 3 == 0) || ($x <= 40 && ($x % 2 == 0))) {
要么您想要测试$ xx,要么您不想重新测试已经引导您进入该行的条件。目前,只要$ xx循环,就会调用最内层的循环。这是一个性能杀手。当然,在嵌套循环中嵌套循环通常是无论如何。
另一种方法可能是生成一个查找表,其中包含所有可能得分的答案,如Arkanon所建议。
答案 4 :(得分:1)
这是我的尝试。
输出按字典顺序按升序个别分数排序。
3次移动速度非常快,但如果尝试的话多,那就会崩溃。
有可能(理论上)计算任意数量的移动的可能性,尽管你很快就会达到脚本最大执行时间:)。
最大可达成分为180分(3倍三倍20分)。你可以浪费大量的CPU尝试更高的分数。这将是一个空洞的结果。
class darts {
const DATAFILE = "darts-scores.txt"; // score names & values database
static $score_val; // values of scores
static $score_name; // names of scores
static $score_num; // # of score values
static $res; // search result
static $tries; // for statistics
// internal search
static private function moves ($score, $moves, $i=0, &$list = array())
{
self::$tries++; // update stats
// search from the current scores only
for ( ; $i != self::$score_num; $i++)
{
$val = self::$score_val[$i];
if ($val > $score) break; // no need to try these ones
if ($moves == 1) // unrolled the recursion to improve performances
{
if ($score == $val)
{
// found another combination
$list[$moves] = self::$score_name[$i];
self::$res[] = $list;
}
}
else // go down to seek the rest of the combination
{
$list[$moves] = self::$score_name[$i];
self::moves ($score - $val, $moves-1, $i, $list);
}
}
}
// public search function
static function moves_for_score ($score, $moves=3)
{
self::$res = array();
self::$tries=0;
self::moves ($score, $moves);
return self::$res;
}
// turn results into a string
static function flatten ($res)
{
return implode (", ",
array_map (
function ($e){ return "[".implode(':',$e)."]"; },
$res));
}
// initialize scores table
static function init ()
{
if (!file_exists (self::DATAFILE))
{
// you can change the names of the scores with these two lines
$scores = array (
"miss" =>0,
"bull"=>25,
"double bull"=>50);
$type = array (
1=>"S",
2=>"D",
3=>"T");
// generate all scores
for ($t = 1 ; $t <= 3 ; $t++)
for ($i = 1 ; $i <= 20 ; $i++)
{
$scores[$type[$t].$i] = $t * $i;
}
asort ($scores);
foreach ($scores as $name=>$val) $out[] = "$name:$val";
file_put_contents (self::DATAFILE, implode ("\n", $out));
}
// read score file
$in = preg_split ("/[:\n]/", file_get_contents (self::DATAFILE));
self::$score_num = count($in)/2;
for ($i = 0 ; $i != self::$score_num ; $i++)
{
self::$score_name[$i] = $in[$i*2];
self::$score_val [$i] = (int) $in[$i*2+1];
}
}
}
darts::init();
////////////////////////////////////////////////////
// basic usage
////////////////////////////////////////////////////
$score = 281;
$moves = 5;
$res = darts::moves_for_score ($score, $moves);
echo "Sequences for $score in $moves moves: "
.darts::flatten($res)
."<br>";
echo "<pre>".print_r($res, true)."</pre><br>";
////////////////////////////////////////////////////
// stress test
////////////////////////////////////////////////////
echo "<br>Computing all possible sequences from 0 to 181...";
$start = microtime(true);
$tries = 0;
$max = 0;
for ($i = 0 ; $i <=180 ; $i++)
{
$res = darts::moves_for_score ($i);
$flat = darts::flatten($res);
if (strlen ($flat) > $max)
{
$max = strlen($flat);
$res_max = $res;
$i_max = $i;
}
$tries += darts::$tries;
}
echo "done in ".(microtime(true)-$start)."s, $tries tries<br>";
echo "<br>Longest list of possibilities:<br>$i_max -> "
.darts::flatten ($res_max)."<br>";