基于QT(C++)实现(图形界面)校园导览系统
校园导览系统
一、任务描述
大学校园充满着忙忙碌碌的学生和老师们,但是有时候用户宝贵的时间会被复杂的道路和愈来愈多的建筑物的阻碍而浪费,为了不让同学们在自己的目的地的寻路过程中花费更多的时间,我们着手开发这样一款校园导览系统。
对于一个导航系统来说,使用起来方便直观,各地点之间更换快捷,规划出来的路线清晰准确,这些才是客户最核心的需求。在生活节奏愈来愈快的现代社会,“即搜即出”是提升用户体验的一个重点所在。
在位置的选取上,用户们在使用时通常会有如下两大类搜索习惯,一是该用户清楚地知道自己所需要上课的教室或者导员所在的办公室,那就可以只将该用户引导到对应的教学楼或者办公楼,如“教学楼N楼”、“东配楼”等,又或者对于有运动需求的同学将其引导到对应的体育场馆,如“体育馆”“游泳馆”“篮球场”等,这些我们只需要提供物理位置即可;二是该用户希望通过搜索某类型服务设施来查询周边存在的该类型设施,并从结果中选择自己心仪的那一个,如用户搜索“超市,系统将展示“地下超市”和“小麦铺超市”两处地点,并由该用户自行选择目的地。并且对于部分需要跨校区上课的同学或者需要往返于两个校区之间的教师,我们就需要对其提供相对应的交通方式,例如校车或者地铁等公共交通方式。
正如高德百度有红绿灯较少和转向较少等多种导航逻辑,校园导览系统也可以根据同学们的日常习惯提供不同的导航方式,对于时间紧急的同学可以选择最短时间策略,而对于不那么急,不想走太多的路的同学可以选择最短路径。如果这个时候刚好需要帮忙送一个论文或者其他重要东西的时候,也可以找出当前位置到最终目的地并通过某点的最短路径。不可否认的是校园内的基础设施建设条件足以支撑部分骑单车同学来通行,这时候就需要将自行车用户和步行用户区分开来,以免发生意外。由于校车的特殊性,对于校车可能经过的路径也需要进行一定的提示,在用户使用时加以提醒,避免意外的发生。
生活在这里,我们总会对这里有着自己的感情,校园内的学长学姐对某些建筑可能会有一些流传很久的通俗的称呼,那对于新入校的同学可能会不明就里,对于这种情况我们也做出了自己的应对,通过问询学长学姐及日常听闻,我们将这些通俗的称呼和它们所指向的建筑物做了映射,即使搜索这些通俗的称呼也是可以导航到相关建筑的。
既然是校园导览系统,那就不置可否地需要一个实时向用户介绍周边建筑物名称及用途的功能,那也就需要我们不断地读取并记录用户的实时位置,并以此为依据展现出周边建筑物的名称用途,能够让用户更好的游览校园。
二、需求分析
对校园导览系统要求提供以下方面的服务:
- 地点查询,负责查询相关地点信息和搜索周边一定范围内建筑和服务设施;
- 导航系统,负责进行两点之间不同策略的导航,或途径某点的多点导航;
- 模拟时钟,负责系统的时间推进,模拟导航的推进;
- 地图实现,负责实现两个校区的地图;
- 日志更新,负责记录用户的状态变化和键入信息。
2.1 地点查询
在地点查询方面应填写的用户需求描述如下:
地点检索
① 模糊搜索:允许用户输入的搜索信息与实际的地点名称存在一定差异,并能够根据用户输入的搜索信息匹配最接近的地点名称,例如:输入“教学楼”,可以匹配“教学楼N楼”“教学楼S楼”等。
② 逻辑名与物理名对应:同时允许用户搜索部分建筑的通俗称呼,例如:用户搜索“学一”,可以匹配“学生食堂”等。
查询信息
① 非即时查询:在未进行导航时,可以搜索并查询某一地点的位置及相关信息,并在图形化展示界面上显示出来。
② 即时查询:在进行导航过程中也可以随时暂停并实时查询所处的位置或同时展示其相关信息,并在图形化界面上展示出来。
显示一定范围内建筑信息
① 非即时查询:在非导航状态下,可以根据用户查询的某一地点以及用户设置搜索的最大半径来搜索附近的建筑和服务设施,并显示其相关信息。
② 即时查询:在导航过程中可以实时暂停导航功能,基于用户的当前位置和用户设置的最大半径来搜索附近的建筑和服务设施,并显示其相关信息。
2.2 导航系统
在地点管理方面应填写的用户需求描述如下:
两点导航
可以根据用户当前需求选择如下不同的导航逻辑:
① 最短路径:根据用户输入的起点和终点,找出两点之间距离最短的路线并在图形化界面上展示出来。
② 最短时间:根据用户输入的起点和终点,检索两点之间可能的路径,并根据距离/实际速度得出各条路径所需要的时间,将各路径时间进行比对找出最短时间的路径,在图形化界面上加以展示。(实际速度=通行效率*理想速率)
③ 含校内交通工具的最短时间:用户输入起点和终点后可以在校园内随地找到可骑行的单车,且只能在可骑行路段行进,此时根据距离/实际速度得出各条路径所需时间,将各时间进行对比即可得出最短时间的路径,并在图形化界面上展示出来。(实际速度=通行效率*理想速率)
多点导航
用户在使用过程中可能存在希望途径多点再到达目的地的情况:
途径多点的最短路径:用户输入起点和终点,并输入希望途径的地点,找出从起点出发途径途径点到达目的地的最短路径,并在图形化界面面展示该路径。
课表导航
可以根据学生的课表结合当前时间进行导航:导览系统根据当前时间在当前登录用户的课程表文件内进行检索,自动匹配当前时间的下一节要上的课程信息,并自动找出该逻辑位置对应的相关地点开始导航,在图形化界面上进行展示。
2.3 模拟时钟
默认时钟比例
① 用户在校园内使用该系统进行导航时,系统内部时钟与外界时间存在一定比例,例如,系统内行进一秒的路程,相当于在外界实际行进三十秒。
② 另外在校际进行变化时,时钟比例不同于校园内进行导航时,但仍存在着和外界实际时间变化的比例。例如,系统内行进一秒的路程,相当于外界实际行进十分钟。
自行设定比例
除了默认设定的时钟比例外,用户可以根据自身当前需求来自行设定一个时钟比例,例如由原来的1:30可以更改为1:60。
模拟导航
用户在使用导航功能时,代表着用户的点可以随着系统模拟时间的流动而改变位置,以此来体现模拟导航的过程。
随开随停
用户在使用本系统进行导航的过程中,可以随时停止当前系统内的时钟并进行相关的一系列操作。
2.4 地图实现
通过格式化的方式存储地图信息:
① 对地图进行标点并编号,格式化存储每个点的坐标及名称等相关信息;
② 格式化存储每个点周围的邻接信息;
③ 构建逻辑名称与物理名称的映射表。
2.5 日志更新
① 在用户键入信息或者点击某按钮时,记录该用户的当此活动;
② 当用户状态发生改变时,实时记录其状态的变化。
③ 可以根据时间查询用户该时间的状态及活动信息。
三、概要设计
3.1 开发环境
在校园导览系统的设计与开发上,采用C++语言进行算法部分的编写工作,基于QT进行图形化界面的开发,并在搭载Windows10和IOS的计算机上进行测试工作。
本次实验的编写环境可分为硬件和软件环境,它们分别如下所示:
① 硬件环境:搭载了Windows10和IOS操作系统的笔记本电脑;
② 软件环境:Xcode VS code QT 5.9.9
3.2 总体结构
根据团队对于校园导览系统的需求分析,我们将系统构建为如下结构:
3.3 模块划分
- Login模块:登录模块,提供用户登录窗口,并检测账号密码是否存在于本地文件且是否对应。
- Account模块:账户模块,用于存储用户的账号密码信息与课程时间表信息。
- Mainwindow模块:程序主窗口模块,串联所有核心算法模块,提供用户与系统的交互界面,并以图形化的方式输出。
- Clock模块:时钟模块,提供模拟导航系统的时间。
- Current模块:当前状态模块,提供用户当前状态信息及周边信息。
- Dijkstra模块:迪杰斯特拉算法模块,提供两点间的最短路径的算法,并且结合BFS实现搜索一定范围内的所有顶点及其路径的功能
- Ant模块:蚁群算法模块,提供途径多点的最短路径算法。
- LogSearch模块:日志查询模块,提供查询日志文件相关信息的功能。
四、数据结构说明
注:省略了所有的构造函数、析构函数、setter(set函数)与所有的getter(get函数)
4.1 Vertex——顶点
数据结构定义
classVertex
class Vertex
{
private:int no;qreal x;qreal y;QString description;bool isSpot;
};
成员变量意义
int no:顶点的编号;
qreal x:该顶点的x坐标;
qreal y:该顶点的y坐标;
QString description:该顶点的相关描述信息;
bool isSpot:该点是否为有实际意义的节点的标志。
4.2 Edge——有向边
数据结构定义
class Edge
{
private:qreal distance;qreal crowdness; qreal velocity;
};
成员变量意义
qreal distance:该边的总距离
qreal crowdness:该边的拥挤度
qreal velocity:该边可允许通行的最大速度
4.3 current——当前状态
数据结构定义
class Current
{
public: bool inShahe();bool inBenbu();bool inBetween();private:qreal x;qreal y;qreal velocity;Priority priority;QVector<QMap<int,Edge*>> adj_map;QVector<Vertex*> vertices;QVector<Vertex*> path; Vertex* last_ver;int next_ver_index;qreal total_dis; qreal current_dis; qreal total_pass; qreal current_pass; qreal total_time; qreal total_pass_time;
};
成员变量意义
qreal x:当前位置的x坐标;
qreal y:当前位置的y坐标;
qreal velocity:当前所在边的允许通行速度;
Priority priority:当前的选择策略(优先度);
QVector<QMap<int,Edge*>> adj_map:邻接表;
QVector<Vertex*> vertices:顶点信息表;
QVector<Vertex*> path:途径数组,包括起点;
Vertex* last_ver:上一顶点,当current_pass==0时,last_ver为当前节点;
int next_ver_index:下一个顶点在途径数组中的下标;
qreal total_dis:路径总距离;
qreal current_dis:当前边的长度;
qreal total_pass:总已走距离;
qreal current_pass:当前已走距离(距离上一个节点的距离);
qreal total_time:总所需时间;
qreal total_pass_time:总已经历的时间。
成员函数说明
bool inShahe():判断当前是否在沙河校区;
bool inBenbu():判断当前是否在本部校区;
bool inBetween():判断当前是否在校外。
4.4 Log——日志记录
数据结构定义
class Log
{
public:void readLog();void writeLog();void addItem(Clock clk, QString op, QString inf);void popItem();QVector<QString> search(Clock clk_high, Clock clk_low, const QString &ope) const;private:typedef struct ITEM {Clock clk;QString ope;QString info;} item;QVector<item> log_list;
};
成员变量意义
typedef struct ITEM {Clock clk:日志记录的时间;QString ope:日志记录的操作信息;QString info:日志记录的解释性信息;
} item:日志记录的结构体;QVector<item> log_list:日志记录数组,即日志记录
成员函数说明
void readLog():读日志文件;
void writeLog();写日志文件;
void addItem(Clock clk, QString op, QString inf):
向日志记录数组中写入一条记录;
void popItem():删除日志记录数组中的最后一条记录;
QVector search(Clock clk_high,
Clock clk_low, const QString &ope) const:
搜索一定时间范围内的所有日志记录,并格式化为字符串返回。
4.5 Account——账号
数据结构定义
class Account
{
public:Course gotoclass(Clock clock) const;private:QString id;QString password;QVector<Course> courses;
};
成员变量意义
QString id:账户名;
QString password:密码;
QVector courses:课程表。
成员函数说明
Course gotoclass(Clock clock) const:
对应当前时间找到正在进行的课程并返回。
4.6 Clock——定时器
数据结构定义
class Clock
{
public:QString get_clock_str();void update_clock();int difSec(const Clock &clock);bool operator>=(const Clock &clock);Clock operator-(int h);private:int sec;int min;int hour;int wday;
};
成员变量意义
int sec:秒;
int min:分;
int hour:时;
int wday:星期(0-6,0表示周日)。
成员函数说明
QString get_clock_str():返回化为格式化字符串的时钟;
void update_clock():时钟更新,+1s;
int difSec(const Clock &clock):求两时钟相差的秒数;
bool operator>=(const Clock &clock):
重载运算符>=:用于判断时间的大于等于关系;
Clock operator-(int h):
重载运算符-:用于计算时间减去传入的小时数后得到的时间。
4.7 Course——课程
数据结构定义
class Course
{
private:QString course_name;Clock begin_time;Clock end_time;int classroom;
};
成员变量意义
QString course_name:课程名;
Clock begin_time:课程开始时间;
Clock end_time:课程结束时间;
int classroom:教室对应的顶点编号。
4.8 adj_map——邻接表
数据结构定义
QVector<QMap<int,Edge*>> adj_map;
成员变量意义
QVector的下标:顶点编号;
QMap的key:邻接点的序号;
QMap的value:顶点到Key的有向边;
4.9 vertices——顶点数组
数据结构定义
QVector<Vertex*> vertices;
4.10 logic2phy——逻辑名到顶点的映射表
数据结构定义
QMap<QString,QVector<int>> logic2phy;
成员变量意义
QMap的key:逻辑名;
QMap的value:对应的物理名的顶点编号数组;
QVector的值:物理名的顶点编号。
五、详细设计
5.1 Login模块
登录模块,提供用户登录窗口,并检测账号密码是否存在于本地文件且是否对应。
主要函数及功能如下:
函数原型 | 功能 |
voidon_pbt_login_clicked(); | 登录按钮对应的槽函数,检查用户输入的账号密码与文件是否对应,若对应则关闭窗口返回accepted,不对应则弹出提示框。 |
boolcheckLogin(constQString&id,constQString&pwd); | 参数分别为用户输入的账号和密码,检查账号是否存在于文件中,账号密码是否对应。 |
voidon_pbt_exit_clicked(); | 退出按钮槽函数,关闭窗口,返回rejected。 |
voidreadAccounts();; | 读取文件中的账号密码及课程表信息。 |
5.2 Account模块
账户模块,用于存储用户的账号密码信息与课程时间表信息。
主要函数及功能如下:
函数原型 | 功能 |
Coursegotoclass(Clockclock); | 参数为当前时间,结合课表信息判断当前是否有课,若有课返回当前在上的课,若没课返回-1。 |
5.3 Mainwindow模块
程序主窗口模块,串联所有核心算法模块,提供用户与系统的交互界面,并以图形化的方式输出。
主要函数及功能如下:
函数原型 | 功能 |
voidinitAction() | 初始化工具栏按钮 |
voidinitToolBar() | 初始化工具栏 |
voidinitTimer() | 初始化定时器 |
voidreadAdj() | 读入地图,存入邻接表 |
voidreadVer() | 读入节点信息表 |
voidreadL2P() | 读入逻辑名到物理名映射表 |
voidreadShuttleSchedule() | 读入班车时刻表 |
voidcurrentOutput() | 输出当前状态信息 |
boolinShahe(intspot) | 判断传入的点是否在沙河校区 |
boolinBenbu(intspot) | 判断传入的点是否在本部校区 |
boolinBetween(intspot) | 判断传入的点是否在两校区之间 |
voidsearchNear() | 搜索附近一定范围的建筑并提供行进路线、距离和时间 |
voidsearchSpot() | 搜索单个建筑并提供行进路线、距离和时间 |
voidfindPath() | 两点寻路函数 |
voidantFind() | 多点寻路函数 |
voidswitchShahe() | 切换至沙河地图 |
voidswitchBetween() | 切换至校外地图 |
voidswitchBenbu() | 切换至本部地图 |
voidsetTime() | 设置时钟加速的结束时间 |
voiddrawPath(constQVector<Vertex*>;&path,boolcurrentIn) | 绘制整条路径,参数分别为要绘制的路径、是否将当前位置绘入路线的标志 |
voidmove() | 更新当前状态并画出行进路线,若跨校区则进行地图切换 |
voidclear() | 擦除地图上的所有路线 |
voidsetCurrent(constQVector<Vertex*>&path,qrealdis,qrealtime,Prioritypr) | 设置当前状态,传入的参数分别为导航路径、距离、时间和优先度 |
voidsetCurrentToVer(intno) | 将当前状态移动至指定的顶点 |
voidupdateTime() | 系统时间+1s并显示在文本框中 |
intgetFirstShuttleTime(qrealt) | 参数为到达班车上车点所需的秒数,返回到达上车点后还需要的等车秒数 |
intgetFirstBusTime(qrealt) | 参数为到达班车上车点所需的秒数,返回到达上车点后还需要的等车秒数 |
voidfuzzySearch(constQString&str) | 参数为输入的字符串,执行模糊搜索,将搜索结果记录在数组中,并依此动态创建多个选择按钮 |
voidmouseDoubleClickEvent;(QMouseEvent*e) | 鼠标双击事件,在界面中显示当前点击顶点的相关信息 |
voidsetVelocity(intx) | 设置速度函数 |
voidclock_slot() | 定时器超时执行的槽函数,包括currentOutput、updateTime、move等。 |
voidcancel() | 清除右侧创建的按钮 |
voidreenter() | 重新输入函数,清除右侧按钮,清除文本输入框 |
voidchoose_findpath_time() | 选择最短时间路线 |
voidchoose_findpath_dis() | 选择最短距离路线 |
voidchoose_searchnear(inti) | 从搜索附近的所有结果中选择一个作为终点 |
voidchoose_search_fzs(inti) | 从搜索地点文本框的模糊搜索的所有结果中选择一个作为文本 |
voidchoose_fp_begin_fzs(inti) | 从两点寻路起点文本框的模糊搜索的所有结果中选择一个作为文本 |
voidchoose_fp_end_fzs(inti) | 从两点寻路终点文本框的模糊搜索的所有结果中选择一个作为文本 |
voidchoose_ant_fzs(inti) | 从多点寻路文本框的模糊搜索的所有结果中选择一个作为文本 |
voidantfindNext() | 继续输入多点寻路的下一个途经地点 |
voidantfindStart() | 多点寻路开始导航 |
voidantfindStartDis() | 选择多点寻路的最短距离路线 |
voidantfindStartTime() | 选择多点寻路的最短时间路线 |
voidstopTime() | 暂停系统时钟 |
voidlogSearch() | 打开日志搜索窗口 |
voidgotoClass() | 根据当前时间和用户课程表,导航去教室 |
voidsearch_fzs(constQString&str) | 对搜索地点文本框中的内容进行模糊搜索 |
voidfindpath_begin_fzs(constQString&str) | 对两点寻路起点文本框中的内容进行模糊搜索 |
voidfindpath_end_fzs(constQString;&str) | 对两点寻路终点文本框中的内容进行模糊搜索 |
voidantfind_fzs(constQString&str) | 对多点寻路文本框中的内容进行模糊搜索 |
算法分析:
导航线路推进:
用求得的路径数组、距离、时间等信息设置当前状态,表示开始导航。在clock_slot中调用的move函数中,程序计算经过系统的1s后前进的距离,更改当前状态中的current_pass及上一顶点、下一顶点等信息。同时将Qt的painter_path连线到当前状态的坐标。具体推进伪代码如下:
Double t = 1;
double v = 当前速度*所在边的拥挤度;
Double d = t*v;
While(当前状态加上d足以到达下一顶点) {If(下一顶点是当前路径终点) {更改当前状态到终点(坐标、已走距离/时间、上一/下一节点、速度=0等)}Else {t -= 走到下一节点所需时间;更改当前状态到下一顶点(坐标、已走距离/时间、上一/下一节点、当前边距离等);v = 所在新边的拥挤度*当前速度;d = v * t;绘制路线(连线到当前顶点);}}//whileIf(没到终点) {更改当前状态到下一顶点(坐标、已走距离/时间、上一/下一节点、当前边距离等);}绘制路线(连线到当前坐标);
两点寻路跨校区导航:
由于跨校区校外路段及班车、公交的特殊性,采取分段导航的策略。如果在寻路时发现起止点不在同一校区,即设置跨校区cross标志为1,并进行3段寻路(以公交为例):第一段从起点到当前校区公交上车点;第二段为设定好的公交路线;第三段从目的校区公交上车点到终点。
开始导航后若当前状态到达第一段路线终点,系统自动用第二段路线设置当前状态,到达第二段终点时同理。
途经多点导航:
由于途经多点导航与传统的旅行商问题略有区别:并非遍历所有顶点也并非需要回到起点,故蚁群算法有所改动。
不能以原始邻接表adj_map作为蚁群算法的邻接表,会出现两顶点不连通的情况。故应二重循环遍历途经顶点数组,两两之间使用dijkstra算法生成有向最短路径,并存于抽象完全图QMap<int,QMap<int,Dijkstra>> abmap中(abmap[i][j]存储从顶点i到顶点j的最短路径、距离、时间)。通过将蚁群算法使用的邻接图设为此abmap,问题就解决了。但是生成该abmap的时间复杂度是O(n^4),效率略低,不过总体相比旅行商问题的指数级是十分优秀的,并且准确度较高。
途经多点跨校区:
类似的,多点寻路也会面临跨校区的问题。解决方法也是分段寻路,以下以公交为例。
首先将途经顶点数组分为两个:沙河顶点数组和本部顶点数组。然后将沙河公交上车点加入沙河顶点数组(如果原本没有),本部同理。然后对应两个顶点数组分别生成两个abmap。
随后遍历沙河顶点数组和本部顶点数组,将不同校区的所有途经顶点构建为一个abmap(沙河顶点a到本部顶点b的路径由3段合成:a到沙河公交点路径+跨校区路径+本部公交点到b的路径)。以该abmap进行蚁群算法。
接着将蚁群后得到的路径拆分,由于最短路径的必然性,该路径中必然可分为三段,第一段为沙河校区路径(终点为沙河公交点),第二段为跨校区路径,最后一段为本部路径(起点为本部公交点)。
开始导航后若当前状态到达第一段路线终点,系统自动用第二段路线设置当前状态,到达第二段终点时同理。
离开/进入校园自动切换地图的显示:
当前状态校园出口并即将跨校区时,系统自动切换校区是合理的需求。为了易于实现该操作,我们多设置了两个逻辑上的顶点,称之为沙河和本部切地图点,以沙河切地图点为例。
沙河切地图点物理上与沙河校门点重合,即从校门到该点的距离为0。利用边距离为0这一特殊性,我们在move函数中加入判断:若当前上一顶点为沙河校门点且当前边距离为0且跨校区标志cross==true,将自动切换地图。本部同理。
中文模糊搜索:
在C++中英文一个字符占一个字节,而中文一个字符占3个字节,故不能采用单纯的字符串匹配函数。我们在使用string::find的基础上增加模3(%3)操作,判断搜索到的子串第一次出现的下标是否为3的整数倍,如果是才可认为匹配成功。
该算法能解决绝大部分中文匹配异常的问题,但仍有样例会造成错误,如“人类”和“死”将被认定为相同字符串。不过由于我们的程序中不涉及错误字段,故可以信任该算法。
该算法应提前筛除匹配串为空串的情况,否则一定认为匹配成功:空串是任意串的子串,出现下标为0。
模糊搜索结果选择
通过动态创建多个QLabel和QPushButton,并绑定同一槽函数,使用Lambda表达式传值以确定所点击按钮的编号,实现将多个搜索结果展示于用户界面供用户选择。用一个指针数组记录动态创建的组件,以便在选择后全部删除释放空间。
路线显示问题
只有在当前画面显示的地图与当前导航所处地图相匹配的情况下才显示导航的线路。因此通过设置当前显示地图的标志,将之与当前状态所在地图进行匹配,若成功匹配则将当前位置的小圆点加入到QGraphicsScene中,并且将QPathItem的path设置为当前地图对应的QPainterPath。如此可实现即使地图不匹配,图形化的移动仍会进行,只不过没有显示出来。
5.4 Clock模块
时钟模块,提供模拟导航系统的时间。
主要函数及功能如下:
函数原型 | 功能 |
QStringget_clock_str() | 将时钟转化为格式化字符串并返回 |
voidupdate_clock() | 系统时钟更新,+1s |
intdifSec(constClock&clock) | 求两时钟相差的秒数 |
booloperator>=(constClock&clock) | 重载运算符>=:用于判断时间的大于等于关系 |
Clockoperator-(inth) | 重载运算符-:用于计算时间减去小时数得到的时间 |
5.5 Current模块
当前状态模块,提供用户当前状态信息及周边信息。
主要函数及功能如下:
函数原型 | 功能 |
boolinShahe() | 判断当前状态是否在沙河校区 |
boolinBenbu() | 判断当前状态是否在本部校区 |
boolinBetween() | 判断当前状态是否在校外地图 |
5.6 Dijkstra模块
迪杰斯特拉算法模块,根据传入的优先度(距离/时间)提供两点间的最短距离(时间)路径的算法,并且结合BFS实现搜索一定范围内的所有顶点及其路径的功能
共实现四种功能:
单源点的两点寻路:
起点和终点均为顶点的最短路径算法。
双源点的两点寻路:
起点位于边的中间(不在顶点上),终点为顶点的最短路径算法。
单源点的范围搜索:
起点为顶点的Dijkstra+BFS算法。
双源点的范围搜索:
起点位于边的中间(不在顶点上)的Dijkstra+BFS算法。
主要函数及功能如下:
函数原型 | 功能 |
voidsingleSearch(intbegin,qrealr,;qrealv) | 搜索单源点一定距离范围内的建筑并计算到各建筑的行进路线 |
voiddoubleSearch(intbegin1,intbegin2,;qrealr,qrealv,qrealldis,qrealrdis) | 搜索双源点一定距离范围内的建筑并计算到各建筑的行进路线(当点位于某边而非节点上时,以边两端的节点作为源点) |
voidsingleFind(intbegin,intend,;Prioritypriority,qrealv) | 根据不同策略计算单源点到终点的行进路线 |
voiddoubleFind(intbegin1,intbegin2,;intend,Prioritypriority,;qrealv,qrealldis,qrealrdis) | 根据不同策略计算双源点到终点的行进路线(当点位于某边而非节点上时,以边两端的节点作为源点) |
voidstart() | Dijkstra算法 |
voidsearch() | Dijkstra+BFS算法 |
voidinsert_to_visit(intx) | 插入待遍历顶点,保证顶点集有序 |
单源点两点寻路算法(Dijkstra算法)描述:
数据结构
QVector<int> to_visit;
待遍历顶点集,维持为一个距离(时间)递减的优先队列,每次遍历弹出其队头元素。
QMap<int,int> in_visited;
已处理的顶点集,Key为顶点编号,Value为1表示在集合内,否则不在。由于in_visited不需要遍历,而需要经常查询某个顶点是否已处理,故使用底层为红黑树的QMap容器,提高查询效率。
QMap<int,int> parent;
父顶点表,Key为顶点编号,Value为其对应的父顶点的编号。同样由于经常需要查询某顶点的父亲,故用QMap存储。该表用于逆向生成路径。
QVector<Vertex*> dij_path;
路径数组,值为顶点指针。
QMap<int,qreal> dis;
距离表,Key为顶点编号,Value为其到起点的距离。使用QMap原因如上。
QMap<int,qreal> time;
时间表,Key为顶点编号,Value为其到起点的时间。使用QMap原因如上。
操作步骤
① 初始时,visited为空,to_visit中只有起点s。
② 然后,从to_visit中弹出队头顶点(距离(时间)最短的顶点)记为current,并将其加入到visited中。
③ 接着,遍历不在visited中的与current邻接的所有顶点,计算他们到current的距离d(时间t),如果比原先到该点的距离(时间)短,就更新它,并且将其parent设为current。
④ 重复②③操作,直到所有顶点都已加入visited。
最后通过parent表,从终点end逆向求出最短路径。
伪代码如下:
While(to_visit非空){Current = to_visit队头;弹出to_visit队头;current加入visited;For(所有current的邻接点) {If(该点in_visited) continue;If((优先度为距离优先 && 该点到current的距离+current 到起点的距离 < 该点到顶点的距离) || (优先度为时间优先 && 该点到current的距离+current 到起点的时间 < 该点到顶点的时间)) {更新dis;更新time;该点的parent = current;}If(current没有父亲) current的parent = begin;}}int p = end;while(p!=begin) {dij_path.push_front(vertices[p]);p = parent[p];
}dij_path.push_front(vertices[begin]);
双源点两点寻路算法(Dijkstra算法)描述:
分别从当前所在边的左端点(记为lbegin)、右端点(rbegin)进行单源点寻路dij_left和dij_right。记当前位置到lbegin的距离为ldis、时间为ltime;到rbegin的距离为rdis、时间为rtime。
若dij_left的距离+ldis<dij_right的距离+rdis(时间同理),取dij_left路径为最短路径,并将dij_left的路径起点为current状态的下一顶点。
单源点范围搜索算法(Dijkstra+BFS算法)描述:
数据结构与算法1类似,仅用如下数据结构替换dij_path:
QMap<int,QVector<Vertex*>> paths:
路径表,Key值为顶点编号,Value为以该顶点为终点的最短路径。
伪代码如下:
While(to_visit非空){Current = to_visit队头;弹出to_visit队头;if(dis[current]>搜索半径) break;current加入visited;For(所有current的邻接点) {If(该点in_visited) continue;If((优先度为距离优先 && 该点到current的距离+current 到起点的距离 < 该点到顶点的距离) || (优先度为时间优先 && 该点到current的距离+current 到起点的时间 < 该点到顶点的时间)) {更新dis;更新time;该点的parent = current;}If(current没有父亲) current的parent = begin;}逆向求出current的路径,加入路径表QVector<Vertex*> temp;int p = current;while(p != begin) {temp.push_front(vertices[p]);p = parent[p];}temp.push_front(vertices[p]);paths[current] = temp;
}
5.7 Ant模块
蚁群算法模块,提供途径多点的最短路径算法。
由于途经多点算法与传统的旅行商问题略有不同,因此蚂蚁最终不必回到起点,蚂蚁行进使用的完全图也并非初始邻接表,而是根据途经顶点集合由Dijkstra算法生成的(该操作在主窗口内完成,故此模块中不涉及)。
蚁群算法描述:
基本原理
① 根据具体问题设置多只蚂蚁,分头并行搜索。
② 每只蚂蚁完成一次周游后,在行进的路上释放信息素,信息素量与解的质量成正比。
③ 蚂蚁路径的选择根据信息素强度大小(初始信息素量设为相等),同时考虑两点之间的距离,采用随机的局部搜索策略。这使得距离较短的边,其上的信息素量较大,后来的蚂蚁选择该边的概率也较大。
④ 每只蚂蚁只能走合法路线(经过每个地点1次且仅1次),为此设置禁忌表来控制。
⑤ 所有蚂蚁都搜索完一次就是迭代一次,每迭代一次就对所有的边做一次信息素更新,原来的蚂蚁死掉,新的蚂蚁进行新一轮搜索。
⑥ 更新信息素包括原有信息素的蒸发和经过的路径上信息素的增加。
⑦ 达到预定的迭代步数,或出现停滞现象(所有蚂蚁都选择同样的路径,解不再变化),则算法结束,以当前最优解作为问题的最优解。
信息素及转移概率的计算
算法流程图如下:
主要函数及功能如下:
函数原型 | 功能 |
voidstart(constQVector&spots,;Prioritypriority) | 蚁群算法开始迭代,参数分别为顶点集和优先度(距离/时间) |
voidclearAll() | 清空所有辅助表(ph、visibility等) |
voidshuffle() | 洗牌,随机取一个顶点作为蚂蚁的起点 |
voidmove() | 蚂蚁根据概率,结合轮盘赌进行移动 |
voidrecord() | 记录单次迭代的最短路径、距离、时间等 |
voidrecord_inall() | 记录所有迭代中的最短路径、距离、时间等 |
voidupdate() | 更新每条路径的信息素浓度 |
voidinit_ph() | 初始化信息素浓度表 |
voidinit_delta_ph() | 初始化信息素增量表 |
voidinit_visibility() | 初始化能见度表 |
voidinit_tabu() | 初始化禁忌表 |
voidclear_tabu() | 清空禁忌表 |
qrealrnd(qreallower,qrealupper) | 返回lower到upper中的一个随机数 |
5.8 LogSearch模块
日志查询模块,提供查询日志文件相关信息的功能。
主要函数及功能如下:
函数原型 | 功能 |
voidsendData(Clockcur) | 主窗口传入当前时间 |
voidon_pushButton_clicked() | 查询按钮的槽函数,根据操作和时间筛选日志记录并输出 |
六、测试样例
地图更换
分别是沙河地图、本部地图、校外地图
更换当前出行方式
日志查询
默认查询前三个小时日志
自行设置查询起止时间
根据操作查询
查询功能
全部搜索栏支持模糊搜索
全部搜索栏支持逻辑名搜索
导航
指定终点寻路
用户指定起止点寻路
用户选择不同的优先级会有不同的路径
寻路完成后开始导航并不断更新当前状态
导航过程中进行操作系统会自动暂停
跨校区两点导航(优先展示第一段导航路径)
系统自行判断不同优先级的时候的不同路径
当试图在班车上或者公交上更改导航逻辑
多点寻路(沙河-图书馆、快递站操场出口、教学楼)
七、评价及改进建议
7.1 总体概述
本校园导览系统基本实现了校园内的导航,支持两个校区的导览,教学楼、图书馆、宿舍、体育馆等近50个建筑,360个顶点,近700条边,并可以查询到校园内各种建筑物及服务设施的相关信息。在导航时,支持最短距离、最短时间、途径最短距离及交通工具的最短时间等四个策略,通过图形化界面实现了模拟导航,在导航时用户可以实时查询周边建筑信息,并可设置当前模拟导航的时间速率。本系统可以记录当前运行情况的日志文件,如学生状态变化和键入信息。
7.2 优点
本系统实现了图形化界面,增强了和用户之间的交互性,使用起来更加便捷直观。系统界面各功能清晰,操作逻辑性强。
用户可以直接在本图形化界面上进行双击操作以查看相关点的信息。
本校园导览系统可以实时在地图上进行导航,并更新当前状态信息,在行进途中可以根据设置的通行率来判断最短时间的路径,且在跨校区导航时会自动计算等车时间,并根据优先度选择不同的跨校区方式。
本系统实现了在行进途中可自行更改目标和导航策略,系统按照当前位置做相应操作。
用户在进行搜索时,可以实现模糊搜索以及逻辑名和实际地址名的对应,让用户不再担心自己会不会无法搜索到自己想去的目的地。
此外,本系统根据时钟判定当前所在时间及星期,自行匹配账户课程表中的当前课程信息并进行导航。
本系统内置时钟,基本默认模拟速率为167ms模拟为现实的1s,并可以进行模拟速率的更改,在满足模拟的同时也不会耗费太多的时间。
7.3 改进建议
本系统在人流量判断负载的方面并没有做到很好的层次,未能实现负载均衡。
本系统暂时只是实现了由课程表到建筑物的对应,并没有继续实现到对应楼层及房间的对应,相同的是,对于宿舍楼内部的具体实现当前也抱憾未能实现。
相关文章:
基于QT(C++)实现(图形界面)校园导览系统
校园导览系统 一、任务描述 大学校园充满着忙忙碌碌的学生和老师们,但是有时候用户宝贵的时间会被复杂的道路和愈来愈多的建筑物的阻碍而浪费,为了不让同学们在自己的目的地的寻路过程中花费更多的时间,我们着手开发这样一款校园导览系统。…...
【C/C++】虚函数
📘 C 虚函数详解(Virtual Function) 📌 什么是虚函数? 虚函数(Virtual Function) 是 C 中实现运行时多态(Runtime Polymorphism) 的核心机制。 它允许派生类 重写&…...
no main manifest attribute, in xxx.jar
1、问题: Spring Boot项目在idea中可以正常运行,但是运行Spring Boot生成的jar包,报错: 1、no main manifest attribute, in xxx.jar 2、xxx.jar中没有主清单属性 2、解决办法: 删除pom.xml中<configuration&g…...
使用 AI 如何高效解析视频内容?生成思维导图或分时段概括总结
一、前言 AI 发展的如此迅速,有人想通过 AI 提效对视频的解析,怎么做呢? 豆包里面有 AI 视频总结的功能,可以解析bilibili网站上部分视频,如下图所示: 但有的视频解析时提示: 所以呢&#x…...
比较入站和出站防火墙规则
组织需要仔细配置防火墙规则,监控网络的传入和传出流量,从而最大限度降低遭受攻击的风险。在有效管理入站和出站防火墙规则前,了解入站与出站流量的区别至关重要。 一、什么是入站流量? 入站流量指的是并非源自网络内部…...
开放式耳机什么品牌的好用?性价比高的开放式耳机品牌推荐一下
这几年蓝牙耳机发展得很快,从最早的入耳式,到现在流行的开放式,选择越来越多。我自己是比较偏向佩戴舒适的类型,用过开放式之后就回不去了。它不堵耳、不压迫,戴着轻松不累,对我这种耳朵容易不适的人来说太…...
WPF之高级绑定技术
文章目录 引言多重绑定(MultiBinding)基本概念实现自定义IMultiValueConverterMultiBinding在XAML中的应用示例使用StringFormat简化MultiBinding 优先级绑定(PriorityBinding)基本概念PriorityBinding示例实现PriorityBinding的后…...
k8s高可用集群,自动化更新证书脚本
#!/bin/bash # 切换到证书目录 cd /etc/kubernetes/pki || exit # 备份原有证书(重要!) sudo cp -r apiserver.crt apiserver.key \ apiserver-etcd-client.crt apiserver-etcd-client.key \ apiserver-kubelet-client…...
【Python 函数】
Python 中的函数(Function)是可重复使用的代码块,用于封装特定功能并提高代码复用性。以下是函数的核心知识点: 一、基础语法 1. 定义函数 def greet(name):"""打印问候语""" # 文档字符串&…...
Filecoin矿工资金管理指南:使用lotus-shed actor withdraw工具
Filecoin矿工资金管理指南:使用lotus-shed actor withdraw工具 引言lotus-shed actor withdraw命令概述命令语法参数选项详解常见使用场景1. 提取全部可用余额2. 提取指定数量的FIL3. 通过受益人地址发送交易 最佳实践资金安全管理操作流程优化 常见问题与解决方案提…...
AI辅助DevOps与自动化测试:重构软件工程效率边界
随着AI技术渗透至软件开发生命周期,DevOps与自动化测试领域正经历颠覆性变革。本文系统性解析AI在需求分析、测试用例生成、部署决策、异常检测等环节的技术实现路径,结合微软Azure DevOps、Tesla自动驾驶测试等典型场景,探讨AI如何突破传统效…...
css内容省略——text-overflow: ellipsis
title: css内容省略 date: 2025-05-07 19:41:17 tags: css text-overflow: ellipsis text-overflow: ellipsis用于在文本溢出容器时显示省略号(…) 1.单行省略 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"&g…...
nginx性能优化与深度监控
一、性能调优方向 1. 系统层面优化 内核参数调整 TCP队列与连接管理: net.core.somaxconn(最大连接队列长度,建议设为65535)net.ipv4.tcp_max_syn_backlog(SYN队列长度,建议65535)net.ipv4.tc…...
leetcode 70.爬楼梯(c++详细最全解法+补充知识)
目录 题目 解答过程 补充哈希表知识 哈希表基本特性 常用成员函数 基本用法 实现代码 1.递归 2.循环遍历 3.哈希表 题目 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? 示例 1&#…...
护照阅读器简介
护照阅读器简介 护照阅读器(Passport Reader)是一种专用设备,用于快速、准确地读取护照、身份证、签证等旅行证件的机读区(MRZ)和芯片(ePassport)信息,广泛应用于出入境管理、机场安…...
切片和边缘计算技术分析报告
切片和边缘计算技术分析报告 一、引言 随着 5G 通信技术的快速发展,网络切片和边缘计算技术逐渐成为通信领域的热点研究方向。网络切片技术通过将物理网络划分为多个逻辑上的虚拟网络,以满足不同业务场景对网络性能的差异化需求。边缘计算则将计算、存…...
vue3笔记(自存)
1. Vue3简介 2020年9月18日,Vue.js发布版3.0版本,代号:One Piece(n 经历了:4800次提交、40个RFC、600次PR、300贡献者 官方发版地址:Release v3.0.0 One Piece vuejs/core 截止2023年10月,最…...
多线服务器具有什么优势
在当今数字化飞速发展的时代,多线服务器宛如一位低调的幕后英雄,默默为我们的网络世界提供着强大的支持。那么,多线服务器到底具有哪些令人瞩目的优势呢 首先,多线服务器的最大优势之一就是网络访问的高速与稳定。想象一下&#x…...
Azure OpenAI 聊天功能全解析:Java 开发者指南
Azure OpenAI 聊天功能全解析:Java 开发者指南 前言 在当今人工智能飞速发展的时代,AI 驱动的文本生成技术正深刻改变着众多应用场景。Azure OpenAI 作为这一领域的重要参与者,由 ChatGPT 提供支持,不仅具备传统 OpenAI 的功能&…...
【情感关系】健全自我
一些看到后深有感触的文字 请大家无论如何也不要相信这种:“童年/原生家庭经历决定人生走向”的论调。 过去可以影响我们但是无法主宰我们,人是有主观能动意识的,认识自己的问题就是改变人生轨迹的第一步。 后来我们会发现,对于…...
SLAM:单应矩阵,本质矩阵,基本矩阵详解和对应的c++实现
单应矩阵(Homography Matrix) 单应矩阵(Homography Matrix)是计算机视觉中描述同一平面在不同视角下投影变换的核心工具,广泛应用于图像校正、拼接、虚拟广告牌替换等场景。以下从原理、求解方法和C++实现三方面展开详解: 一、单应矩阵的数学原理 定义与作用 单应矩阵是…...
数据报(Datagram)与虚电路(Virtual Circuit)的区别
数据报(Datagram)与虚电路(Virtual Circuit)的区别 数据报和虚电路是计算机网络中两种不同的通信方式,主要区别体现在 连接方式、路由选择、可靠性、延迟和适用场景 等方面。以下是它们的详细对比: 1. 基本…...
工业现场ModbusTCP转EtherNETIP网关引领生物现场领新浪潮
生物质发生器是一种能够产生、培养生物的设备。客户现场需要将生物发生器连接到罗克韦尔系统,但是二者协议无法直接通讯,需要通过ModbusTCP转Ethernet/IP网关将两者进行通讯连接,生物质发生器以其独特的工作原理和优势,使得生物的…...
DeepSeek的100个应用场景
在春节前夕,浙江杭州的AI企业DeepSeek推出了其开源模型DeepSeek-R1,以仅相当于Open AI最新模型1/30的训练成本,在数学、编程等关键领域展现出媲美GPT-o1的出色性能。发布仅数日,DeepSeek-R1便迅速攀升至中美两国苹果应用商店免费榜…...
【Linux 系统调试】Linux 调试工具strip使用方法
目录 一. strip 工具的定义与核心作用 1. strip 是什么? 2. strip 工具调试符号的作用 3. strip 工具调试符号的重要性 二. 如何确认文件是否被 strip 处理? 1. 通过 file 命令检查文件状态 2. strip 的典型用法 基础命…...
Solana批量转账教程:提高代币持有地址和生态用户空投代币
前言 Solana区块链因其高吞吐量和低交易费用成为批量操作(如空投)的理想选择。本教程将介绍几种在Solana上进行批量转账的方法,帮助您高效地向多个地址空投代币。 solana 账户模型 在Solana中有三类账户: 数据账户,…...
leetcode hot100 技巧
如有缺漏谬误,还请批评指正。 1.只出现一次的数字 利用异或运算相同得0的特点。所有出现过两次的数字都会在异或运算累加过程中被抵消。、 class Solution { public:int singleNumber(vector<int>& nums) {int res0;for(int i0;i<nums.size();i) re…...
搭建spark伪分布集群
1.先查看虚拟机的默认名称,将其修改为vm01 2.更改了主机名,还需要修改/etc/hosts文件,在这个文件设定了IP地址与主机名的对应关系,类似DNS域名服务器的功能 3.修改spark相关配置文件,包括spark-env.sh和slave两个文件 …...
vue3自定义audio音频播放【进度条,快进,后退,音量加减,播放速度】
本文将介绍如何使用Vue3构建一个功能完备的自定义音频播放器,包含进度条控制、快进/后退、音量调节和播放速度控制等功能。相比使用浏览器默认的audio控件,自定义播放器可以提供更一致的用户体验和更灵活的设计空间,复制粘贴即可使用…...
学习基本开锁知识
本文主要内容 目前市面上锁的种类有哪些 机械锁 钥匙开锁 :这是最常见的传统开锁方式,通过插入匹配的钥匙转动来开锁,如常见的家用门锁、汽车门锁等,钥匙形状和齿纹各异,有单排齿的一字锁、双排齿的双面锁,…...
泛微ECOLOGY9 流程表单中添加按钮的方法
使用场景介绍 有时需要在流程表单中添加一个按钮来实现弹窗、打开指定的页面等需求。 实现方式一:通过ID 在流程表单中想要生成按钮的地方指定一个ID,然后再到ecode中创建按钮及方法。 具体步骤如下: 一、表单中指定ID为 exceldc 二、在ecode中实现按钮及功能。 1.建立…...
小刚说C语言刷题—1331 做彩纸花边
1.题目描述 李晓芳用一条长为 n 米的彩纸制作花边,每朵花李晓芳用一条长为 n 米的彩纸制作花边,每朵花的宽度为 x 厘米,花与花之间的间隔为 y 厘米。请问 n 米的彩纸最多能做多少朵花的花边。 如,图中的案例花的宽度为 4.5cm &a…...
【Python】读取excel文件的时候,遇到“Excel file format cannot be determined”的问题
使用os.path 读取路径下的文件,并拼接文件名,可能会遇到这个问题: ValueError: Excel file format cannot be determined, you must specify an engine manually. 因为我用的是相对路径的拼接的方法,读取出来会有这样的问题&#…...
天气预报、天气查询API接口文档 | 实时天气 | 七日天气 | 15日天气查询
天气预报、天气查询API接口文档 | 实时天气 | 七日天气 | 15日天气查询 这篇文章详细介绍了一种天气查询服务,提供了实时天气(1天)、7天预报和15天预报三个RESTful接口,支持通过地区名称、编码、IP或经纬度等多种方式查询,返回数据包含温度、…...
Linux中的线程安全与线程同步详解
在Linux系统中,线程安全性是指在多个线程同时访问共享资源时,能够确保这些共享资源不被破坏或者产生数据错误。线程同步是一种机制,用于保证多个线程之间的操作次序和协调,以避免竞态条件、死锁等问题。 以下是线程安全和线程同步…...
qwen2.5vl
多模态大模型通用架构: 在通用的MM-LLM(Multi-Modality LLM)框架里,共有五个模块,整体以LLM为核心主干,分别在前后有一个输入、输出的投影模块(Projector),投影模块主要…...
国产Word处理控件Spire.Doc教程:在Java中为Word文本和段落设置边框
在 Word 文档中添加边框是一种突显重点信息的有效方式,尤其适用于包含大量文本的内容场景。相比普通格式,给字符或段落添加边框不仅能强化视觉层次,还能提升文档的专业感与可读性。E-iceblue旗下Spire系列产品是国产文档处理领域的优秀产品&a…...
【CUDA C实战演练】CUDA介绍、安装、C代码示例
文章目录 0. 前言1. 并行计算与异构计算1.1 并行计算(Parallel Computing)1.2 异构计算(Heterogeneous Computing) 2. CUDA 的核心概念2.1 主机(Host)与设备(Device)2.2 线程层次结构…...
滑动窗口——无重复字符最长的字串
题目: 子字符串,我们也可以看成子数组。 题意不难理解,这个题我们暴力枚举的思路是把每一个字符遍历存到hash桶中,如果放两次就进行结果更新。 但这个题我们有更优化的方法,利用数组代替hash(重点不在这&…...
QT中connect高级链接——指针、lambda、宏
1、connect使用指针 connect(button,&QPushButton::released,this,&MainWidget::mySlot); //【抬起】按钮button时,修改按钮b2的标题 2、使用lambda表达式 引入lambda表达式,类似内联函数,可以用于不会被重用的短代码片段&#x…...
说说es配置项的动态静态之分和集群配置更新API
这天因为某件工作来到了es官网某个参数配置相关的页面,注意到了下图圆圈里的“Dynamic”: 链接:https://www.elastic.co/guide/en/elasticsearch/reference/8.1/modules-cluster.html#misc-cluster-settings 显然这是对配置项的一个描述&am…...
如何有效防御服务器DDoS攻击
分布式拒绝服务(DDoS)攻击通过大量恶意流量淹没服务器资源,导致服务瘫痪。本文将提供一套结合代码实现的主动防御方案,涵盖流量监控、自动化拦截和基础设施优化。 1. 实时流量监控与告警 目标:检测异常流量并触发告警…...
C#上传文件到腾讯云的COS
测试环境: vs2022 .net 6控制台应用程序 测试步骤如下: 1 添加子用户,目前是为了拿到secretId和secretKey,打开添加子用户界面链接:https://console.cloud.tencent.com/cam 并为子用户添加API 密钥 2 通过链接htt…...
强缓存与协商缓存的实现机制
文章目录 前言**1. 强缓存(强制缓存)****强缓存生效流程**:**2. 协商缓存(对比缓存)****协商缓存生效流程**:**对比总结****实际应用建议** **1. 缓存配置的三种主要实现方式** 前言 强缓存与协商缓存的实…...
【云备份】项目展示项目总结
目录 一. 项目展示 二. 项目总结 一. 项目展示 首先我们打开服务端,把没有用的东西都删干净,包括备份的文件信息啊什么的 这个时候我们启动服务器 我们先用浏览器去看看 什么东西都没有。 好,我们现在去启动客户端,下面这个是客…...
嵌入式 Linux Platform 驱动模型测试
文章目录 一、为什么要用 Platform 驱动模型? 二、Platform 驱动模型的三大核心组件 1.Platform 总线(虚拟总线) 2.Platform 设备(platform_device) 3.Platform 驱动(platform_driver) 三、Plat…...
Linux:web服务nginx
一.Nginx简介 Nginx (engine x) 是一个高性能的Web和反向代理服务器,同时也是一个 IMAP/POP3/SMTP 代理服器。Nginx处理高并发能力是十分强大的,能经受高负载的考验。而且支持热部署,几乎可以做到 7 * 24 小时不间断运行,即使运行…...
【“星睿O6”评测】Armv9.2a、KLEIDIAI及vulkan加速llamacpp部署本地AI
llamacpp 简介 llama.cpp 的主要目标是通过最小的设置,实现 LLM 推理,在各种硬件上(无论是本地还是云端)提供最先进的性能。 纯 C/C实现,无任何依赖苹果 M1/M2 芯片(Apple silicon)优化&#…...
Advanced Installer 22.5打包windows 安装包
Advanced Installer 22.5打包windows 安装包 1、打开Advanced Installer 22.5打包工具,绿色免安装下载链接:https://download.csdn.net/download/LongtengGensSupreme/90778405 2、选择需要打包的文件 3、设置安装文件夹权限 4、安装参数设置 5、构建设…...
Qt界面设计时窗口中各控件布局及自适应方法
在进行Qt界面设计时,往往会因为控件的布局以及伴随窗口拉伸时控件没有做好自适应部署而导致界面效果大打折扣。 这里简单介绍一下QtDesigner实现界面自适应布局,包括水平布局、垂直布局、栅格布局应用,确保界面元素随窗口变化自动调整。 图1 如图所示,这是我们的设计目标…...