动态规划——斐波那契问题(Java)

目录

什么是动态规划?

练习

练习1:斐波那契数

练习2:三步问题

练习3:使用最小花费爬楼梯

练习4:解码方法


什么是动态规划?

动态规划(Dynamic Programming,DP):是一种常见的算法设计技巧,通常用于解决具有重叠子问题和最优子结构的问题。在动态规划中,将原问题分解成若干子问题,通过求解子问题的最优解来得到原问题的最优解。

如何用动态规划解决问题?

我们可以通过一下5步来解决动态规划问题

1. 确定状态表示

什么是状态表示? 

状态表示:指在解决问题时所需要考虑和记录的关键信息。这些状态可以是问题的某种属性或特征,通过对这些状态的定义和记录,可以更好地理解问题的结构,从而设计出高效的动态规划算法。

在解决动态规划问题时,我们通常需要创建动态规划数组(dp),用于存储子问题的解,动态规划数组一般是一个 一维 或 二维 数组,而状态表示,可以理解为 dp表中值的含义 (即dp[i] 或 dp[i][j]的含义)

如何确定状态表示?

1. 从题目要求中确定

2. 根据题目要求 以及 经验 确定

3. 在分析问题的过程中,发现重复子问题,从而确定状态表示

在使用动态规划解决问题时,我们常见的状态表示有两种:

dp[i]:表示以i位置为结尾,...

dp[i]:表示以i位置为起始,...

2. 确定状态转移方程

什么是状态转移方程? 

状态转移方程:确定状态之间的转移关系,即如何从一个状态推导出另一个状态。这是动态规划问题的核心,也是最难的一步,也就是需要确定 dp[i] 或 dp[i][j] = ?

如何确定状态转移方程?

 需要根据题目要求以及状态表示进行推导

3. 进行初始化

初始化:初始化动态规划数组或表格,通常需要设置起始状态的初始值,为后续的状态转移做准备。(例如:保证后续填表过程中不会发生越界)

4. 确定填表顺序

5. 确定返回值

接下来,我们通过动态规划来解决斐波那契相关练习,帮助我们进一步理解动态规划

练习

练习1:斐波那契数

题目链接:

LCR 126. 斐波那契数 - 力扣(LeetCode)

题目描述:

斐波那契数 (通常用 F(n) 表示)形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:

F(0) = 0,F(1) = 1
F(n) = F(n - 1) + F(n - 2),其中 n > 1

给定 n ,请计算 F(n) 。

答案需要取模 1e9+7(1000000007) ,如计算初始结果为:1000000008,请返回 1。

示例 1:

输入:n = 2
输出:1
解释:F(2) = F(1) + F(0) = 1 + 0 = 1

示例 2:

输入:n = 3
输出:2
解释:F(3) = F(2) + F(1) = 1 + 1 = 2

示例 3:

输入:n = 4
输出:3
解释:F(4) = F(3) + F(2) = 2 + 1 = 3

提示:

  • 0 <= n <= 100

思路分析:

首先,我们来确定状态表示 ,即dp[i] 表示什么

这道题根据题目要求就可直接定义出状态表示:dp[i]:第i个斐波那契数

确定了状态表示后,我们来推导状态转移方程,即dp[i] = ?

题目中其实已经告诉了我们状态转移方程,即 F(n) = F(n - 1) + F(n - 2)

因此,状态转移方程为:

dp[i] = dp[i-1] + dp[i - 2]

接下来,我们对dp数组进行初始化:

从递推公式我们就可以看出:在i = 0 和 i = 1时是没办法进行推导的,因为dp[-2] 和 dp[-1] 不存在

因此,我们在进行填表之前,要先将 0,1位置的值进行初始化,题目中已经告诉我们dp[0] = 0,dp[1] = 1

填表顺序:从左到右依次填写 

返回值:要求出第n个斐波那契数,因此,返回值为dp[n] 

注意:题目要求答案需要取模 1e9+7(1000000007) ,不要忽略了这个条件

代码实现:

class Solution {public int fib(int n) {//处理特殊情况if(n == 0) return 0;//创建dp表int[] dp = new int[n+1];//初始化dp[0] = 0;dp[1] = 1;//填表for(int i = 2; i <= n; i++){dp[i] = (dp[i-1] + dp[i-2]) % 1000000007;}//返回结果return dp[n];}
}

优化:

在这道题中,我们可以使用滚动数组进行优化:

由于我们每次确定dp[i]时,只会涉及到三个变量:dp[i-2] dp[i-1] dp[i],因此,每次求dp[i] 时只需要知道dp[i-1] 和 dp[i-2]的值就可以了,我们可以使用变量 a  b c来分别表示 dp[i-2] dp[i-1] dp[i], c = a + b,这样我们仅需使用三个变量就可以遍历数组

在每次求出c的值后,更新a和b的值(注意先更新a的值(a = b),再更新b的值(b = c)) 使其向后移动,从而确定下一个结果

优化代码:

class Solution {public int fib(int n) {//处理特殊情况if(n == 0) return 0;//优化int a = 0, b = 1, c = 1;for(int i = 2; i <= n; i++){c = (a + b) % 1000000007;//更新a和ba = b;b = c;}//返回结果return c;}
}

练习2:三步问题

题目链接:

面试题 08.01. 三步问题 - 力扣(LeetCode)

题目描述:

三步问题。有个小孩正在上楼梯,楼梯有n阶台阶,小孩一次可以上1阶、2阶或3阶。实现一种方法,计算小孩有多少种上楼梯的方式。结果可能很大,你需要对结果模1000000007。

示例1:

 输入:n = 3 
 输出:4
 说明: 有四种走法

示例2:

 输入:n = 5
 输出:13

提示:

  1. n范围在[1, 1000000]之间

思路分析:

 小孩每次可以上1阶、2阶或3阶台阶,我们首先来模拟小孩上台阶的过程:

状态表示:

我们以 i 位置为结尾进行分析:

那么在这道题中,以i位置为结尾,则可表示为小孩到达第i阶台阶,

而dp[i]则可表示:小孩到达第i阶台阶时,一共有dp[i]种上楼方式

状态转移方程:

由于小孩一次可以上1阶、2阶或3阶台阶,因此,小孩可以从 第 i-3 或 i-2 或 i-1位置到达第i阶台阶

由于dp[i]: 小孩到达第i阶台阶时,一共有dp[i]种上楼方式,因此,

dp[i-1]: 小孩到达第 i - 1阶台阶时,一共有dp[i - 1]种上楼方式,则从i-1位置到达i位置有dp[i-1]种上楼方式

同理,从i-2位置到达i位置有dp[i-2]种上楼方式,从i-3位置到达i位置有dp[i-3]种上楼方式

因此,dp[i] = dp[i-1] + dp[i-2] + dp[i-3]

初始化:

由于dp[i] = dp[i-1] + dp[i-2] + dp[i-3],因此,在i = 0, i = 1 以及 i = 2时是没办法进行推导的,

因此我们需要在填表之前,对0,1,2位置的值进行初始化,

由于至少上一阶台阶,因此,我们选择从1位置开始初始化,根据分析:dp[1] = 1, dp[2] = 2, dp[3] = 4

填表顺序:

i位置的值是由 i-1、i-2和 i-3位置的值得到的,因此填表顺序为从左向右

返回值:

要求上到第n阶台阶的方式,因此我们返回dp[n]

注意:由于本题计算的结果可能很多,需要对结果进行取模,在计算时,将三个值先加起来再进行取模((dp[i-1] + dp[i-2]+ dp[i-3]) % 1000000007)是不可取的(会报错),因此我们每计算一次(每两个数相加),就进行一次取模

代码实现:

class Solution {public int waysToStep(int n) {//处理特殊情况if(n == 1 || n == 2) return n;//创建dp表int[] dp = new int[n+1];//初始化dp[1] = 1;dp[2] = 2;dp[3] = 4;//填表for(int i = 4; i <= n; i++){dp[i] = ((dp[i-1] + dp[i-2]) % 1000000007 + dp[i-3]) % 1000000007;}//返回return dp[n];}
}

与练习1相同,由于dp[i] = dp[i-1] + dp[i-2] + dp[i-3],因此,本题也可以使用滚动数组进行优化:

class Solution {public int waysToStep(int n) {//处理特殊情况if(n == 1 || n == 2) return n;if(n == 3) return 4;//创建dp表//初始化int a = 1, b = 2, c = 4, d = 0;//填表for(int i = 4; i <= n; i++){d = ((a + b) % 1000000007 + c) % 1000000007;a = b;b = c;c = d;}//返回return d;}
}

练习3:使用最小花费爬楼梯

题目链接:

746. 使用最小花费爬楼梯 - 力扣(LeetCode)

题目描述:

给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。

你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。

请你计算并返回达到楼梯顶部的最低花费。

示例 1:

输入:cost = [10,15,20]
输出:15
解释:你将从下标为 1 的台阶开始。
- 支付 15 ,向上爬两个台阶,到达楼梯顶部。
总花费为 15 。

示例 2:

输入:cost = [1,100,1,1,1,100,1,1,100,1]
输出:6
解释:你将从下标为 0 的台阶开始。
- 支付 1 ,向上爬两个台阶,到达下标为 2 的台阶。
- 支付 1 ,向上爬两个台阶,到达下标为 4 的台阶。
- 支付 1 ,向上爬两个台阶,到达下标为 6 的台阶。
- 支付 1 ,向上爬一个台阶,到达下标为 7 的台阶。
- 支付 1 ,向上爬两个台阶,到达下标为 9 的台阶。
- 支付 1 ,向上爬一个台阶,到达楼梯顶部。
总花费为 6 。

提示:

  • 2 <= cost.length <= 1000
  • 0 <= cost[i] <= 999

思路分析:

 可以从0或者1位置开始爬楼梯,当爬到i位置时,需要支付cost[i]继续向上爬楼梯,可以爬到 i + 1 或 i + 2位置,最终求爬到顶楼的最小花费(注意:顶楼在 cost.length 位置,而不是 cost.length - 1位置

状态表示:

我们以 i 位置为结尾进行分析:

那么在这道题中,以i位置为结尾,则可表示到达第i阶台阶,

而dp[i]则可表示:到达第i阶台阶时的最小花费

状态转移方程:

由于一次只能向上爬一个或两个台阶,因此,只能从i - 2位置 或 i - 1位置到达i位置

由于dp[i]: 到达第i阶台阶时的最小花费,因此,

dp[i-1]: 到达第 i - 1阶台阶时,最小花费为dp[i-1],则从i-1位置到达i位置的最小花费为 dp[i-1] + cost[i-1]

同理,从i-2位置到达i位置时最小花费为dp[i-2],则从 i-2 位置到达i位置的最小花费为 dp[i-2] + cost[i-2]

要求到达第i阶台阶时的最小花费,则dp[i] = min(dp[i-1] + cost[i-1], dp[i-2] + cost[i-2])

初始化:

可以从0位置或1位置开始向上爬,因此dp[0] = dp[1] = 0

填表顺序:由于dp[i] = min(dp[i-1] + cost[i-1], dp[i-2] + cost[i-2]),填表顺序为从左向右

返回值:要求到达顶楼(cost.length)的最小花费,因此返回dp[cost.length]

代码实现:

class Solution {public int minCostClimbingStairs(int[] cost) {int n = cost.length;//创建dp表int[] dp = new int[n + 1];//填表for(int i = 2; i <= n; i++){dp[i] = Math.min(dp[i-1] + cost[i-1], dp[i-2] + cost[i-2]);}//返回return dp[n];}
}

练习4:解码方法

题目链接:

91. 解码方法 - 力扣(LeetCode)

题目描述:

一条包含字母 A-Z 的消息通过以下映射进行了 编码 :

'A' -> "1"
'B' -> "2"
...
'Z' -> "26"

要 解码 已编码的消息,所有数字必须基于上述映射的方法,反向映射回字母(可能有多种方法)。例如,"11106" 可以映射为:

  • "AAJF" ,将消息分组为 (1 1 10 6)
  • "KJF" ,将消息分组为 (11 10 6)

注意,消息不能分组为  (1 11 06) ,因为 "06" 不能映射为 "F" ,这是由于 "6" 和 "06" 在映射中并不等价。

给你一个只含数字的 非空 字符串 s ,请计算并返回 解码 方法的 总数 。

题目数据保证答案肯定是一个 32 位 的整数。

示例 1:

输入:s = "12"
输出:2
解释:它可以解码为 "AB"(1 2)或者 "L"(12)。

示例 2:

输入:s = "226"
输出:3
解释:它可以解码为 "BZ" (2 26), "VF" (22 6), 或者 "BBF" (2 2 6) 。

示例 3:

输入:s = "06"
输出:0
解释:"06" 无法映射到 "F" ,因为存在前导零("6" 和 "06" 并不等价)。

提示:

  • 1 <= s.length <= 100
  • s 只包含数字,并且可能包含前导零。

思路分析:

对给定的数字字符串进行解码,1-26分别对应A-Z,且在解码时不能出现前导0(如05)

状态表示:

我们以 i 位置为结尾进行分析:

那么在这道题中,以i位置为结尾,则dp[i]则可表示:0 到 i 位置一共有dp[i]种解码方法

状态转移方程:

由于1 - 26 分别对应 A - Z,则一个字母只可能由一个字符或两个字符进行解码得到,因此,以i位置为结尾进行解码时,最后一个字母只可能由当前字母(s[i])或当前字母和前一个字母(s[i] 和 是[i-1])进行解码得到

由于dp[i]: 0 到 i 位置一共有dp[i]种解码方法,因此,

dp[i-1]: 0 到 i 位置一共有dp[i]种解码方法,若s[i]能够解码,则以i位置为结尾的字符串共有 dp[i-1]种解码方法,若s[i]不能解码,则解码方法为0

同理,dp[i-2]表示以 i - 2为结尾的字符串一共有dp[i-2]种解码方法,若s[i]和s[i-1]位置组成的数字能够解码,则以i位置为结尾的字符串共有 dp[i-2]种解码方法,否之,则解码方法为0

因此,

若s[i]解码成功(s[i] >= '1' && s[i] <= '9'),则dp[i] += dp[i-1],否之,dp[i] += 0

若s[i]和s[i-1]位置组成的数字解码成功,则 dp[i] += dp[i-2],否之,dp[i] += 0

在判断s[i]和s[i-1]位置组成的数字是否解码成功时,我们可以先将其转换为数字 n = (s[i-1] - '0') * 10 + (s[i] - '0'),由于不能出现前导0,因此,若结果在 10 - 26区间内,则解码成功,反之,则失败

初始化:

由于在确定i位置的值时需要 i - 1 和 i - 2位置上的值,因此,我们要先初始化i = 0和 i = 1位置上的值:

dp[0]:若 s[0] >= '1' && s[0] <= '9',则dp[0] = 1,反之,dp[0] = 1

dp[1]:s[1] >= '1' && s[1] <= '9',dp[1] += dp[0],反之,dp[1] += 0

s[0] 和 s[1]结合后数字在[10, 26]区间内,则dp[1] += 1,反之,dp[1] += 0

我们可以发现:前两个位置的初始化方式与填写后续i位置时十分相似,都需要判断字符是否能够

正确解码,因此,我们可以在最前面添加一个辅助节点,来帮助我们进行初始化:

在本题中,添加辅助节点后,就只需要初始化dp[1],若 s[1] >= '1' && s[1] <= '9',则dp[1] += dp[0]

在添加辅助节点时需要注意:

1. 辅助节点里的值要保证后续填表过程中不出错

2. 下标的映射关系

添加辅助节点后dp表与s的映射关系变为了 dp[i] - s[i-1],因此,我们在编写代码时要注意

当s[0]解码正确时,dp[1] += dp[0],因此,dp[0]应初始化为 1

填表顺序:从左往右

返回值:dp[n]

代码实现:

class Solution {public int numDecodings(String ss) {char[] s = ss.toCharArray();int n = s.length;//创建dp表int[] dp = new int[n+1];//初始化dp[0] = 1;//保证后续填表正确if(s[0] > '0' && s[0] <= '9') dp[1] += dp[0];//填表for(int i = 2; i <= n; i++){int num = s[i-1] - '0';//注意下标的映射关系if(num > 0 && num <= 9) dp[i] += dp[i-1];num = (s[i-2] - '0') * 10 + s[i-1] - '0';if(num >= 10 && num <= 26) dp[i] += dp[i-2];}//返回return dp[n];}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/283817.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

锂电池寿命预测 | Matlab基于ALO-SVR蚁狮优化支持向量回归的锂离子电池剩余寿命预测

目录 预测效果基本介绍程序设计参考资料 预测效果 基本介绍 锂电池寿命预测 | Matlab基于ALO-SVR蚁狮优化支持向量回归的锂离子电池剩余寿命预测 基于蚁狮优化和支持向量回归的锂离子电池剩余寿命预测: 1、提取NASA数据集的电池容量&#xff0c;以历史容量作为输入&#xff0c;…

电脑安装双系统windows和ubuntu server

1.创建Ubuntu-server的启动盘 首先要从官网下载Ubuntu-server18.04的ISO文件&#xff0c;用rufs烧录到U盘。如下所示 2. 磁盘分区 在windows创建两个盘&#xff08;linuxboot 和linuxroot&#xff09;&#xff0c;后面一个一个用于boot&#xff0c;一个用于root. 3.开机U盘启…

AI开源概览及工具使用

一、前言 随着ChatGPT热度的攀升&#xff0c;越来越多的公司也相继推出了自己的AI大模型&#xff0c;如文心一言、通义千问等。各大应用也开始内置AI玩法&#xff0c;如抖音的AI特效&#xff1b; 关联资源&#xff1a;代码 GitHub、相关论文、项目Demo、产品文档、Grok Ai、gr…

【项目自我反思之vue的组件通信】

为什么子组件不能通过props实时接收父组件修改后动态变化的值 一、现象二、可能的原因1.响应式系统的限制2.异步更新队列3.父组件和子组件的生命周期4.子组件内部对 props 的处理 三、组件通信的几种场景&#xff08;解决方案&#xff09;1.子组件想修改父组件的数据2.子组件传…

【数据结构】猛猛干7道链表OJ

前言知识点 链表的调试技巧 int main() {struct ListNode* n1(struct ListNode*)malloc(sizeof(struct ListNode));assert(n1);struct ListNode* n2(struct ListNode*)malloc(sizeof(struct ListNode));assert(n2);struct ListNode* n3(struct ListNode*)malloc(sizeof(struc…

如何从零开始拆解uni-app开发的vue项目(一)

uni-app项目分析: 背景:最近接手一个前同事留下的半拉子项目,出拿过来觉得很简单;当我看到app.vue的时候很确定是vue项目,心里不怎么慌,果断安装node.js,然后就去npm ;安装VS code,事实并不是我期盼的那样,或者说根本就不能运行。 报错:应用vs code打开文件,输入命…

力扣每日一题 2024/3/23 统计桌面上的不同数字

题目描述 用例说明 思路讲解 给定整数n&#xff0c;找出循环十亿天后桌上的数字。可以先通过一天来找找规律。 第一天 n%i1 &#xff08;1<i<n&#xff09;只有n-1符合.加入桌面 第二天(n-1)%i1 &#xff08;1<i<n-1&#xff09;只有n-2符合 加入桌面 依次类推…

低代码开发平台开源:依靠科技力量实现数字化转型!

在竞争激烈的当今社会&#xff0c;数字化转型、流程化办公等字眼早已充斥在我们的职场生活中。虽然如此&#xff0c;但是我们依然要面临着这样一个现实问题&#xff1a;很多中小企业发展面临着资源有限、技术储备不足、人才短缺的现实问题&#xff0c;进入流程化办公困境依然明…

记录C++中,子类同名属性并不能完全覆盖父类属性的问题

问题代码&#xff1a; 首先看一段代码&#xff1a;很简单&#xff0c;就是BBB继承自AAA&#xff0c;然后BBB重写定义了同名属性&#xff0c;然后调用父类AAA的打印函数&#xff1a; #include <iostream> using namespace std;class AAA { public:AAA() {}~AAA() {}void …

145 Linux 网络编程1 ,协议,C/S B/S ,OSI 7层模型,TCP/IP 4层模型,

一 协议的概念 从应用的角度出发&#xff0c;协议可理解为“规则”&#xff0c;是数据传输和数据的解释的规则。 典型协议 传输层 常见协议有TCP/UDP协议。 应用层 常见的协议有HTTP协议&#xff0c;FTP协议。 网络层 常见协议有IP协议、ICMP协议、IGMP协议。 网络接口层 常…

NLP 笔记:LDA(训练篇)

1 前言&#xff1a;吉布斯采样 吉布斯采样的基本思想是&#xff0c;通过迭代的方式&#xff0c;逐个维度地更新所有变量的状态 1.1 举例 收拾东西 假设我们现在有一个很乱的屋子&#xff0c;我们不知道东西应该放在哪里&#xff08;绝对位置&#xff09;&#xff0c;但知道哪…

【排序算法】实现快速排序值(霍尔法三指针法挖坑法优化随即选key中位数法小区间法非递归版本)

文章目录 &#x1f4dd;快速排序&#x1f320;霍尔法&#x1f309;三指针法&#x1f320;挖坑法✏️优化快速排序 &#x1f320;随机选key&#x1f309;三位数取中 &#x1f320;小区间选择走插入&#xff0c;可以减少90%左右的递归&#x1f309; 快速排序改非递归版本&#x1…

设计模式及其在项目、框架中的应用

设计模式的作用&#xff1a; 1、类之间关系图&#xff0c;明确的角色及其关系、作用&#xff1b; 2、符合开闭原则&#xff0c;职责明确&#xff0c;并且开放的拓展点可以有效应对后期的变化。 &#xff08;一&#xff09;、责任链模式 适用场景&#xff1a; 在一个流程中&…

【QT入门】 Qt实现自定义信号

往期回顾&#xff1a; 【QT入门】图片查看软件(优化)-CSDN博客 【QT入门】 lambda表达式(函数)详解-CSDN博客 【QT入门】 Qt槽函数五种常用写法介绍-CSDN博客 【QT入门】 Qt实现自定义信号 一、为什么需要自定义信号 比如说现在一个小需求&#xff0c;我们想要实现跨ui通信&a…

Git 使用笔记

基本操作&#xff1a; 初始化 &#xff08;git init&#xff09; 使用背景和作用&#xff1a; 在本地建立一个文件夹后&#xff0c;基于这个文件夹进行git 操作&#xff0c;赋予git操作本文件夹的权限 。查看当前文件夹状态&#xff08;git status&#xff09; 每次打开文件夹…

环信新版单群聊UIKit集成指南——Android篇

前言 环信新版UIKit已重磅发布&#xff01;目前包含单群聊UIKit、聊天室ChatroomUIKit&#xff0c;本文详细讲解Android端单群聊UIKit的集成教程。 环信单群聊 UIKit 是基于环信即时通讯云 IM SDK 开发的一款即时通讯 UI 组件库&#xff0c;提供各种组件实现会话列表、聊天界…

机器学习:智能时代的核心引擎

目录 一、什么是机器学习 二、监督学习 三、无监督学习 四、半监督学习 五、强化学习 一、什么是机器学习 机器学习是人工智能的一个分支&#xff0c;它主要基于计算机科学&#xff0c;旨在使计算机系统能够自动地从经验和数据中进行学习并改进&#xff0c;而无需进行明确…

鸿蒙Harmony应用开发—ArkTS(stateStyles:多态样式)

Styles和Extend仅仅应用于静态页面的样式复用&#xff0c;stateStyles可以依据组件的内部状态的不同&#xff0c;快速设置不同样式。这就是我们本章要介绍的内容stateStyles&#xff08;又称为&#xff1a;多态样式&#xff09;。 概述 stateStyles是属性方法&#xff0c;可以…

CodeSys创建自定义的html5控件

文章目录 背景创建html5control.xml文件控件界面以及逻辑的实现使用的资源安装自定义的html5控件库 背景 查看官方的资料&#xff1a;https://content.helpme-codesys.com/en/CODESYS%20Visualization/_visu_html5_dev.html 官方的例子&#xff1a;https://forge.codesys.com/…

【机器学习入门 】逻辑斯蒂回归和分类

系列文章目录 第1章 专家系统 第2章 决策树 第3章 神经元和感知机 识别手写数字——感知机 第4章 线性回归 文章目录 系列文章目录前言一、分类问题的数学形式二、最大似然估计三、交叉熵损失函数四、多类别分类多类别逻辑斯蒂回归归一化指数函数交叉熵误差和均方误差的比较 五…