【boost搜索引擎】下
boost搜索引擎
- 1. 编写搜索引擎模块 Searcher
- 2. 编写 http_server 模块
- 3. 编写前端模块
- 4. 添加日志
- 5. 补充 去掉暂停词
- 6. 项目扩展方向
1. 编写搜索引擎模块 Searcher
这一模块主要提供建立索引,以及收到用户的发起的http请求通过Get方法提交的搜索关键字,然后对关键字进行分词,先在倒排索引中查找到对应关键字的文档ID,然后在正排索引中根据文档ID,找到多个相关文档内容,拼接返回给用户。
class Searcher
{
private:Index *index; //供系统进⾏查找的索引
public:Searcher(){}~Searcher(){}
public:void InitSearcher(const std::string &input){//1. 获取或者创建index对象//2. 根据index对象建⽴索引}//query: 搜索关键字//json_string: 返回给⽤⼾浏览器的搜索结果void Search(const std::string &query, std::string *json_string){//1.[分词]:对我们的query进⾏按照searcher的要求进⾏分词//2.[触发]:就是根据分词的各个"词",进⾏index查找//3.[合并排序]:汇总查找结果,按照相关性(weight)降序排序//4.[构建]:根据查找出来的结果,构建json串 -- jsoncpp}};
这里使用到了Josn库,Jsoncpp 库用于实现 json 格式的序列化和反序列化。
安装Json库
#更新源
sudo apt-get update
#安装
sudo apt-get install libjsoncpp-dev
//Searcher.hpp#pragma once
#include "index.hpp"
#include <jsoncpp/json/json.h>class Searcher
{
public:void InitSearcher(const std::string &output){// 1.获取或者创建index对象index = Index::GetInstance();cout << "获取index单例成功..." << endl;// 2.根据index对象建立索引index->BuildIndex(output);cout << "建立正排和倒排索引成功..." << endl;}// query: 搜索关键字// json_string: 返回给用户浏览器的搜索结果void Search(const std::string &query, std::string *json_string){//1.[分词]:对我们的query进行按照searcher的要求进行分词std::vector<std::string> words;JiebaUtil::CutString(query,&words);//2.[触发]:就是根据分词的各个"词",进行index查找,建立index是忽略大小写,所以搜索,关键字也需要InvertedList inverted_list_all;//倒排拉链for(auto word : words){//忽略大小写boost::to_lower(word);//找对应关键字倒排索引InvertedList* inverted_list = index->GetInvertedIndex(word);if(inverted_list == nullptr){continue;}//所有关键字对应的倒排索引都放在这里。有没有什么问题?inverted_list_all.insert(inverted_list_all.end(),inverted_list->begin(),inverted_list->end());}//倒排索引找没找到没有必要往下走了if(inverted_list_all.empty()){return;}//3.[排序]:汇总查找结果,按照相关性(weight)降序排序std::sort(inverted_list_all.begin(),inverted_list_all.end(),\[](const InvertedElem& e1,const InvertedElem& e2){\return e1.weight > e2.weight;});//4.[构建]:根据查找出来的结果,构建json串 -- jsoncpp --通过jsoncpp完成序列化&&反序列化Json::Value root;for(auto& item : inverted_list_all){DocInfo* doc = index->GetForwardIndex(item.doc_id);if(nullptr == doc){continue;}Json::Value elem;elem["title"] = doc->title;//我们不需要把文档所有内容返回,只要返回有关键字的摘要就行了elem["desc"] = GetDesc(doc->content,item.word);elem["url"] = doc->url;//for deubg, for delete//elem["id"] = (int)item.doc_id;//elem["weight"] = item.weight; //int->stringroot.append(elem);}//将序列化后的结果返回给用户//Json::StyledWriter write;Json::FastWriter write;*json_string = write.write(root);}
private:std::string GetDesc(const std::string& html_content,const std::string& word){//找到word在html_content中的首次出现,然后往前找50字节(如果没有,从begin开始),往后找100字节(如果没有,到end就可以的)//截取出这部分内容// const size_t prev_step = 50;// const size_t next_step = 100;const int prev_step = 50;const int next_step = 100;//1.找到首次出现//这里有个细节,如果用find去找文档内容首次出现关键字的地方是有问题的//find它是不会忽略大小写的,内容是有大小写的,但是我们把关键字却是以小写存到倒排中的//也就是说当前用这个小写关键字去内容中找关键字首次出现的地方,明明内容中是有这个关键字的//但是就是因为我们把关键字以小写写入倒排,所有导致明明有但是在内容中找不到//size_t pos = html_word.find(word);auto it = std::search(html_content.begin(),html_content.end(),word.begin(),word.end(),\[](const char& s1,const char& s2){ \return std::tolower(s1) == std::tolower(s2);});if(it == html_content.end()){return "None1";//实际上走到这里关键字一定会content中出现,因为标题也是content中找到}// const size_t pos = std::distance(html_word.begin(),it);const int pos = std::distance(html_content.begin(),it);//2.获取start,end// size_t start = 0;// size_t end = html_word.size() - 1;int start = 0;int end = html_content.size() - 1;//这里有个坑,size_t是无符号整型的,即使是负数也会转换出一个特别大的整数//如果pos - prev_step < 0 负数就会转变成一个特别的的正数// if(pos - prev_step > start) start = pos - prev_step;// if(pos + next_step < end) end = pos + next_step;//方法1: - 变成 +, + 变成 -,防止越界// if(pos > start + prev_step) start = pos - prev_step;// if((int)pos < (int)(end - next_step)) end = pos + next_step;//方法2: size_t 变成 intif(pos - prev_step > start) start = pos - prev_step;if(pos + next_step < end) end = pos + next_step;//3. 截取子串,returnif(start >= end) return "None2";std::string desc = html_content.substr(start,end-start);desc += "...";return desc;}
private:Index *index = nullptr;
};
上面代码还有一个问题!
如果你有一个文档是 :今天晚上吃米饭。分词:今天/晚饭/吃米饭/米饭。建立倒排索引,假设每个关键字对应的文档ID都是100
假如你搜索的关键字也是,今天晚饭吃米饭。先分词 ,今天/晚饭/吃米饭/米饭,然后去倒排搜索中去查,它们对应的文档ID可都是100,上面代码我们可是不管不顾直接把从倒排拉链中取出的节点都放在一个新的倒排拉链中保存起来。然后去排序,在去正排索引根据文档ID查文档内容。
如果这样的话,最终你会发现搜索引擎给你返回的内容都是重复的!!!
所以不能直接把从倒排拉链中取出的节点都放在一个新的倒排拉链中保存起来,而是先要去重! 因为文档ID都是一样的,所以我们可以根据文档ID去重,然后把文档ID相同的关键字放在一起,并且把每个关键字的权值也放在一起。
所以我们在searcher模块就有一个新的节点,这个节点用来保存在倒排索引中搜索到的然后没有出现没有重复文档ID节点。因为所有重复文档的ID和关键字以及权值我们都放在一起了。
typedef struct InvertedElemUnique
{uint64_t doc_id;int weight;//用数组存每个关键字,不要用sting,因为还要用关键字在content找内容//如果doc_id虽然重复,但是关键字是在content中是分开的//如果把关键字都拼一起了,然后去content去找内容是找不到的std::vector<std::string> words;InvertedElemUnique() : doc_id(0), weight(0) {}} InvertedElemUnique;
然后我们可以先用unordered_map根据wendID先去重,然后在放到倒排拉链,后面就和之前的一样啦。
class Searcher
{
public://...// query: 搜索关键字// json_string: 返回给用户浏览器的搜索结果void Search(const std::string &query, std::string *json_string){//1.[分词]:对我们的query进行按照searcher的要求进行分词std::vector<std::string> words;JiebaUtil::CutString(query,&words);//2.[触发]:就是根据分词的各个"词",进行index查找,建立index是忽略大小写,所以搜索,关键字也需要//InvertedList inverted_list_all;//倒排拉链//为了去重std::unordered_map<uint64_t,InvertedElemUnique> token_map;//在把去重后的结果放到倒排拉链std::vector<InvertedElemUnique> inverted_list_all;for(auto word : words){boost::to_lower(word);//找对应关键字倒排索引InvertedList* inverted_list = index->GetInvertedIndex(word);if(inverted_list == nullptr){continue;}//所有关键字对应的倒排索引都放在这里//inverted_list_all.insert(inverted_list_all.end(),inverted_list->begin(),inverted_list->end());//这里有些问题,如果分词情况: 你/今天/玩饭/吃什么 对应的doc_id假如都是100,搜索到的内容就会有重复//因此不能直接就放,需要先去重//内容重复,我们通过doc_id就清楚知道,然后我们可以把doc_id相同对应的权值和关键字都合并for(auto& elem : *inverted_list){auto& item = token_map[elem.doc_id];item.doc_id = elem.doc_id;item.weight += elem.weight;item.words.push_back(std::move(elem.word));}}for(auto &it : token_map){inverted_list_all.push_back(std::move(it.second));}//倒排索引找没找到没有必要往下走了if(inverted_list_all.empty()){return;}//3.[排序]:汇总查找结果,按照相关性(weight)降序排序// std::sort(inverted_list_all.begin(),inverted_list_all.end(),\// [](const InvertedElem& e1,const InvertedElem& e2){\// return e1.weight > e2.weight;// });//3.[排序]:汇总查找结果,按照相关性(weight)降序排序std::sort(inverted_list_all.begin(),inverted_list_all.end(),\[](const InvertedElemUnique& e1,const InvertedElemUnique& e2){\return e1.weight > e2.weight;});//4.[构建]:根据查找出来的结果,构建json串 -- jsoncpp --通过jsoncpp完成序列化&&反序列化Json::Value root;for(auto& item : inverted_list_all){DocInfo* doc = index->GetForwardIndex(item.doc_id);if(nullptr == doc){continue;}Json::Value elem;elem["title"] = doc->title;//我们不需要把文档所有内容返回,只要返回有关键字的摘要就行了//elem["content"] = doc->content;//elem["desc"] = GetDesc(doc->content,item.word);elem["desc"] = GetDesc(doc->content,item.words[0]);elem["url"] = doc->url;//for deubg, for delete//elem["id"] = (int)item.doc_id;//elem["weight"] = item.weight; //int->stringroot.append(elem);}//将序列化后的结果返回给用户//Json::StyledWriter write;Json::FastWriter write;*json_string = write.write(root);}
private:Index *index = nullptr;
};
2. 编写 http_server 模块
这里为了方便,我们就不自己手搓一个http服务器了。可以使用cpp-httplib库。
也是在gitee中找,随便选一个

cpp-httplib不建议下载最新的,因为运行这个库对gcc有要求必须要是高版本。不过如果你的unbentu是20.04这个版本的话,就不用管了,直接下下面的其中一个。

下载好之后也是拉到linux中解压之后,最后在软连接一下,不然在使用的时候会报找不到头文件的错误。

//http_server.cc#include "cpp-httplib/httplib.h"
#include "searcher.hpp"//http根目录,所有资源都放在这里
const std::string root_path = "./wwwroot";
//构建索引的文档路径
const std::string output = "data/raw_html/raw.txt";int main()
{Searcher search;search.InitSearcher(output);httplib::Server svr;//启动服务器svr.set_base_dir(root_path.c_str());//http根目录//for debug// svr.Get("/hi", [](const httplib::Request &req, httplib::Response &rsp)// { rsp.set_content("你好,世界!", "text/plain; charset=utf-8"); });svr.Get("/s", [&](const httplib::Request &req, httplib::Response &rsp){\if(!req.has_param("word")){rsp.set_content("必须要有搜索关键字!", "text/plan; charset=utf-8");return;}std::string word = req.get_param_value("word");//cout<<"用户正在搜索: "<<word<<endl;LOGMESSAGE(NORMAL,"用户正在搜索: " + word);std::string json_string;search.Search(word,&json_string);if(json_string.empty()){rsp.set_content("Not Found", "text/plain");}else{rsp.set_content(json_string,"application/json");} });svr.listen("0.0.0.0", 8080);//绑定IP和Portreturn 0;
};
以上就是boost搜索引擎的后端所有内容了,接下来就是前端的一些知识了。
3. 编写前端模块
了解 html,css,js
html: 是网页的骨骼 – 负责网页结构
css:网页的皮肉 – 负责网页美观的
js(javascript):网页的灵魂–负责动态效果,和前后端交互
教程:w3school
这里不多说,因为我也是一个二把刀。。。可以自己学一学。
在根目录wwwroot下再建一个index.html ,这个就是首页。

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><script src="http://code.jquery.com/jquery-2.1.1.min.js"></script><title>boost 搜索引擎</title><style>/* 去掉网页中的所有的默认内外边距,html的盒子模型 */* {/* 设置外边距 */margin: 0;/* 设置内边距 */padding: 0;}/* 将我们的body内的内容100%和html的呈现吻合 */html,body {height: 100%;}/* 类选择器.container */.container {/* 设置div的宽度 */width: 800px;/* 通过设置外边距达到居中对齐的目的 */margin: 0px auto;/* 设置外边距的上边距,保持元素和网页的上部距离 */margin-top: 15px;}/* 复合选择器,选中container 下的 search */.container .search {/* 宽度与父标签保持一致 */width: 100%;/* 高度设置为52px */height: 52px;}/* 先选中input标签, 直接设置标签的属性,先要选中, input:标签选择器*//* input在进行高度设置的时候,没有考虑边框的问题 */.container .search input {/* 设置left浮动 */float: left;width: 600px;height: 50px;/* 设置边框属性:边框的宽度,样式,颜色 */border: 1px solid black;/* 去掉input输入框的有边框 */border-right: none;/* 设置内边距,默认文字不要和左侧边框紧挨着 */padding-left: 10px;/* 设置input内部的字体的颜色和样式 */color: #CCC;font-size: 14px;}/* 先选中button标签, 直接设置标签的属性,先要选中, button:标签选择器*/.container .search button {/* 设置left浮动 */float: left;width: 150px;height: 52px;/* 设置button的背景颜色,#4e6ef2 */background-color: #4e6ef2;/* 设置button中的字体颜色 */color: #FFF;/* 设置字体的大小 */font-size: 19px;font-family:Georgia, 'Times New Roman', Times, serif;}.container .result {width: 100%;}.container .result .item {margin-top: 15px;}.container .result .item a {/* 设置为块级元素,单独站一行 */display: block;/* a标签的下划线去掉 */text-decoration: none;/* 设置a标签中的文字的字体大小 */font-size: 20px;/* 设置字体的颜色 */color: #4e6ef2;}.container .result .item a:hover {text-decoration: underline;}.container .result .item p {margin-top: 5px;font-size: 16px;font-family:'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif;}.container .result .item i{/* 设置为块级元素,单独站一行 */display: block;/* 取消斜体风格 */font-style: normal;color: green;}</style>
</head>
<body><div class="container"><div class="search"><input type="text" value="请输入搜索关键字"><button onclick="Search()">搜索一下</button></div><div class="result"><!-- 动态生成网页内容 --><!-- <div class="item"><a href="#">这是标题</a><p>这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要</p><i>https://search.gitee.com/?skin=rec&type=repository&q=cpp-httplib</i></div><div class="item"><a href="#">这是标题</a><p>这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要</p><i>https://search.gitee.com/?skin=rec&type=repository&q=cpp-httplib</i></div><div class="item"><a href="#">这是标题</a><p>这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要</p><i>https://search.gitee.com/?skin=rec&type=repository&q=cpp-httplib</i></div><div class="item"><a href="#">这是标题</a><p>这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要</p><i>https://search.gitee.com/?skin=rec&type=repository&q=cpp-httplib</i></div><div class="item"><a href="#">这是标题</a><p>这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要</p><i>https://search.gitee.com/?skin=rec&type=repository&q=cpp-httplib</i></div> --></div></div><script>function Search(){// 是浏览器的一个弹出框// alert("hello js!");// 1. 提取数据, $可以理解成就是JQuery的别称let query = $(".container .search input").val();if(query == '' || query == null){return;}console.log("query = " + query); //console是浏览器的对话框,可以用来进行查看js数据//2. 发起http请求,ajax: 属于一个和后端进行数据交互的函数,JQuery中的$.ajax({type: "GET",url: "/s?word=" + query,success: function(data){console.log(data);BuildHtml(data);}});}function BuildHtml(data){if(data == '' || data == null || data == "Not Found"){document.write("搜索内容错误");return;}// 获取html中的result标签let result_lable = $(".container .result");// 清空历史搜索结果result_lable.empty();for( let elem of data){// console.log(elem.title);// console.log(elem.url);let a_lable = $("<a>", {text: elem.title,href: elem.url,// 跳转到新的页面target: "_blank"});let p_lable = $("<p>", {text: elem.desc});let i_lable = $("<i>", {text: elem.url});let div_lable = $("<div>", {class: "item"});a_lable.appendTo(div_lable);p_lable.appendTo(div_lable);i_lable.appendTo(div_lable);div_lable.appendTo(result_lable);}}</script>
</body>
</html>
4. 添加日志
一般会把错误的信息打印到日志中,方便之后找问题。
#pragma once
#include<iostream>
#include<string>#define NORMAL 0
#define WARRING 1
#define DEBUG 2
#define FATAL 3#define LOGMESSAGE(LEVEL,MESSAGE) LogMessage(#LEVEL,MESSAGE,__FILE__,__LINE__);//时间戳变成时间
char* timeChange()
{time_t now=time(nullptr);struct tm* local_time;local_time=localtime(&now);static char time_str[1024];snprintf(time_str,sizeof time_str,"%d-%d-%d %d-%d-%d",local_time->tm_year + 1900,\local_time->tm_mon + 1, local_time->tm_mday,local_time->tm_hour, \local_time->tm_min, local_time->tm_sec);return time_str;
}void LogMessage(std::string level,std::string message,std::string file,int line)
{char* nowtime = timeChange();std::cout << "[" << level << "]" << "[" << nowtime << "]" \<< "[" << message << "]" << "[" << file << " : " << line << "]" << std::endl;
}
可以把之前代码有打印信息的地方,都换成下面这种

5. 补充 去掉暂停词
暂停词在 dict/stop_words.utf8 这个目录下

比如如果你在当前搜索引擎搜索关键字 is,明明是一个boost搜索引擎,你搜它根本没有意义,更何况一个html文件中有很多is,确实没有意义。就拿目前的代码来运行搜索关键字 filesytem是有意义的。

你去搜索is,服务器给这么多搜索结果,但是根本没有意义

因此,我们使用cppjieba进行分词的时候,我们要特殊处理一下,不管是建立索引时用到cppjieba分词,还是搜索时用到cppjieba分词。我们都要把这些暂停词去掉。因为每个地方都在用,所以写成单例。
//Common.h//去掉暂停词
class JiebaUtil
{
private:static JiebaUtil* _inst;static std::mutex _mtx;JiebaUtil():jieba(DICT_PATH, HMM_PATH, USER_DICT_PATH, IDF_PATH, STOP_WORD_PATH){}JiebaUtil(const JiebaUtil&) = delete;JiebaUtil& operator=(const JiebaUtil&) = delete;cppjieba::Jieba jieba;//为了快速查找暂停词使用unordered_map,Value使用bool没有实际意义std::unordered_map<std::string,bool> _stop_words;
public:static JiebaUtil* GetInstance(){if(_inst == nullptr){std::unique_lock<std::mutex> lock;if(_inst == nullptr){_inst = new JiebaUtil;_inst->InitJiebaUtil();}}return _inst;}static void CutString(const std::string &src, std::vector<std::string> *out){GetInstance()->CutStringHelper(src,out);}private:void InitJiebaUtil(){std::ifstream ifs("./dict/stop_words.utf8");if(!ifs.is_open()){LOGMESSAGE(FATAL,"load stop words file error");return;}//读取暂停词到哈希桶std::string line;while(std::getline(ifs,line)){_stop_words.insert({line,true});}ifs.close();}void CutStringHelper(const std::string &src, std::vector<std::string> *out){//先进行分词jieba.CutForSearch(src, *out);//遍历分词结果,去暂停词auto it = out->begin();while(it != out->end()){auto iter = _stop_words.find(*it);if(iter != _stop_words.end()){//删除暂停词,注意迭代器失效it = out->erase(it);}else{++it;}}}
};
JiebaUtil* JiebaUtil::_inst = nullptr;
虽然这样启动很慢,但是你是一个服务器,刚启动慢,查快也很正常。
当我们换上面的代码,然后重新启动服务器,在搜索 is ,直接提示搜索内容错误。

以上就是我们这个项目的所有内容。不过我们上面还是实现的很简单。有兴趣的可以在加点东西。
6. 项目扩展方向
- 建立整站搜索
我们目前只把boost_1_87_0/doc/html/下面的.html文件放到date/input,然后做搜索,但是其他地方也有.html文件。我们可以把boost_1_87_0路径下所有内容拷贝到data下,然后始递归搜索所有.html文件名带路径的时候,src_path直接设置位data,这样就能把所有.html文件和路径名都放到files_list中。

这个时候url头部就变成了下面这个样子
bool ParserUrl(const std::string &file_path, std::string *url)
{//std::string url_head = "https://www.boost.org/doc/libs/1_87_0/doc/html";std::string url_head = "https://www.boost.org/doc/libs/1_87_0";std::string url_tail = file_path.substr(src_path.size());*url = url_head + url_tail;return true;
}
- 设计一个在线更新的方案,用到信号,爬虫,完成整个服务器的设计
- 不使用组件,而是自己设计一下对应的各种方案(有时间,有精力)
- 在我们的搜索引擎中,添加竞价排名(强烈推荐)
- 热次统计,智能显示搜索关键词(字典树,优先级队列)(比较推荐)

6. 设置登陆注册,引入对mysql的使用 (比较推荐)
相关文章:
【boost搜索引擎】下
boost搜索引擎 1. 编写搜索引擎模块 Searcher2. 编写 http_server 模块3. 编写前端模块4. 添加日志5. 补充 去掉暂停词6. 项目扩展方向 1. 编写搜索引擎模块 Searcher 这一模块主要提供建立索引,以及收到用户的发起的http请求通过Get方法提交的搜索关键字ÿ…...
数据结构优化DP总结
单调栈:Codeforces Round 622 (Div. 2) C2. Skyscrapers (hard version) 简单来讲就是最后需要呈现出一个单峰数组,使得总高度最高。 最开始想到暴力枚举每一个元素都充当最高的“单峰”,但是这里的 n 过大,这样枚举肯定会TLE。 …...
[Linux系统编程]进程间通信—system V
进程间通信—system V 1. System V 共享内存(Shared Memory)1.1 共享内存的建立过程1.2 共享内存函数2. System V 消息队列(Message Queues)3. System V 信号量(Semaphores)4. 总结前言: 之前所提的管道通信是基于文件的,OS没有做过多的设计工作。 system V 进程间通信…...
Eigen库几何模块深度解析与实践指南
Eigen库几何模块深度解析与实践指南 a. Eigen几何模块概述 i. 几何模块的核心功能 在三维空间中,几何变换是描述物体位置和姿态变化的基础,其数学基础涵盖了线性代数中的矩阵运算等知识。Eigen库的几何模块为这些变换提供了高效且便捷的实现方式。 旋转、平移和缩放是三维…...
第十四届蓝桥杯大赛软件赛省赛C/C++ 大学 B 组(部分题解)
文章目录 前言日期统计题意: 冶炼金属题意: 岛屿个数题意: 子串简写题意: 整数删除题意: 总结 前言 一年一度的🏀杯马上就要开始了,为了取得更好的成绩,好名字写了下前年2023年蓝桥…...
C语言常见3种排序
主要是三种排序方法:冒泡排序、选择排序、插入排序。 文章目录 一、冒泡排序 1.代码: 2.工作原理: 3.具体过程: 二、选择排序 1.代码 2. 工作原理 3.具体过程: 三、插入排序 1.代码 2.工作原理 3.具体过程 总结 一、…...
分析sys高问题的方法总结
一、背景 sys高的问题往往属于底层同学更需要关注的问题,sys高的问题往往表现为几种情况,一种是瞬间的彪高,一种是持续的彪高。这篇博客里,我们总结一下常用的分析方法和分析工具的使用来排查这类sys高的问题。 二、通过mpstat配…...
智谱发布AI Agent“AutoGLM沉思”,开启AI“边想边干”新时代
近日,智谱正式推出全新AI Agent产品——AutoGLM沉思,标志着人工智能从“思考”迈向“执行”的关键突破。该智能体不仅具备深度研究能力,还能自主完成实际操作,真正实现“边想边干”的智能化应用。 在演示环节,智谱展示…...
使用Leaflet对的SpringBoot天地图路径规划可视化实践-以黄花机场到橘子洲景区为例
目录 前言 一、路径规划需求 1、需求背景 2、技术选型 3、功能简述 二、Leaflet前端可视化 1、内容布局 2、路线展示 3、转折路线展示 三、总结 前言 在当今数字化与智能化快速发展的时代,路径规划技术已经成为现代交通管理、旅游服务以及城市规划等领域的…...
【小兔鲜】day02 Pinia、项目起步、Layout
【小兔鲜】day02 Pinia、项目起步、Layout 1. Pinia2. 添加Pinia到Vue项目3. 案例:Pinia-counter基础使用3.1 Store 是什么?3.2 应该在什么时候使用 Store? 4. Pinia-getters和异步action4.1 getters4.2 action如何实现异步 1. Pinia Pinia 是 Vue 的专…...
PyTorch 激活函数
激活函数是神经网络中至关重要的组成部分,它们为网络引入了非线性特性,使得神经网络能够学习复杂模式。PyTorch 提供了多种常用的激活函数实现。 常用激活函数 1. ReLU (Rectified Linear Unit) 数学表达式: PyTorch实现: torch.nn.ReLU(inplaceFals…...
魔塔社区使用llamafactory微调AI阅卷试题系统
启动 LLaMA-Factory 1. 安装 LLaMA-Factory 执行安装指令 git clone --depth 1 https://github.com/hiyouga/LLaMA-Factory.git cd LLaMA-Factory pip install -e ".[torch,metrics]"解决依赖冲突 如果遇到依赖冲突,可使用以下命令安装,不…...
Java面试黄金宝典29
1. 什么是普通索引和唯一性索引 定义: 普通索引:是最基本的索引类型,它为数据表中的某一列或多列建立索引,以加快数据的查询速度。它不限制索引列的值重复,允许存在多个相同的值。唯一性索引:在普通索引的基…...
git `switch` 命令详解与实用示例
文章目录 git switch 命令详解与实用示例git switch vs git checkoutgit switch 用法1. 切换到已有分支2. 创建并切换到新分支3. 切换到上一个分支4. 切换到远程分支(自动创建本地分支并追踪远程)5. 放弃未提交的修改并切换分支 总结 git switch 命令详解…...
Oracle中文一二三四排序【失败】
原文地址: Oracle数据库如何对中文的一二三四五六七八九十数进行正序排列排序_中文数字排序-CSDN博客 自定义排序函数 -- 自定义中文映射阿拉伯数字函数 CREATE OR REPLACE FUNCTION P_ORDER_CHINESE_TO_ARABIC(V_NUM VARCHAR2) RETURN NUMBER IS BEGIN-- 根据…...
AWS S3 和 Lambda 使用
目录: AWS概述 EMR Serverless AWS VPC及其网络 关于AWS网络架构的思考 AWS S3 和 Lambda 使用 本文将通过一个实例来说明如何使用 AWS S3 和 Lambda。 使用场景:通过代码将文件上传到S3,该文件需要是公开访问的,并对上传的文件进…...
Mysql 在什么样的情况下会产生死锁?
在 MySQL 中,死锁是指两个或多个事务相互等待对方释放锁,导致所有相关事务无法继续执行的情况。死锁会影响数据库的并发性能,因此需要及时检测并处理。假设有两个事务 T1 和 T2: 事务 T1 首先锁定 表 A 的行 1。然后尝试锁定 表 B…...
符号秩检验
内容来源 非参数统计(第2版) 清华大学出版社 王星 褚挺进 编著 符号秩检验 在符号检验的基础上,增加了数据绝对值大小的信息 检验统计量 用一个简单的例子来说明 样本数据 X i , i 1 , ⋯ , 6 X_i,i1,\cdots,6 Xi,i1,⋯,6 如下 X …...
RainbowDash 的 Robot
H RainbowDash 的 Robot - 第七届校赛正式赛 —— 补题 题目大意: 给一个 n ∗ m n*m n∗m 的二维网格,在第 i i i 列中,前 a i a_i ai 单元格被阻断,无法通行,即 [ 1 , a i ] [1,a_i] [1,ai] 。 一个机器人正…...
yum repolist all全部禁用了 怎么办
文章目录 步骤思考解决yum仓库全部被禁用的问题步骤思考: 检查仓库状态:运行yum repolist all,查看所有仓库的启用状态。 被禁用的仓库会显示为disabled。 启用所有仓库:可以逐一启用,或者使用命令批量启用。 例如使用yum-config-manager --enable ‘*’,但需要注意是否有…...
SQL WHERE 与 HAVING
WHERE 和 HAVING 都是 SQL 中用于筛选数据的子句,但它们有重要的区别 WHERE 子句 在 分组前 过滤数据 作用于 原始数据行 不能使用聚合函数 执行效率通常比 HAVING 高 SELECT column1, column2 FROM table WHERE condition; HAVING 子句 在 分组后 过滤数据 …...
如何在 Unity3D 导入 Spine 动画
一、前言 《如何在 Unity3D 项目中导入 Spine 动画》,虽然在网上有很多这种文章,直接将问题交给 DeepSeek 也能得到具体的操作流程,但是照着他们提供的方法还是能遇到几个问题,比如: AI 回答没有提到 Unity 无法识别.…...
子网划分2
子网分配的问题,下列vlsm如何设置? 某公司申请了一个C类202.60.31.0的IP地址,要求设置三个子网,一个为100台主机,一个为50台主机,另一个为50台主机,用VLSM如何设置? 哪位高手指教一…...
C++的UDP连接解析域名地址错误
背景 使用c开发一个udp连接功能的脚本,可以接收发送数据,而且地址是经过内网穿透到外网的 经过 通常发送数据给目标地址,需要把目的地址结构化,要么使用inet_addr解析ip地址,要么使用inet_pton sockaddr_in target…...
23种设计模式中的观察者模式
定义了一种一对多的依赖关系,当一个对象的状态发生改变时,其所有依赖者都会收到通知并自动更新。 观察者模式是一种发布-订阅模式。它让发送通知的一方(被观察者)和接收通知的一方(观察者)能够解耦…...
论文笔记:ASTTN模型
研究现状 现有研究大多通过分别考虑空间相关性和时间相关性或在滑动时间窗口内对这种时空相关性进行建模,而未能对直接的时空相关性进行建模。受最近图领域Transformer成功的启发,该模型提出利用局部多头自关注,在自适应时空图上直接建立跨时…...
Java单例模式详解
单例模式详解 一、单例模式概述 单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来访问这个实例。 核心特点 唯一实例:保证一个类只有一个实例存在全局访问:提供统一的访问入…...
Linux命令-tar
tar 命令的完整参数列表: 参数 描述 -c 创建新的归档文件 -x 解压归档文件 -t 列出归档文件内容 -r 追加文件到归档文件 -u 更新归档文件中的文件 -d 从归档文件中删除文件 -f 指定归档文件的名称 -v 显示详细信息(verbose) -z 使用 gzip 压缩…...
深入解析 Git Submodule:从基础到高级操作指南
深入解析 Git Submodule:从基础到高级操作指南 一、Git Submodule 是什么? git submodule 是 Git 提供的一个强大功能,允许在一个 Git 仓库(主仓库)中嵌入另一个独立的 Git 仓库(子模块)。主仓…...
2025-4-2 蓝桥杯刷题情况(分布式队列)
1.题目描述 小蓝最近学习了一种神奇的队列:分布式队列。简单来说,分布式队列包含 N 个节点(编号为0至N-1,其中0号为主节点),其中只有一个主节点,其余为副节点。 主/副节点中都各自维护着一个队列,当往分布式队列中添加…...
