Studying-代码随想录训练营day58| 拓扑排序精讲、dijkstra(朴素版)精讲

news2024/11/16 9:01:15

第58天,拓扑排序和最短路径算法讲解!!💪(ง •_•)ง💪,编程语言:C++

目录

拓扑排序精讲

拓扑排序的背景 

题目:117. 软件构建 (kamacoder.com)

拓扑排序的思路 

模拟过程

有环情况 

写代码

dijkstra(朴素版)精讲

模拟过程

debug方法

如何求路径 

dijkstra与prim算法的区别 

总结 


拓扑排序精讲

文档讲解:代码随想录拓扑排序精讲

拓扑排序的背景 

拓扑排序,虽然称之为排序算法,但是确实经典的图论算法之一。

拓扑排序的应用场景主要是解决存在依赖关系的问题。例如大学排课,先上A课,才能上B课,上了B课才能上C课,上了A课才能上D课,等等一系列这样的依赖顺序。 问给规划出一条完整的上课顺序。又比如我们在做项目安装文件包的时候,经常发现复杂的文件依赖关系, A依赖B,B依赖C,B依赖D,C依赖E等等。

对于简单的依赖关系来说,其实我们一眼就可以看出来。但是对于存在成百上千条依赖关系,甚至存在循环依赖的情况,我们就需要依靠算法来进行解决了。

总结来说:给出一个有向图,把这个有向图转成线性的排序,就叫拓扑排序。

当然拓扑排序也要检测这个有向图是否有环,即存在循环依赖的情况,这种情况是不能做线性排序的。所以拓扑排序也是图论中判断有向无环图的常用方法。

接着我们从题目出发,进行具体分析:

题目:117. 软件构建 (kamacoder.com)

拓扑排序的思路 

拓扑排序更重要的是一个解题的思路,而具体的实现算法,可能是广搜也可能是深搜。但只要能把有向无环图进行线性排序的算法都可以叫做拓扑排序。

我们这里主要讲解卡恩算法(BFS),底层是广度优先搜索的算法。其实现思路以示例为例:

首先我们应该找到的是出发点,显然我们肉眼可以看出出发点是0。但是如果没有图的情况下,我们如何确定出发点呢,这就需要依靠出发点的特征。出发点最重要的特征就是入度为0,也就是没有别的点指向它(在题中也可以理解为,实现节点0不需要任何依赖)。因此我们在拓扑排序的时候,应该优先找入度为0的节点,只有入度为0,它才是出发节点。

接着是拓扑排序的过程,其主要就两步:

  1. 找到入度为0的节点,加入结果集
  2. 将该结点从图中移除。

接着循环以上两步,直至把所有节点移除。(只要没有环,是能够不断找到入度为0的节点的)。

结果集的顺序,就是我们想要的拓扑排序的顺序。(结果集里顺序可能不唯一)

模拟过程

用本题的示例来进行模拟:

1.找到入度为0的节点,加入结果集:

2.将该结点在图中删除:


1.找到入度为0的节点,加入结果集 

这里发现,节点1和节点2入度都是0,选哪个都可以。


1.找到入度为0的节点,加入结果集

节点2和节点3入度都为0,选哪个都行,这里选节点2。

2.将该节点从图中删除

最后3,和4随机选择,并进行入栈即可,答案不为1。

有环情况 

如果这个图是有环的,那么在我们把0加入到结果集里面后,就不再有入度为0的节点了。此时结果集里面也就只有一个元素了。因此如果我们发现结果集里面的元素个数,不等于图中节点的个数,我们就可以认定图中一定有有向环。 这也是拓扑排序判断有向环的方法。

写代码

解题思路理解起来很简单,但代码实现并不容易。

为了每次可以找到所有节点的入度信息,我们要在初始化的时候,把每个节点的入度和每个节点的依赖关系做好统计。

cin >> n >> m;
vector<int> inDegree(n, 0); // 记录每个文件的入度
vector<int> result; // 记录结果
unordered_map<int, vector<int>> umap; // 记录文件依赖关系,这也是邻接表的一种写法

while (m--) {
    cin >> s >> t;
    inDegree[t]++; // t的入度加一
    umap[s].push_back(t); // 记录s指向哪些文件,s->t
}

在遍历入度为0的节点的时候,我们需要用一个队列来存放,因为入度为0的节点不止一个,可能很多节点入度都为0,需要将这些入度为0的节点都放到队列里,依次去处理。

queue<int> que;
for (int i = 0; i < n; i++) {
    // 入度为0的节点,可以作为开头,先加入队列
    if (inDegree[i] == 0) que.push(i);
}

之后我们遍历入度为0的节点,将其放入结果集当中:

while (!que.empty()) {
    int  cur = que.front(); // 当前选中的节点
    que.pop();
    result.push_back(cur);
}

接着有一个非常关键的步骤,将入度为0的节点从图中删除。显然我们不仅仅是要把点从图中去掉这么简单,更重要的是要将与该点有关的边都删掉,而删掉这些边带来最直观的就是对应连接的点的入度会减一!

例如上图,把节点0去掉之后,1,2节点就从入度1变为了入度0。这样节点1和节点2才能作为下一轮选取的节点。

所以我们在代码实现的时候,本质是要将该节点作为出发点所连接的节点的入度减一,这样才好更具入度选择一下个节点,而不用真的在图里把这个节点删掉。这个步骤应该放在遍历队列取出节点的后面。

while (!que.empty()) {
    int  cur = que.front(); // 当前选中的节点
    que.pop();
    result.push_back(cur);
    // 将该节点从图中移除 
    vector<int> files = umap[cur]; //获取cur指向的节点
    if (files.size()) { // 如果cur有指向的节点
        for (int i = 0; i < files.size(); i++) { // 遍历cur指向的节点
            inDegree[files[i]] --; // cur指向的节点入度都做减一操作
            // 如果指向的节点减一之后,入度为0,说明是我们要选取的下一个节点,放入队列。
            // 这是一个不断降低,不断增加入度为0的节点的过程
            if(inDegree[files[i]] == 0) que.push(files[i]); 
        }
    }
}

最后我们可以得到代码:

#include <iostream>
#include <vector>
#include <unordered_map>
#include<queue>
using namespace std;

int main() {
    int n, m;
    cin >> n >> m;
    
    int s, t;
    vector<int> inDegree(n, 0); //保存节点入度,节点从0开始
    unordered_map<int, vector<int>> umap; //使用邻接表的方式,保存依赖关系
    while(m--) {
        cin >> s >> t;
        inDegree[t]++; //是s->t;
        umap[s].push_back(t); //保存路径
    }
    vector<int> result; //保存结果
    //采用广度优先搜索的方法BFS
    queue<int> que;
    //初始化队列,把入度为0的节点,加入到队列当中
    for(int i = 0; i < n; i++) {
        if(inDegree[i] == 0) {
            que.push(i);
        }
    }
    
    while(!que.empty()) {
        //1.取出入度为0的节点,加入结果集中
        int cur = que.front();
        que.pop();
        result.push_back(cur);
        //2.将该点,以及该点的边从图中去掉
        vector<int> files = umap[cur]; //取出cur的连接对象
        for(int i = 0; i < files.size(); i++) {
            inDegree[files[i]]--; //将边删除
            if(inDegree[files[i]] == 0) {
                que.push(files[i]);
            }
        }
    }
    //判断是否有环,就看结果集的个数是不是n
    if(result.size() != n) {
        cout << -1 << endl;
    }
    else {
        for(int i = 0; i < n - 1; i++) {
            cout << result[i] << " ";
        }
        cout << result[n - 1]; //最后一个元素单独打印不留下空格
    }
    return 0;
}

dijkstra(朴素版)精讲

文档讲解:代码随想录dijkstra(朴素版)精讲

题目:47. 参加科学大会(第六期模拟笔试) (kamacoder.com)

本题是标准的求最短路径的问题,理论上来说,我们可以找到所有从起点到终点的路径,然后找到时间花费最短的路径即可,但这样时间复杂度很高。因此我们学习一种求解最短路径的算法Dijkstra算法(迪杰斯特拉算法)。

dijkstra算法的功能:在有权图,且权值非负数,求从起点到其他节点的最短路径。

需要注意两点:

  • dijkstra算法可以同时求起点到所有节点的最短路径
  • 权值不能为负数

以题目为例进行分析:

图中标绿线的部分就是最短路径。事实上dijkstra算法和prim算法的思路非常接近。dijkstra算法同样是贪心的思路,不断寻找距离源点最近的没有访问过的节点。 

我们同样从dijkstra三部曲进行分析:

  1. 第一步,选源点到哪个节点近且该节点未被访问过
  2. 第二步,该最近节点被标记访问过
  3. 第三步,更新非访问节点到源点的距离(即更新minDist数组)

可以发现确实和prim算法非常像,且都有一个同样的数组minDist,这个数组是用来记录每一个节点距离源点的最小距离(源点也即出发点),这是dijkstra算法的核心所在。

接下来我们进行dijkstra算法的解题过程,我们首先讲的是朴素版的dijkstra算法

模拟过程

初始化:首先我们需要初始化两个数组,一个minDist数组,一个visited数组。

minDist数组初始化为int的最大值,因为它记录的是所有节点到源点的最短路径,因此初始化为最大值,才便于后续出现最短路径的时候,进行更新。

visited数组初始化为false,表示是否访问过。

接着我们需要把原点的距离设为0,意味着原点到自己的距离为0,minDist[1] = 0(我们默认节点是从1开始的,节点0没有意义,不做处理)

然后我们进行dijkstra三部曲:

1、选源点到哪个节点最近且该节点未被访问过:当前应选取节点1,距离为0,且未被访问过。

2、标记该节点为访问过:把节点1标记为访问过,visited[1] = true;

3、更新非访问节点到源点的距离(即更新minDist数组):依据当前遍历的节点进行更新。

此次更新了两个距离:

  • 源点到节点2的最短距离为1,小于原minDist[2]的数值max,更新minDist[2] = 1
  • 源点到节点3的最短距离为4,小于原minDist[3]的数值max,更新minDist[4] = 4

这里我们要注意,不能少了比较原先数值的步骤。因为这个值是会发生改变的,它表示源点到当前点的距离,随着我们遍历的过程中,可能会出现距离更小的路径进行覆盖。

然后进行下一轮dijkstra三部曲:

1、选源点到哪个节点近且该节点未被访问过:未访问过的节点中,源点到节点2距离最近,选节点2。

2、该最近节点被标记访问过:节点2被标记访问过

3、更新非访问节点到源点的距离(即更新minDist数组):依据当前遍历的节点进行更新。

这个过程可以理解为 源点(节点1)通过 已经计算过的节点(节点2)可以链接到的节点有节点3,节点4和节点6。这个地方我们对节点3的值进行了覆盖,也是这个原因,因为还有一条路径是能够到达节点3的。

  • 源点到节点6的最短距离为minDist[2] + 4 = 5,小于原minDist[6]的数值max,更新minDist[6] = 5
  • 源点到节点3的最短距离为minDist[2] + 2 = 3,小于原minDist[3]的数值4,更新minDist[3] = 3
  • 源点到节点4的最短距离为minDist[2] + 5 = 6,小于原minDist[4]的数值max,更新minDist[4] = 6

注意我们是依靠节点2的距离来进行更新的,因为节点2的距离我们已经确定了。

最后不断的重复上述过程:

将所有节点都加入,在本题中我们最后加入节点7,就不用更新minDist数组了,因为所有的visited都标记为true了(节点0不作考虑)。

最后我们得到答案12。

#include <iostream>
#include <vector>
#include <climits> //包含INT_MAX等类型最大值最小值
using namespace std;
int main() {
    int n, m, p1, p2, val;
    cin >> n >> m;
    vector<vector<int>> grid(n + 1, vector<int>(n + 1, INT_MAX));
    for(int i = 0; i < m; i++){
        cin >> p1 >> p2 >> val;
        grid[p1][p2] = val;
    }
    int start = 1;
    int end = n;
    // 存储从源点到每个节点的最短距离
    std::vector<int> minDist(n + 1, INT_MAX);
    // 记录顶点是否被访问过
    std::vector<bool> visited(n + 1, false);
    minDist[start] = 0;  // 起始点到自身的距离为0

    for (int i = 1; i <= n; i++) { // 遍历所有节点

        int minVal = INT_MAX;
        int cur = 1;
        // 1、选距离源点最近且未访问过的节点
        for (int v = 1; v <= n; ++v) {
            if (!visited[v] && minDist[v] < minVal) {
                minVal = minDist[v];
                cur = v;
            }
        }
        visited[cur] = true;  // 2、标记该节点已被访问
        // 3、第三步,更新非访问节点到源点的距离(即更新minDist数组)
        for (int v = 1; v <= n; v++) {
            if (!visited[v] && grid[cur][v] != INT_MAX && minDist[cur] + grid[cur][v] < minDist[v]) {
                minDist[v] = minDist[cur] + grid[cur][v];
            }
        }
    }
    if (minDist[end] == INT_MAX) cout << -1 << endl; // 不能到达终点
    else cout << minDist[end] << endl; // 到达终点最短路径
    return 0;
}

debug方法

一般程序debug的方法就是打印日志,对于本题来说就是打印minDist数组,来查看哪里出了问题,minDist数组的变化是否符合预期。

代码:可以这么写代码:

#include <iostream>
#include <vector>
#include <climits>
using namespace std;
int main() {
    int n, m, p1, p2, val;
    cin >> n >> m;
    vector<vector<int>> grid(n + 1, vector<int>(n + 1, INT_MAX));
    for(int i = 0; i < m; i++){
        cin >> p1 >> p2 >> val;
        grid[p1][p2] = val;
    }
    int start = 1;
    int end = n;
    std::vector<int> minDist(n + 1, INT_MAX);
    std::vector<bool> visited(n + 1, false);
    minDist[start] = 0;
    for (int i = 1; i <= n; i++) {
        int minVal = INT_MAX;
        int cur = 1;
        for (int v = 1; v <= n; ++v) {
            if (!visited[v] && minDist[v] < minVal) {
                minVal = minDist[v];
                cur = v;
            }
        }
        visited[cur] = true;
        for (int v = 1; v <= n; v++) {
            if (!visited[v] && grid[cur][v] != INT_MAX && minDist[cur] + grid[cur][v] < minDist[v]) {
                minDist[v] = minDist[cur] + grid[cur][v];
            }
        }
        // 打印日志:
        cout << "select:" << cur << endl;
        for (int v = 1; v <= n; v++) cout <<  v << ":" << minDist[v] << " ";
        cout << endl << endl;;
    }
    if (minDist[end] == INT_MAX) cout << -1 << endl;
    else cout << minDist[end] << endl;
    return 0;
}

//运行结果:
select:1
1:0 2:1 3:4 4:2147483647 5:2147483647 6:2147483647 7:2147483647

select:2
1:0 2:1 3:3 4:6 5:2147483647 6:5 7:2147483647

select:3
1:0 2:1 3:3 4:5 5:2147483647 6:5 7:2147483647

select:4
1:0 2:1 3:3 4:5 5:8 6:5 7:2147483647

select:6
1:0 2:1 3:3 4:5 5:8 6:5 7:14

select:5
1:0 2:1 3:3 4:5 5:8 6:5 7:12

select:7
1:0 2:1 3:3 4:5 5:8 6:5 7:12

12

如何求路径 

本题打印路径的方式和prim算法中的方式是一样的,同样是加入在minDist数组更新的过程当中即可。

代码:

vector<int> parent(n + 1, -1);

for (int v = 1; v <= n; v++) {
    if (!visited[v] && grid[cur][v] != INT_MAX && minDist[cur] + grid[cur][v] < minDist[v]) {
        minDist[v] = minDist[cur] + grid[cur][v];
        parent[v] = cur; // 记录边
    }
}

dijkstra与prim算法的区别 

可以发现dijkstra算法和prim算法十分相似,唯一的区别在于三部曲的第三步,也就是更新minDist数组。

prim是求 非访问节点到最小生成树的最小距离,而 dijkstra是求非访问节点到源点的最小距离

prim 更新 minDist数组的写法:

for (int j = 1; j <= v; j++) {
    if (!isInTree[j] && grid[cur][j] < minDist[j]) {
        minDist[j] = grid[cur][j];
    }
}

因为 minDist表示节点到最小生成树的最小距离,所以 新节点cur的加入,只需要使用grid[cur][j] ,grid[cur][j] 就表示 cur 加入生成树后,生成树到节点j 的距离。

dijkstra 更新 minDist数组的写法: 

for (int v = 1; v <= n; v++) {
    if (!visited[v] && grid[cur][v] != INT_MAX && minDist[cur] + grid[cur][v] < minDist[v]) {
        minDist[v] = minDist[cur] + grid[cur][v];
    }
}

因为 minDist表示 节点到源点的最小距离,所以新节点cur的加入,需要使用源点到cur的距离 (minDist[cur]) + cur 到节点v的距离(grid[cur][v]) 才是 源点到节点v的距离。

由于这个特点,prim算法是可以有负权值的,因为prim算法只需要将节点以最小权值和链接到一起,不涉及到单一路径。但是dijkstra算法是不可以的。


总结 

今天又了解了两个算法:拓扑排序算法和dijkstra(朴素版)算法

拓扑排序算法解决的是:有向无环图转换为线性排序的方法,它还能用于解决判断有向图是否有环的情况。

dijkstra算法则是解决:最短路径的问题,要理解它的核心minDist数组,同时该算法是不能够解决存在负权值问题的。还要理解它与prim算法的区别,在于minDist数组的更新的不同之处,关键在于prim算法要的是节点到生成树的最小距离,而dijkstra算法是节点到源点的距离。

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

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

相关文章

基于K8S配置Jenkins主从节点实例

基于K8S配置Jenkins主从节点实例 1.配置Jenkins主节点1.确认 Jenkins Pod 名称2.进入 Jenkins Pod&#xff1a;3.生成SSH密钥对4.将公钥复制到目标节点&#xff1a; 2.配置Jenkins的node1节点1.安装java2.配置 Jenkins node1节点的 Java 路径1.添加Java环境变量2.生效Java环境变…

如何根据 EcoVadis 审核的评分标准改进企业社会责任表现?

要根据 EcoVadis 审核的评分标准改进企业社会责任表现&#xff0c;可以采取以下步骤&#xff1a; ​深入研究评分标准 详细了解每个主题&#xff08;环境、劳工与人权、商业道德、可持续采购&#xff09;及其子主题的具体要求和关键指标。 进行自我评估 对照评分标准&#xf…

未授权访问漏洞(非重点 上)

1.MongoDB 1.在fofo里搜索 port"27017 2.Memcached 1.用fofa语法 port"11211" 搜索资产 2.使用 telnet 连接 3.Zookeeper 1.在 fofa 中使用 port"2181" 获取资源 2.在 kali 中使用 echo envinc ip 2181 测试是否存在漏洞 4.Elasticsearch 1.在 …

⌈ 传知代码 ⌋ MSA+抑郁症模型总结(二)

&#x1f49b;前情提要&#x1f49b; 本文是传知代码平台中的相关前沿知识与技术的分享~ 接下来我们即将进入一个全新的空间&#xff0c;对技术有一个全新的视角~ 本文所涉及所有资源均在传知代码平台可获取 以下的内容一定会让你对AI 赋能时代有一个颠覆性的认识哦&#x…

Java语言程序设计——篇十一(5)

&#x1f33f;&#x1f33f;&#x1f33f;跟随博主脚步&#xff0c;从这里开始→博主主页&#x1f33f;&#x1f33f;&#x1f33f; 欢迎大家&#xff1a;这里是我的学习笔记、总结知识的地方&#xff0c;喜欢的话请三连&#xff0c;有问题可以私信&#x1f333;&#x1f333;&…

基于arcpro3.0.2的北斗网格生成简介

基于arcpro3.0.2的北斗网格生成简介 采用2000坐标系、可基于行政区范围 软件可生成第一级到第十级北斗网格经纬跨度 等分 约赤道处距离 第一级 6X4度 60 和A~V 660 km 第二级 30X30分 12X8 …

sql注入第一关和第二关

第一关&#xff1a; 输入?id1 正常 输入?id1 报错 .0 输入?id1-- 正常 判断他是字符型注入 闭合方式是: ?id1 and 12 union select 1,2,group_concat(schema_name) from information_schema.schemata-- 联合注入 爆出库&#xff1a;ctfshow,ctftraining,information_…

nvidia系列教程-AGX-Orin系统刷机及备份

目录 前言 一、准备工作 二、AGX Orin 系统刷机步骤 三、AGX Orin 系统备份 总结 前言 NVIDIA AGX Orin 是一款高性能的嵌入式计算平台&#xff0c;专为边缘计算和 AI 应用而设计。为了确保系统的稳定性和适应不同的应用场景&#xff0c;用户可能需要对 AGX Orin 进行系统刷…

SpringBoot集成阿里百炼大模型 原子的学习日记Day01

文章目录 概要下一章SpringBoot集成阿里百炼大模型&#xff08;多轮对话&#xff09; 原子的学习日记Day02 整体架构流程技术名词解释集成步骤1&#xff0c;选择大模型以及获取自己的api-key&#xff08;前面还有一步开通服务就没有展示啦&#xff01;&#xff09;2&#xff0c…

2024.8.05(glibc的安装及MySQL的安全用户角色权限)

一、glibc的安装 1、清空/etc目录下的my.cnf [rootlocalhost ~]# ls -l /etc/my.cnf -rw-r--r--. 1 root root 570 6月 8 2017 /etc/my.cnf [rootlocalhost ~]# rm -rf /etc/my.cnf 2、删除mariadb [rootlocalhost ~]# yum -y remove mariadb [rootlocalhost ~]# find / -na…

wps 最新 2019 专业版 下载安装教程,解锁全部功能,免费领取

文章目录 前言软件介绍软件下载安装步骤激活步骤小福利&#xff08;安卓APP&#xff09;软件介绍软件下载安装步骤 前言 本篇文章主要针对WPS2019专业版的安装下载进行详细讲解&#xff0c;软件已激活&#xff0c;可放心使用&#xff1b;并且可以进行账号登录&#xff0c;进行…

Router路由的使用

目录 一.Vue Router的使用&#xff1a; 二.使用vue-router来实现登录页面与主页面展示效果&#xff1a; 1.创建 index.js &#xff1a; 2.在 main.js 导入创建的路由器&#xff1a; 3.在App.vue声明标签&#xff1a; 三.子路由的使用&#xff1a; 1.添加五个组件 2.配置…

光线追踪(纹理映射)

最近在跟着ray trace in one week来学习光线追踪&#xff08;很多概念茅塞顿开&#xff09;做到一半想着记录一下&#xff08;比较随心&#xff09;上面是之前的效果。ray trace in one week Texture Coordinates for Spheres&#xff08;球体纹理坐标&#xff09; u, v 纹理…

K-means聚类算法原理解析

度量最小距离 对于 K-means 聚类算法而言&#xff0c;找到质心是一项既核心又重要的任务&#xff0c;找到质心才可以划分出距离质心最近样本点。从数学角度来讲就是让簇内样本点到达各自质心的距离总和最小。通过数学定义&#xff0c;我们将“质心”具象化&#xff0c;既然要使…

使用SpringBoot+Vue3开发项目(2)---- 设计文章分类的相关接口及页面

目录 一.所用技术栈&#xff1a; 二.后端开发&#xff1a; 1.文章分类列表渲染&#xff1a; 2.新增文章分类&#xff1a; 3.编辑文章分类&#xff1a; 4.删除文章分类 &#xff1a; 5.完整三层架构后端代码&#xff1a; &#xff08;1&#xff09;Controller层&#xff1a…

学习大数据DAY31 Python基础语法4和基于Python中的MySQL 编程

目录 Python 库 模块 time&datetime 库 连接 MySQL 操作 结构操作 数据增删改操作 数据查询操作 上机练习 7 面向对象 OOP 封装 继承 三层架构---面向对象思想模型层 数据层 业务逻辑显示层 上机练习 8 三层架构开发豆瓣网 关于我对 AI 写代码的看法&#xf…

大模型技术在企业应用中的实践与优化

【导读】大模型技术更新层出不穷&#xff0c;但对于众多企业及开发者而言&#xff0c;更为关键的命题则是如何进行应用落地&#xff0c;实现真正的智能化转型。本文系统且深入地探讨了大模型在企业应用中的关键环节和技术要点。从构建高质量的专属数据集、选择适宜的微调策略&a…

天线增益测试方法之射频器件S参数测试软件

天线增益的精确测量对于优化无线信号传输至关重要。NSAT-1000射频器件S参数测试软件作为针对S参数的测试设备&#xff0c;大幅提高了测试精度和效率。本文将为大家介绍该软件在天线增益测试方面的具体操作流程。 一、准备工作 在测试天线增益之前&#xff0c;需要准备好测试软件…

【启明智显分享】Model3A 7寸TFT触摸彩屏智能电压力锅解决方案

随着智能家居市场的快速发展&#xff0c;电压力锅作为厨房电器的代表之一&#xff0c;正逐步向智能化、高端化转型。为了进一步提升用户体验&#xff0c;增强产品竞争力&#xff0c;我们推出基于Model3A 7寸触摸彩屏电压力锅解决方案。该方案旨在通过Model3A芯片的强大性能与7寸…

24/8/5算法笔记 BGD,SGD,MGD梯度下降

今日对比不同梯度下降的代码 1.BGD大批量梯度下降(一元一次&#xff09; 首先导入库 import numpy as npimport matplotlib.pyplot as plt 随机生成线性回归函数 Xnp.random.rand(100,1)w,bnp.random.randint(1,10,size2)#增加噪声&#xff0c;更像真实数据 #numoy广播机制…