八大算法思想

目录

 

1.穷举法(枚举法) 【鸡兔同笼问题】

2.递推算法思想【兔子产仔问题】

2.1关系分析

3.递归算法思想【阶乘】

4.分治算法思想【寻找假币问题】

5.概率算法思想【蒙特卡罗PI概率算法问题】

6.回溯法【八皇后问题】

7.贪心算法


1.穷举法(枚举法) 【鸡兔同笼问题】

对于已知范围结果进行一一列举进行验证

 

《孙子算经》今有鸡兔同笼,上有三十五头,下有九十四足,问鸡兔各几何

 /*
 * 获取兔子数量
 * @param int $header 头
 * @param int $foot 脚
 * @return int
 */
function chicken_num(int $header, int $foot)
{
    for ($i = 0; $i < $header; $i++) {
        if (($i * 4 + ($header - $i) * 2) == $foot) {
            return $i;
        }
    }
    return 0;
}

2.递推算法思想【兔子产仔问题】

根据已知结果和关系,求解中间结果

 

斐波那契《算盘书》

如果一对两个月大的兔子以后每一个月都可以生一对小兔子, 而一对新生的兔子出生两个月后才可以生小兔子。 *也就是说, 1 月份出生,3 月份才可产仔。那么假定一年内没有产生兔子死亡事件,那么1年后共有多少对兔子呢?

2.1关系分析

忽略死亡

第一个月出生1对,即f(1) = 1;

第二个月出生1对,即f(2) = 1;

第三个月出生2对,即f(3) = 2;

第四个月出生3对,即f(4) = 3;

第五个月出生5对,即f(5) = 5;

……(斐波那契数列)

第N个月出生f(n) = f(n-1)+ f(n-2)

/*
 * @param int $n 月份
 * @return int
 */
function fibonacci(int $n)
{
    if ($n == 1 || $n == 2) {
        return 1;
    } else {
        return fibonacci(($n - 1)) + fibonacci(($n - 2));
    }
}

3.递归算法思想【阶乘】

递归算法就是在程序中不断反复调用自身来达到求解问题的方法。这里的重点是调用自身,这就要求待求解的问题能够分解为相同问题的一个子问题。这样 ,通过多次递归调用,便可以完成求解。

/*
 * 阶乘
 * @param int $n
 */
function fact(int $n)
{
    if ($n <= 1) {
        return 1;
    }
    return $n * fact(($n - 1));
}

4.分治算法思想【寻找假币问题】

分治算法的基本思想是将一个计算复杂的问题分为规模较小,计算简单的小问题求解,然后综合各个小问题,得到最终问题的答案。

一个袋子里有30个硬币,其中一枚是假币,并且假币和真币一模- 样,肉眼很难分辨,目前只知道假币比真币重量轻一点。  请问如何区分出假币?

/**
* @param $list 假币数组
* @return int 
*/
function isFalse(array $list)
{
    if (count($list) <= 3) {
        $keys = array_keys($list);

        switch (count($list)) {
            case 1:
                return $keys[0];
            case 2:
                return $list[$keys[0]] > $list[$keys[1]] ? $keys[1] : $keys[0];
            case 3:
                if (($list[$keys[0]] + $list[$keys[1]]) / 2 > $list[$keys[2]]) {
                    return $keys[2];
                } else {
                    $list[$keys[0]] > $list[$keys[1]] ? $keys[1] : $keys[0];
                }
        }
    }

    $qian = intval(count($list) / 2) + 1;

    $list_new = array_chunk($list, $qian, true);


    if (array_sum($list_new[0]) / count($list_new[0]) > array_sum($list_new[1]) / count($list_new[1])) {
        return isFalse($list_new[1]);
    } else {
        return isFalse($list_new[0]);
    }
}
$list = array_fill(0,30,2);

$list[10] = 1;
echo  isFalse($list);

 

5.概率算法思想【蒙特卡罗PI概率算法问题】

概率算法依照概率统计的思路来求解问题,往往不能得到问题的精确解,但却在数值计算领域得到了广泛的应用。 因为很多数学问题,往往没有或者很难计算解析解,这时便需要通过数值计算来求解近似值。

在边长为1的正方形内,以1为半径画一个1/4圆。落入院内的概率为PI/4?

 

 

/**
* @param $n 落叶次数
* @return int 
*/
function getPi(int $n)
{
    $p = 0;
    for ($i = 0; $i < $n; $i++) {
        $x = lcg_value();
        $y = lcg_value();

        if (($x * $x + $y * $y) <= 1) {
            $p++;
        }
    }
   return ($p / $n * 4);
}

6.回溯法【八皇后问题】

所谓回溯法,名字高大上,思想很朴素。

 设想把你放在一个迷宫里,想要走出迷宫,最直接的办法是什么呢?  没错,试。先选一条路走起,走不通就往回退尝试别的路,走不通继续往回退,直到找到出口或所有路都试过走不出去为止。

/**
*
*/
class Queen
{

   /**
     * 记录当前列是否存在皇后
     * @var array
     */
    public $column = [];
    /**
     * 记录/  (45°线)是否存在皇后
     * @var array
     */
    public $rup = [];
    /**
     * 记录\  (135°线)是否存在皇后
     * @var array
     */
    public $lup = [];
    /**
     * 记录皇后方案
     * @var array
     */
    public $num = [];

    public static $f = 0;//方案序号

    /**
     * 输出八皇后方案
     * @param int|null $n
     */
    public static function board(?string $n)
    {
        self::$f ++;
        echo "<h5>方案".self::$f.":{$n}</h5>";
        echo "<style>
        td{
        width: 50px;
        height: 50px;
        color: beige;
        
        }
        .b{
            background:black;
        }
        .g{
            background:green;
        }
        </style>";
        echo "<table>";
        for ($i = 0; $i < 8; $i++) {
            $cop = intval($n[$i]);
            echo "<tr>";
            for ($j = 1; $j <= 8; $j++) {
                $con = $cop == $j ? 'W' : '';

                echo ($i + $j) % 2 == 1 ? "<td class='b' align='center'>{$con}</td>" : "<td class='g' align='center'>{$con}</td>";
                $con = '';
            }
            echo "</tr>";
        }
        echo "</table>";
    }

    /**
     * 执行运算方案
     * @param int $num
     */
    public function main(int $num = 0)
    {

        if($num >= 8){

            self::board(implode('',$this->num));
        }
        for ($i = 1; $i <= 8; $i++) {

            if ((empty($this->column[$i]) || $this->column[$i] == false)
                && (empty($this->rup[$i + $num]) || $this->rup[$i + $num] == false)
                && (empty($this->lup[$i - $num]) || $this->lup[$i - $num] == false)) {

                $this->num[$num] = $i;
                $this->column[$i] = $this->rup[$i + $num] = $this->lup[$i - $num] = true;
                $this->main(($num + 1));
                $this->column[$i] = $this->rup[$i + $num] = $this->lup[$i - $num] = false;

            }

        }
    }


}

(new Queen())->main();

7.动态规划【最长公共子序列】

参考 https://blog.csdn.net/hrn1216/article/details/51534607
动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。
与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的。
若用分治法来解这类问题,则分解得到的子问题数目太多,有些子问题被重复计算了很多次

S1 = [1,3,4,5,6,7,7,8];
S2 = [3,5,7,4,8,6,7,8,2];
求最长公共子序列?

7.1分析

具体请参考 https://blog.csdn.net/hrn1216/article/details/51534607

 用i表示S1的个数对应Xi
 用j表示S2的个数对应Yj
 用C(i,j)表示结果 则得:
 (1)i = j = 0;结果长度为0
 (2)Xi = Yj =C(i-1,j-1)
 (3)Xi != Yj  max(C(i-1,j),C(i,j-1))

7.2向上查找

    下标j

下标i
0123456789
S2357486782
0S10000000000
110000000000
230111111111
340111222222
450122222222
560122223333
670123333444
770123333444
880123344455

7.3向左查找

    下标j

下标i
0123456789
S2357486782
0S10000000000
110000000000
230111111111
340111222222
450122222222
560122223333
670123333444
770123333444
880123344455

 

class Dp
{
    private $c = array();
    private $s1;
    private $s2;


    public function outPrint(array $s1, array $s2)
    {
        $this->c = [];
        $this->s1 = $s1;
        $this->s2 = $s2;

        for ($i = 0; $i < count($s1); $i++) {
            for ($j = 0; $j < count($s2); $j++) {
                $this->c[$i][$j] = $this->dp_f($i, $j);
            }
        }
        $lastI = $i - 1;
        $lastJ = $j - 1;
        $last = $this->c[$lastI][$lastJ];

        //向上找
        $upList = $this->getUp($last, $lastI, $lastJ);
        //优先向左
        $leftList = $this->getLeft($last, $lastI, $lastJ);

        return array(implode(',', $upList), implode(',', $leftList));
    }

    /**
     * 动态规划
     * @param int $i
     * @param int $j
     * @return int|mixed
     */
    private function dp_f(int $i, int $j)
    {
        if (isset($this->c[$i]) && isset($this->c[$i][$j])) {
            return $this->c[$i][$j];
        }
        if ($i == -1 || $j == -1) {
            return 0;
        }

        if ($this->s1[$i] == $this->s2[$j]) {
            return ($this->dp_f(($i - 1), ($j - 1)) + 1);
        }
        if ($this->s1[$i] != $this->s2[$j]) {
            return max($this->dp_f(($i - 1), $j), $this->dp_f($i, ($j - 1)));
        }

        return 0;
    }

    /**
     * 向上查找
     * @param int $lastUp
     * @param int $upI
     * @param int $upJ
     * @return array
     */
    private function getUp(int $lastUp, int $upI, int $upJ)
    {
        $upList = [];

        while ($lastUp > 0 && $upI > -1 && $upJ > -1) {
            if ($this->s1[$upI] == $this->s2[$upJ]) {
                array_unshift($upList, $this->s1[$upI]);
                $upI--;
                $upJ--;
                $lastUp--;
            } else {
                //优先向上
                if (!empty($this->c[($upI - 1)]) && $lastUp == $this->c[($upI - 1)][$upJ]) {
                    $upI--;
                } else {
                    $upJ--;
                }
            }
        }
        return $upList;
    }

    /**
     * 向上查找
     * @param int $lastUp
     * @param int $upI
     * @param int $upJ
     * @return array
     */
    private function getLeft(int $lastLeft, int $liftI, int $liftJ)
    {
        $leftList = [];
        while ($lastLeft > 0 && $liftI > -1 && $liftJ > -1) {
            if ($this->s1[$liftI] == $this->s2[$liftJ]) {
                array_unshift($leftList, $this->s1[$liftI]);
                $liftI--;
                $liftJ--;
                $lastLeft--;
            } else {
                //优先向左
                if (!empty($this->c[$liftI][($liftJ - 1)]) && $lastLeft == $this->c[$liftI][($liftJ - 1)]) {
                    $liftJ--;
                } else {
                    $liftI--;
                }
            }
        }
        return $leftList;
    }
}

$s1 = [1, 3, 4, 5, 6, 7, 7, 8];
$s2 = [3, 5, 7, 4, 8, 6, 7, 8, 2];

$c = (new Dp())->outPrint($s1, $s2);
var_dump($c);

8.贪心算法【纸币找零问题】

所谓贪心算法是指,在对问题求解时,总是做出在当前看来是最好的选择。 也就是说,不从整体最优上加以考虑,他所做出的仅是在某种意义上的局部最优解。

 

 

假设1元、2元、5元、10元、20元、50元、100元的纸币,张数不限制,现在要用来支付K元,至少要多少张纸币?

/**
* @param $money 需要找零金额
* @return array
*/
function greedy_money(int $money){
    $rData = [];
    $moneyLevel = [1,2,5,10,20,50,100];
    for ($i = (count($moneyLevel)-1); $i >= 0 ; $i--){
        $num = intval($money / $moneyLevel[$i]);
        if($num > 0){
            $rData[$moneyLevel[$i]] = $num;
        }
        $money = $money % $moneyLevel[$i];

    }
    return $rData;
}

 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值