算法:[动态规划] 斐波那契数列模型

news2025/1/7 6:31:30

目录

题目一:第 N 个泰波那契数

题目二:三步问题

题目三:最小花费爬楼梯

题目四:解码方法


题目一:第 N 个泰波那契数

泰波那契序列 Tn 定义如下: 

T0 = 0, T1 = 1, T2 = 1, 且在 n >= 0 的条件下 Tn+3 = Tn + Tn+1 + Tn+2

给你整数 n,请返回第 n 个泰波那契数 Tn 的值。

示例 1:

输入:n = 4
输出:4
解释:
T_3 = 0 + 1 + 1 = 2
T_4 = 1 + 1 + 2 = 4

示例 2:

输入:n = 25
输出:1389537

提示:

  • 0 <= n <= 37
  • 答案保证是一个 32 位整数,即 answer <= 2^31 - 1

大家都知道斐波那契数,而这道题是泰波那契数,注意是从0开始的

泰波那契数就是指,第n项是前面三项之和

所以前三项是0  1  1的话,前几个泰波那契数就是:0  1  1  2  4  7这样子的


下面采用动态规划的方法处理这道题,动态规划分为五步,前两步是最重要的:

使用动态规划时,一般会先创建一个数组,起名为dp,数组称之为dp表,把这个dp表填满,里面的某一个值可能就是最终的结果

①状态表示
②状态转移方程
③初始化
④填表顺序
⑤返回值

下面说说这五步具体的逻辑:

①状态表示

状态表示初步理解一下,就是dp表中某一个位置的值所表示的含义

状态表示是怎么确定的呢?

1、题目要求
2、经验 + 题目要求
3、分析问题的过程中,发现重复的子问题

这道题比较简单,所以就是第一种题目要求来确定状态表示,也就是:

dp[i] 表示:第 i 个泰波那契数的值

②状态转移方程

状态转移方程,也就是让 dp[i] 等于什么,而这道题已经告诉我们状态转移方程是:

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

③初始化

我们做动态规划就是为了把dp表给填满,初始化也就是保证填表的时候不越界

填表就是根据第二步得到的状态转移方程来填表,之所以要保证不越界,就拿本题中的泰波那契数的转移方程来说:
如果 i 取值为0,那代入这个状态转移方程时,就会出现dp[0] = dp[-1] + dp[-2] + dp[-3],此时这个数组会出现越界访问的问题,i 取值为1和2时,也会出现这种越界访问的问题

所以填表的时候需要保证不越界,而这道题前三道题会越界,那我们填表时,把前三个位置初始化即可,这道题目中也告诉了我们前三个值为0  1  1

④填表顺序

填表顺序就是为了填写当前状态时,所需要的状态已经计算过了

放在本题中说,也就是在填写 dp[4] 这个位置时,必须要知道dp[1]、dp[2]、dp[3]的值,所以本题的填表顺序是从左到右的,因为填当前位置时,左边的已经全部填进去了

⑤返回值

返回值就是题目要求的最终结果,即题目要求 + 状态表示,本题是求第 n 个泰波那契数,所以直接返回 dp[n] 即可


所以经过上面的五步,初步的代码是:

class Solution 
{
public:
    int tribonacci(int n) 
    {
        //处理边界情况
        if(n == 0) return 0;
        if(n == 1 || n == 2) return 1;
        // 1、创建 dp表
        vector<int> dp(n + 1);
        // 2、初始化
        dp[0] = 0, dp[1] = dp[2] = 1;
        // 3、填表
        for(int i = 3; i <= n; i++)
            dp[i] = dp[i-1] + dp[i-2] + dp[i-3];
        // 4、返回
        return dp[n];
    }
};

接下来可以进行空间优化,在动态规划中,空间优化一般都是使用滚动数组来进行优化的

我们发现,在代入状态转移方程时,求第 i 个位置的值时,只会用到 i 前面的三个值,其他位置的值是浪费空间的,当我们依次求 dp[i] 的时候,前面的有些状态是可以舍弃的,只需要用中间若干的状态,此时就可以用滚动数组进行优化

优化的效果就是:如果之前的空间复杂度是O(N^2),优化完就是O(N),如果之前是O(N),优化完就是O(1)

这道题中,因为我们只需要前面的三个变量,所以给出三个变量 a、b、c,当求第 d 个时,最开始是这样的:

求出来 d 等于 2 后,直接往下滚动,也就是:

又能求出最新的 d,在这个位置求完后,又能继续滚动,直到第 n 个位置,返回结果即可

下面的图能清楚的看出来滚动数组的执行过程:

也就是b的值给a,c的值给b,d的值给c,需要注意的是:
这里的赋值顺序是从前到后的,即:a = b, b = c, c = d
如果从后赋值, c = d, b = c, a = b,即每次的值都会被覆盖,c赋值给b之前,已经被d的值覆盖了,所以这种操作是不可取的

空间优化后代码:

class Solution 
{
public:
    int tribonacci(int n) 
    {
        //处理边界情况
        if(n == 0) return 0;
        if(n == 1 || n == 2) return 1;
        //空间优化
        int a = 0, b = 1, c = 1, d = 0;
        for(int i = 3; i <= n; i++)
        {
            d = a + b + c;
            a = b;
            b = c;
            c = d;
        }
        return d;
    }
};

题目二:三步问题

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

示例1:

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

示例2:

 输入:n = 5
 输出:13

提示:

        n范围在[1, 1000000]之间


假设这个小孩现在正在0号位置也就是水平地面上,面前有1、2、3、4号台阶:

0    1    2    3    4 ......

1、想上1号台阶:1种方式
①直接从0号上到1号台阶:0 -> 1

2、想上2号台阶:2种方式
①直接从0号上到2号台阶:0 -> 2
②直接从1号上到2号台阶,怎么到1号不管,但必须经过1号台阶,再到2号台阶:0 -> 1 -> 2

3、想上3号台阶:4种方式
①直接从0号上到3号台阶:0 -> 3
②直接从1号到3号,怎么到1号不管,但必须经过1号台阶:0 -> 1 -> 3
③④直接从2号到3号,怎么到2号不管,但必须经过2号台阶,因为前面的 2 中,已经说明了想上2号台阶有2种方式,所以有2种方式方式:
0 -> 2 -> 3;0 -> 1 -> 2  -> 3

4、想上4号台阶:7种方式
①直接从1号到4号,因为从0到不了,那就从1到4号台阶,怎么到1号不管,但必须经过1号台阶,而0到1的方式上面也说到了有1种方式,所以从1号到4号有1种方式:
0 -> 1 -> 4
②③直接从2号到4号,怎么到2不管,前面也说到了从0号到2号有2种方式,所以从2号到4号也有2种方式:
0 -> 2 -> 4;0 -> 1 -> 2 -> 4
④⑤⑥⑦直接从3到4,同理,怎么到3不管,前面从0到3的方式有4种,所以这里也是4种方式


所以下面根据上面所说的原理,列出dp表:

我们发现,到1、2、3号台阶的方式数量一旦直到,那么到第4号台阶的方式数量就是到1、2、3号台阶的方式数量之和,到5号台阶数量也是一样

所以继续动态规划的五步:

①状态表示

通过经验 + 题目要求,理解为:以 i 位置为结尾, ........,可以得知:

dp[i] 表示到达第 i 个台阶时,一共有多少种方法

②状态转移方程

依旧是通过经验,我们需要以 i 位置的状态,最近的一步来划分问题

由于这道题中的小孩一次能跨1阶、2阶或3阶,所以最近的一步就是从 i-1、i-2、i-3位置到 i 位置

因为从 i-3 位置到 i 位置一步就能跨到,所以只需知道从地面到 i-3 位置需要多少种方式即可,而 dp[i-3] 就是到达第 i-3 个台阶时,一共有多少种方法,所以从 i-3 位置到 i 位置就转化为了 dp[i-3] 

i-1、i-2 位置到 i 位置同理可得:dp[i-1]、dp[i-2],所以状态转移方程就是:

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

③初始化

初始化就为了填表的时候不越界,这道题是从1位置开始的,dp[0]是无意义的,所以dp[1]、dp[2]、dp[3]是会越界的,所以需要将dp[1]、dp[2]、dp[3]初始化,在上面讲述时计算了到3个台阶的方式数量,所以初始化为:

dp[1] = 1,dp[2] = 2,dp[3] = 4

④填表顺序

这道题填表顺序依旧是从左往右

⑤返回值

题目要求返回到 n 位置时,有多少种方式,而dp[n]正好就是这种意义

所以返回dp[n]即可


在初始化前,需要处理边界情况,因为n的范围是是:[1, 1000000],而我们需要初始化1、2、3,如果 n 为1,就会越界,所以需要处理边界条件

这里同样可以使用滚动数组进行优化,这里就不实现了,比较简单,和题目一中的优化几乎一摸一样,实现代码如下:

class Solution {
public:
    int waysToStep(int n) 
    {
        // 1、创建dp表
        // 2、填表前初始化
        // 3、填表
        // 4、求返回值

        //每次加法需要对MOD取模
        const int MOD = 1e9 + 7;

        if(n == 1 || n == 2) return n;
        if(n == 3) return 4;
        vector<int> dp(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]) % MOD + dp[i-3]) % MOD;
        return dp[n] % 1000000007;
    }
};

题目三:最小花费爬楼梯

给你一个整数数组 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

这道题需要注意的是,题目所给的数组的最后一个元素不是楼顶,而是台阶,楼顶是最后一个位置的下一个位置,示例一和示例二是这样子理解的:

依旧是五步:

解法一:状态表示(以 i 位置为结尾)

①状态表示

这里依旧是经验 + 题目要求

以 i 位置为结尾, .....

所以结合题目要求,在以 i 位置为结尾的前提下,这道题就理解为:到达 i 位置的最小花费

dp[i] 表示:到达 i 位置时,最小花费

②状态转移方程

状态转移方程也就是用之前或是之后的状态,推导出 dp[i] 的值

根据最近的一步,来划分问题:

到达 i 位置有两种情况:

1、先到达 i - 1 位置,然后支付 cost[i - 1] ,走一步,即:dp[i - 1] + cost[ i - 1]
2、先到达 i - 2 位置,然后支付 cost[i - 2] ,走两步,即:dp[i - 2] + cost[ i - 2]

所以得到结论:

dp[i] = min( dp[i - 1] + cost[ i - 1], dp[i - 2] + cost[ i - 2]  )

③初始化

初始化保证填表的时候不越界,因为这里需要用到 i 位置的前两个位置的cost值,所以当 i 是0或1时,会出现越界的情况,所以需要初始化

又因为题目说,可以从 0 位置或是 1 位置开始爬楼梯,所以就是说从 0 位置或是 1 位置爬楼梯不用花钱,所以都初始化为0即可

dp[0] = dp[1] = 0

④填表顺序

因为填 dp[i] 时,dp[i-1] 和 dp[i-2] 是已经算出来的,所以填表顺序也是从左往右填的

⑤返回值

想到达楼顶,所以返回值返回的是 dp[n]

所以代码如下:

class Solution 
{
public:
    int minCostClimbingStairs(vector<int>& cost) 
    {
        int n = cost.size();
        //创建dp表
        vector<int> dp(n + 1);
        //初始化这步可以不写,因为创建dp表时,默认全部初始化为0了
        dp[0] = dp[1] = 0;
        //根据状态转移方程填写dp表
        for(int i = 2; i <= n; i++)
            dp[i] = min(dp[i-1] + cost[i-1], dp[i-2] + cost[i-2]);
        //返回结果
        return dp[n];
    }
};

解法二:状态表示(以 i 位置为起点)

①状态表示

此时解法二,经验 + 题目要求,以 i 位置为起点,.......

所以结合题目要求,在以 i 位置为起点的前提下,这道题就理解为:从 i 位置到达楼顶的最小花费

dp[i] 表示:从 i 位置出发,达到楼顶的最小花费

②状态转移方程

状态转移方程也就是用之前或是之后的状态,推导出 dp[i] 的值

根据最近的一步,来划分问题:

从 i 位置出发有两种情况:

1、先支付 cost[i],然后走一步,变为从 i + 1 位置出发到达终点,即:cost[i] + dp[i + 1]
2、先支付 cost[i],然后走两步,变为从 i + 2 位置出发到达终点,即:cost[i] + dp[i + 2]

所以得到结论:

dp[i] = min( cost[i] + dp[i + 1], cost[i] + dp[i + 2] )

③初始化

初始化是为了保证填表时不越界,而这里的 dp[i] 每次填写时,需要的是后两个的cost值,所以需要先初始化后两个位置的值

因为第 n 个位置是楼顶,所以只需要初始化第 n -1 和 n - 2位置的值,所以初始化为:

dp[n - 1] = cost[n - 1],dp[n - 2] = cost[n - 2]

④填表顺序

由上面的初始化可知,我们需要知道 i 位置之后的两个位置在dp表中的值,才能知道 i 位置的值,所以填表顺序是从右向左

⑤返回值

由于 dp[i] 表示从某个位置出发,到达楼顶的最小花费,而我们刚开始是从 0 位置或是 1 位置出发的,所以需要这两个位置较小的那一个,所以返回 min(dp[0], dp[1])

代码如下:

class Solution 
{
public:
    int minCostClimbingStairs(vector<int>& cost) 
    {
        int n = cost.size();
        //创建dp表
        vector<int> dp(n);
        //初始化
        dp[n - 1] = cost[n - 1], dp[n - 2] = cost[n - 2];
        //根据状态转移方程填写dp表
        for(int i = n - 3; i >= 0; i--)
            dp[i] = min(dp[i+1] + cost[i], dp[i+2] + cost[i]);
        //返回结果
        return min(dp[0], dp[1]);
    }
};

题目四:解码方法

一条包含字母 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 只包含数字,并且可能包含前导零。

这道题题意就不说明了,示例已经很清楚了,下面说动态规划实现的思路,依旧是5步:

①状态表示

同样是经验 + 状态表示:以 i 位置为结尾, ......

因为这个题目是给出一个字符串来解码,得到有多少种解码方式,所以表示到 i 位置时,有多少个解码方法数量

dp[i] 表示:以 i 位置为结尾时,解码方法的总数

②状态转移方程

根据最近的一步,划分问题

所以分为两个情况,i 位置单独解码;i 位置与 i - 1 位置结合一起解码

这两种情况都有两种可能:成功、失败

1、i 位置单独解码

解码成功:即 i 位置字符是 [1, 9] 之间的
       此时的解码数就是以 i - 1 位置为结尾时,所有的解码方案后面统一加上以 i 位置的解码成功的字符,这时的解码数量依旧是以 i - 1 位置为结尾的解码方案数,也就是dp[i - 1]
解码失败:即 i 位置字符是 0 
       此时在单独解码的方式下, 前面所有的方案都白费,因为 i 位置解码失败,整体也就失败了,所以此时的解码方案数是 0 

2、i 位置与 i - 1 位置结合一起解码

假设 i 位置字符是 a,i - 1 位置是 b

解码成功:即 a * 10 + b 是 [10, 26] 之间的,因为映射的英文字母只有26个且 a 不能为0
       此时的解码数就是以 i - 2 位置为结尾时,所有的解码方案后面统一加上以 i 位置和 i - 1 位置结合起来后的解码成功的字符,这时的解码数量依旧是以 i - 2 位置为结尾的解码方案数,也就是dp[i - 2]
解码失败:即 i 位置字符是 0 
       此时在结合解码的方式下, 前面所有的方案都白费,因为 i 位置与 i - 1 位置结合解码失败,整体也就失败了,所以此时的解码方案数是 0 

所以总结上述的描述过程,状态转移方程为:

dp[i] = dp[i - 1] + dp[i - 2](这里是解码成功时才会加)

③初始化

观察上述的状态转移方程,计算 i 位置时,需要用到 i - 1 和 i - 2 位置的值,所以需要初始化dp[0]和dp[1]的值

dp[0]:有2种情况,0/1,表示0号位置字符能否解码成功,成功就是1,失败就是0

dp[1]:有三种情况,0/1/2,表示0号和1号位置的字符,是否能够单独解码成功,或是结合解码成功,如果都不成功就是0,都成功就是2,成功一种就是1

④填表顺序

观察状态转移方程,很显然:

填表顺序是从左往右填

⑤返回值

dp[i] 表示 i 位置结尾时的解码方法总数,所以这里返回:dp[n - 1]


代码如下:

class Solution 
{
public:
    int numDecodings(string s) 
    {
        int n = s.size();
        //创建dp表
        vector<int> dp(n);
        //初始化
        dp[0] = (s[0] - '0') == 0 ? 0 : 1;
        //处理边界条件
        if(n == 1) return dp[0];

        int a = s[0] - '0';
        int b = s[1] - '0';
        if(a != 0 && b != 0) dp[1]++;
        int tmp = a * 10 + b;
        if(tmp >= 10 && tmp <= 26) dp[1]++;

        //状态转移方程,需要判断是否解码成功
        for(int i = 2; i < n; i++)
        {
            int a = s[i - 1] - '0';
            int b = s[i] - '0';
            tmp = a * 10 + b;
            //分两种情况,判断是否加编码数
            if(b != 0) dp[i] += dp[i - 1]; //单独编码时的情况
            if(tmp >= 10 && tmp <= 26) dp[i] += dp[i - 2]; //结合编码时的情况
        }
        //返回结果
        return dp[n - 1];
    }
};

下面说说细节问题,也就是处理边界问题与初始化问题的技巧:

我们可以发现上述代码编写过程中,下面两个框的内容是高度相似的

所以下面引入一种常见的方法,即:将整个dp数组向后移动一位

也就是原来的dp数组的空间是 n ,现在就变为 n + 1 了,统一移动一位后,在数组最前就多了一个0下标,也称为虚拟节点

此时我们处理边界条件时,就会比原来少处理一个位置,虽然依旧是要处理 dp[0] 和 dp[1],但是此时的dp[0]只需要根据题意赋值即可,相当于原来非常繁琐的初始化两次,减少为了一次

有两个注意事项:

①虚拟节点里面的值,需要根据题意保证后面的填表是正确的
②需要注意下标的映射关系

注意事项一中,dp[0]中的值并不是固定的,而是要根据题目条件判断,对于本道题来说,初始化完 dp[0] 和 dp[1] 后,填dp[2]时,dp[0]表示的含义就是 i 和 i - 1位置组合解码成功时需要加的值,所以如果 dp[2] 和 dp[1] 组合后解码成功,就需要加 dp[0] 的值,因此这里的 dp[0] 需要赋值为1

注意事项二中,由于dp数组统一向后移动了一位,所以在映射到原字符串s时,需要 -1 操作,对于本道题来说,dp数组向后移动后,初始化 dp[1] 时,需要判断的就是 s[1 - 1] - '0' 是否为0,而不是判断 s[1] - '0' 是否为0

优化后的代码如下:

class Solution 
{
public:
    int numDecodings(string s) 
    {
        int n = s.size();
        //创建dp表
        vector<int> dp(n + 1);
        //初始化
        dp[0] = 1;
        dp[1] = (s[1 - 1] - '0') == 0 ? 0 : 1;

        //状态转移方程,需要判断是否解码成功
        for(int i = 2; i <= n; i++)
        {
            //统一找字符串s中字符时,需要-1操作
            int a = s[i - 2] - '0';
            int b = s[i - 1] - '0';
            int tmp = a * 10 + b;
            //分两种情况,判断是否加编码数
            if(b != 0) dp[i] += dp[i - 1]; //单独编码时的情况
            if(tmp >= 10 && tmp <= 26) dp[i] += dp[i - 2]; //结合编码时的情况
        }
        //返回结果
        return dp[n];
    }
};

可以看到是比源代码优化很多的,将原代码中高度相似的代码只出现一次


动态规划——斐波那契数列模型相关的题目结束

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CAN总线(下)

位时序 为了灵活调整每个采样点的位置&#xff0c;使采样点对齐数据位中心附近&#xff0c;CAN总线对每一个数据位的时长进行了更细的划分&#xff0c; 分为同步段&#xff08;SS&#xff09;、传播时间段&#xff08;PTS&#xff09;、相位缓冲段1&#xff08;PBS1&#xff0…

【Threejs进阶教程-优化篇】4.Vue/React与threejs如何解决冲突和卡顿(续)

Vue/React与threejs如何解决冲突和卡顿-续 使用说明核心思路环境搭建(vuethree)vue运行机制分析业务分离使用threejs做背景 3D模块封装使用ES6的Class来让逻辑性更强Threejs尽量按需引入创建一个类扩展写法本次代码执行顺序 扩展内容添加orbitControls和辅助线解决事件覆盖 与V…

程序员学长 | 快速学会一个算法,xLSTM

本文来源公众号“程序员学长”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;快速学会一个算法&#xff0c;xLSTM 今天给大家分享一个超强的算法模型&#xff0c;xLSTM。 xLSTM&#xff08;Extended Long Short-Term Memory&…

AI大模型的智能心脏:向量数据库的崛起

在人工智能的飞速发展中&#xff0c;一个关键技术正悄然成为AI大模型的智能心脏——向量数据库。它不仅是数据存储和管理的革命性工具&#xff0c;更是AI技术突破的核心。随着AI大模型在各个领域的广泛应用&#xff0c;向量数据库的重要性日益凸显。 01 技术突破&#xff1a;向…

水箱高低水位浮球液位开关工作原理

工作原理 水箱高低水位浮球液位开关是一种利用浮球随液位升降来实现液位控制的设备。其基本原理是浮球在液体的浮力作用下上下浮动&#xff0c;通过磁性作用驱动与之相连的磁簧开关的开合&#xff0c;从而实现液位的高低控制和报警。当液位升高时&#xff0c;浮球上浮&#xf…

Jmeter实现接口自动化

自动化测试理论知识 什么是自动化测试&#xff1f; 让程序或工具代替人为执行测试用例什么样的项目适合做自动化&#xff1f; 1、项目周期长 --多长算长&#xff1f;&#xff08;自己公司运营项目&#xff09; 2、需求稳定&#xff08;更多具体功能/模块&#xff09; 3、需要…

LabVIEW机器视觉系统中的图像畸变、校准和矫正

在机器视觉应用中&#xff0c;图像畸变、校准和矫正是确保图像准确性的关键步骤。LabVIEW作为一种强大的图像处理和分析工具&#xff0c;提供了一系列功能来处理这些问题。以下是对图像畸变、校准和矫正的详细介绍。 图像畸变 图像畸变 是指由于摄像镜头的光学特性或拍摄角度问…

昇思25天学习打卡营第3天|MindSpore张量

# 打卡 目录 # 打卡 类 涉及知识点 1. 创建张量的4种方式 运行例子 2. 张量属性和索引 运行例子 3. 张量运算 运行例子 4. Tensor 与 Numpy 转换 5. 稀疏张量&#xff1a;CSR和COO CSRTensor 运行例子 COOTensor 运行例子 RowTensor 类 import mindspore from…

CMD命令详细介绍 | 超详细版本!

文章目录 启动cmd命令用户启动使用管理员的账号启动 文件夹命令网络命令其他常用命令介绍常用快捷方式程序员相关命令 本文参考了博客园一篇帖子&#xff0c;ULR&#xff1a;cmd常用命令介绍(可收藏) - Mrwhite86 - 博客园 (cnblogs.com) CMD是Windows操作系统自带的命令行解释…

【Linux】打包命令——tar

打包和压缩 虽然打包和压缩都涉及将多个文件组合成单个实体&#xff0c;但它们之间存在重要差异。 打包和压缩的区别&#xff1a; 打包是将多个文件或目录组合在一起&#xff0c;但不对其进行压缩。这意味着打包后的文件大小可能与原始文件相同或更大。此外&#xff0c;打包…

【工具分享】Gophish

文章目录 Gophish安装方式功能简介 Gophish Gophish 是一个开源的网络钓鱼框架&#xff0c;它被设计用于模拟真实世界的钓鱼攻击&#xff0c;以帮助企业和渗透测试人员测试和评估他们的网络钓鱼风险。Gophish 旨在使行业级的网络钓鱼培训对每个人都是可获取的&#xff0c;它易…

文件存储的方法一

文章目录 概念介绍实现方法示例代码 我们在上一章回中介绍了"如何实现本地存储"相关的内容&#xff0c;本章回中将介绍如何实现文件存储.闲话休提&#xff0c;让我们一起Talk Flutter吧。 概念介绍 我们在上一章回中介绍的本地存储只能存储dart语言中基本类型的数值…

鲨疯了,免费的Viva又能画图,又能做视频,又能......

theme: smartblue 点赞 关注 收藏 学会了 本文简介 众所周知&#xff0c;Midjourney擅长画画&#xff0c;Runway擅长做视频(Sora也说它可以做)。 Viva&#xff1a;我不是针对你&#xff0c;我只想说在座的各位都是乐色&#xff5e; 注意&#xff0c;它叫Viva&#xff0c;不是…

Java引用的4种类型:强、软、弱、虚

在Java中&#xff0c;引用的概念不仅限于强引用&#xff0c;还包括软引用、弱引用和虚引用&#xff08;也称为幻影引用&#xff09;。这些引用类型主要用于不同的内存管理策略&#xff0c;尤其是在垃圾收集过程中。以下是对这四种引用类型的详细解释&#xff1a; 1. 强引用&am…

GD32实战篇-双向数控BUCK-BOOST-BUCK降压理论基础

本文章基于兆易创新GD32 MCU所提供的2.2.4版本库函数开发 向上代码兼容GD32F450ZGT6中使用 后续项目主要在下面该专栏中发布&#xff1a; https://blog.csdn.net/qq_62316532/category_12608431.html?spm1001.2014.3001.5482 感兴趣的点个关注收藏一下吧! 电机驱动开发可以跳转…

论文解析——FTRANS: Energy-Efficient Acceleration of Transformers using FPGA

作者及发刊详情 Li B , Pandey S , Fang H ,et al.FTRANS: energy-efficient acceleration of transformers using FPGA[J].ACM, 2020.DOI:10.1145/3370748.3406567. 摘要 正文 主要工作贡献 与CPU和GPU在执行Transformer和RoBERTa相比&#xff0c;提出的FTRANS框架获得了…

计算云服务2

第二章 裸金属服务器 什么是裸金属服务器(BMS) 裸金属服务器(Bare Metal Server&#xff0c;BMS)是一款兼具虚拟机弹性和物理机性能的计算类服务为用户以及相关企业提供专属的云上物理服务器&#xff0c;为核心数据库、关键应用系统、高性能计算、大数据等业务提供卓越的计算…

买的Google账号登录,修改辅助邮箱收不到验证码?可能是个简单的错误

这篇文章分享一个案例&#xff0c;购买了谷歌账号以后如何修改辅助邮箱&#xff0c;修改辅助邮箱的一些要点&#xff0c;以及常见的一个错误。 一、案例回放 这个朋友昨天在我的一个视频下面留言说买了谷歌账号以后&#xff0c;想修改辅助邮箱地址&#xff0c;但是输入了辅助…

µCOS-III 任务同步机制-任务信号量

1. 什么是任务信号量 任务信号量是一种用于任务间同步和通信的计数器&#xff0c;通常用于解决任务间的竞争条件和资源共享问题。在C/OS-III中&#xff0c;任务信号量提供了二进制信号量和计数信号量两种类型&#xff1a; 二进制信号量&#xff1a;只能取值0或1&#xff0c;适…

VSCode神仙插件——Codeium (AI编程助手)

1、安装&登录插件 安装过程中会让你登录Codeium账户&#xff0c;可以通过Google账户登录&#xff0c;或者可以注册一个Codeium账户&#xff08;如果没有弹出让你登录账户的界面&#xff0c;可以等安装结束后在右下角找到登录的地方&#xff09; 右下角显示如下图所示&#…