1. 程式人生 > >演算法 -- 猴子選大王的四種方法,並對其時間與記憶體消耗的分析和對比&PHP

演算法 -- 猴子選大王的四種方法,並對其時間與記憶體消耗的分析和對比&PHP

本篇利用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


歡迎補充!

千而の大獅子!