演算法 -- 猴子選大王的四種方法,並對其時間與記憶體消耗的分析和對比&PHP
阿新 • • 發佈:2018-11-08
本篇利用PHP對“猴子選大王”問題,給出了四種方法,並對其進行了時間消耗的分析與對比。
題目:n個猴子要選出一個大王,隨機給出一個數m,當猴子報數為m的時候,則被淘汰,剩餘的最後一個猴子即為大王。
一、演算法解釋及程式碼展示
方法一:圍圈報數
n 個猴子圍成一圈從 1 開始報數,報數等於 m 的猴子淘汰,然後下個猴子繼續從 1 報數。
方法二:變換佇列
n 個猴子持 1~n 的號碼排成一列,從持 1 號牌子的猴子開始遍歷,不是 m 的倍數號碼牌的猴子移動到佇列最後,號碼牌變為為最後一個猴子的號碼牌 +1,是m的倍數號碼牌的猴子淘汰。
方法三:遞迴
n 個猴子排隊報數,來個飼養員i來幫忙計數,計數 i 等於 m 的時候,當前報數的猴子淘汰,飼養員 i 再從 0 開始計數。
方法四:取餘數
這次又來了個飼養員 j,現在飼養員 i 和 j 覺定不淘汰猴子了,採用更科學的數學計演算法,猴子排隊,飼養員 i 計算每個猴子的餘數,飼養員 j 給猴子報數,計算完最後一個猴子,飼養員i取得餘數加 1 就是猴王。
下面是程式碼展示:
方法一:圍圈報數 getMonkeyKing1($n, $m)
function getMonkeyKing1($n, $m) { $i = 1; $arr = range(1, $n); while (count($arr) != 1) { foreach ($arr as $k => $v) { if ($i == $m) { unset($arr[$k]); $i = 1; } else { $i++; } } } return reset($arr); }
方法二:變換佇列getMonkeyKing2($n, $m)
function getMonkeyKing2($n, $m) { $arr = range(1, $n); $i = 0; while (count($arr) != 1) { if (($i + 1) % $m == 0) { unset($arr[$i]); } else { // 將一個數組元素放入最後 array_push($arr, $arr[$i]); unset($arr[$i]); } $i++; } return $arr[$i]; }
方法三:遞迴getMonkeyKing3($n, $m, $i = 0)
function getMonkeyKing3($n, $m, $i = 0)
{
if (!is_array($n)) {
$n = range(1, $n);
}
if (count($n) == 1) {
return reset($n);
}
foreach ($n as $k => $v) {
$i++;
if ($i % $m == 0) {
unset($n[$k]);
$i = 0;
}
}
return getMonkeyKing3($n, $m, $i);
}
方法四:取餘數getMonkeyKing4($n, $m)
function getMonkeyKing4($n, $m)
{
$i = 0;
for ($j = 2; $j <= $n; $j++) {
$i = ($i + $m) % $j;
}
return $i + 1;
}
二、消耗時間與記憶體對比分析
分別對四種演算法進行了時間消耗與記憶體消耗對比與分析,對四個演算法分別進行了10次試驗,試驗引數n取值為100000,m取值為3,取平均值如下表格:
序號 | 演算法 | 函式 | 時間消耗(毫秒) | 記憶體消耗(byte) |
① | 方法一:圍圈報數 | getMonkeyKing1(100000,3) | 81.500053405762 | 374712 |
② | 方法二:變換佇列 | getMonkeyKing2(100000,3) | 48.094320297241 | 374704 |
③ | 方法三:遞迴 | getMonkeyKing3(100000,3, $i = 0) | 79.710340499877 | 375192 |
④ | 方法四:取餘數 | getMonkeyKing4(100000,3) | 4.8909902572632 | 373720 |
結論:時間消耗:①>③>②>④,記憶體消耗:③>①>②>④,方法四是最優方案。
ps:其實在,方法①②③中,可以有優化的地方,比如,可以採用動態規劃的思想,把count($n)儲存起來,不用每次計算,可以省下很多時間,關於動態規劃的思想,可以看我的這篇文章:演算法 -- 求最長公共字串&PHP
歡迎補充!
千而の大獅子!