【编译原理实验二】——自动机实验:NFA转DFA并最小化
本篇适用于ZZU的编译原理课程实验二——自动机实验:NFA转DFA并最小化,包含了实验代码和实验报告的内容,读者可根据需要参考完成自己的程序设计。
如果是ZZU的学弟学妹看到这篇,那么恭喜你,你来对地方啦!
如果需要相关文档资料的话,我也已经上传,免费下载:「编译原理实验二代码+实验报告(ZZU)」
不要忘了点赞👍和收藏💌支持一下哦!
源代码
先给出实验的源代码
#include <iostream>
#include <fstream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <sstream>
using namespace std;// NFA类定义
struct NFA {set<int> states; // 状态集合set<int> alphabet; // 字母表map<pair<int,int>, set<int>> transitions; // 转移函数 F(fromState, symbol)={toStates}int start_state{}; // 初始状态set<int> accept_states; // 接受状态集合
};// DFA类定义
struct DFA {set<int> states; // 状态集合set<int> alphabet; // 字母表map<pair<int,int>, int> transitions; // 转移函数int start_state{}; // 初始状态set<int> accept_states; // 接受状态集合
};// 从文件读取NFA
NFA readNFAFromFile(const string& filename) {NFA nfa;ifstream file(filename);string line;// 读取状态集合getline(file, line);istringstream iss(line); // 将一行数据转换成一个输入流,随后可以像处理文件或标准输入一样从`iss`中提取数据int state;while (iss >> state) { // 从转换好后的每一行输入中,逐个读取整数作为状态`state`nfa.states.insert(state);}// 读取字母表getline(file, line);iss.clear(); // 重置流状态iss.str(line); // 读取新的一行作为流int symbol; // 输入的标志字母while (iss >> symbol) {nfa.alphabet.insert(symbol);}// 读取转移规则数量int trans_count;file >> trans_count;// 读取转移规则for (int i = 0; i < trans_count; i++) {int from_state, now_symbol; // 当前状态,转换字母file >> from_state >> now_symbol; // 从文件中读取set<int> to_states_set; // 目标状态集合int to_state; // 目标状态// 读取目标状态,添加到到目标状态集合while (file.get() != '\n' && file >> to_state) {to_states_set.insert(to_state);}nfa.transitions[{from_state, now_symbol}] = to_states_set; // 转移函数 F(fromState, symbol)={toState}}// 读取初始状态和接受状态file >> nfa.start_state;int accept_state;file >> accept_state;nfa.accept_states.insert(accept_state);return nfa;
}// 获取ε-闭包集合
set<int> getEpsilonClosure(const NFA& nfa, const set<int>& states) {set<int> closure = states;// 队列进行存储状态集合statesqueue<int> q;for (int state : states) {q.push(state);}while (!q.empty()) {// 从前往后弹出状态int current = q.front();q.pop();// 对每个状态进行判断闭包auto it = nfa.transitions.find({current, -1}); // 查找转换函数中的当前状态的所有ε边if (it != nfa.transitions.end()) {for (int next : it->second) { // it中的元素为键值对类型pair,second就是获取键值对的后一个值if (closure.find(next) == closure.end()) { // 未被记录进闭包集合closure.insert(next);q.push(next);}}}}return closure;
}// 合并两个NFA
NFA mergeNFAs(const NFA& nfa1, const NFA& nfa2) {NFA merged;// 找到最大状态编号int max_state = 0;for (int state : nfa1.states) max_state = max(max_state, state);for (int state : nfa2.states) max_state = max(max_state, state);// 新的起始状态int new_start = max_state + 1;merged.start_state = new_start;// 合并状态集merged.states = nfa1.states;merged.states.insert(nfa2.states.begin(), nfa2.states.end());merged.states.insert(new_start);// 合并字母表merged.alphabet = nfa1.alphabet;merged.alphabet.insert(nfa2.alphabet.begin(), nfa2.alphabet.end());// 合并转移函数merged.transitions = nfa1.transitions;for (const auto& trans : nfa2.transitions) {merged.transitions[trans.first] = trans.second;}// 添加从新起始状态到原始NFA起始状态的ε转移merged.transitions[{new_start, -1}].insert(nfa1.start_state);merged.transitions[{new_start, -1}].insert(nfa2.start_state);// 合并接受状态merged.accept_states = nfa1.accept_states;merged.accept_states.insert(nfa2.accept_states.begin(), nfa2.accept_states.end());return merged;
}// NFA转换为DFA
DFA convertNFAtoDFA(const NFA& nfa) {DFA dfa;map<set<int>, int> dfa_states; // nfa状态集 --> dfa的一个状态queue<set<int>> unprocessed_states;// 初始化DFAdfa.alphabet = nfa.alphabet;// 求DFA的起始状态set<int> initial_state = getEpsilonClosure(nfa, {nfa.start_state});dfa_states[initial_state] = 0;dfa.start_state = 0;unprocessed_states.push(initial_state); // 将dfa起始状态集加入已处理状态队列// 使用子集构造法构建DFAwhile (!unprocessed_states.empty()) {set<int> current_state = unprocessed_states.front();unprocessed_states.pop();int dfa_state = dfa_states[current_state];// 检查是否为接受状态for (int state : current_state) {if (nfa.accept_states.find(state) != nfa.accept_states.end()) {dfa.accept_states.insert(dfa_state);break;}}// 对每个输入符号构造转移for (int symbol : nfa.alphabet) {set<int> next_state;// 将当前状态的多条转移合并for (int state : current_state) {auto it = nfa.transitions.find({state, symbol});if (it != nfa.transitions.end()) {next_state.insert(it->second.begin(), it->second.end()); // 多条转移的终点合并加入到当前状态集合的目标状态集合// 实现多条转移合并为一条集合与集合之间的转移}}// 计算ε-闭包next_state = getEpsilonClosure(nfa, next_state);if (!next_state.empty()) {// 判断是否是新状态if (dfa_states.find(next_state) == dfa_states.end()) {int new_state = dfa_states.size(); // 为新状态进行编号dfa_states[next_state] = new_state;unprocessed_states.push(next_state);}dfa.transitions[{dfa_state, symbol}] = dfa_states[next_state];}}}// 设置DFA状态集for (const auto& state : dfa_states) {dfa.states.insert(state.second);}return dfa;
}// DFA最小化
DFA minimizeDFA(const DFA& dfa) {// 初始划分:接受状态和非接受状态vector<set<int>> partitions(2);map<int, int> partition_map;for (int state : dfa.states) {if (dfa.accept_states.find(state) != dfa.accept_states.end()) {partitions[0].insert(state);partition_map[state] = 0;} else {partitions[1].insert(state);partition_map[state] = 1;}}bool changed; // 标记划分是否改变do {changed = false;vector<set<int>> new_partitions; // 新的划分for (const auto& partition : partitions) {if (partition.size() <= 1) {new_partitions.push_back(partition);continue;}map<vector<int>, set<int>> subdivision; // 子划分for (int state : partition) {vector<int> parts; // 划分号for (int symbol : dfa.alphabet) {auto it = dfa.transitions.find({state, symbol});if (it != dfa.transitions.end()) {parts.push_back(partition_map[it->second]);} else {parts.push_back(-1);}}subdivision[parts].insert(state);}for (const auto& sub : subdivision) {new_partitions.push_back(sub.second);if (sub.second.size() != partition.size()) {changed = true;}}}// 发生了改变if (changed) {partitions = new_partitions;partition_map.clear();for (size_t i = 0; i < partitions.size(); i++) {for (int state : partitions[i]) {partition_map[state] = i;}}}} while (changed);// 构建最小化DFADFA min_dfa;min_dfa.alphabet = dfa.alphabet;// 映射旧状态到新状态map<int, int> state_map;int new_state_id = 0;for (const auto& partition : partitions) {for (int state : partition) {if (state_map.find(state) == state_map.end()) {state_map[state] = new_state_id;min_dfa.states.insert(new_state_id);if (state == dfa.start_state) {min_dfa.start_state = new_state_id;}if (dfa.accept_states.find(state) != dfa.accept_states.end()) {min_dfa.accept_states.insert(new_state_id);}new_state_id++;}}}// 构建新的转移函数for (const auto& trans : dfa.transitions) {int from_state = state_map[trans.first.first];int symbol = trans.first.second;int to_state = state_map[trans.second];min_dfa.transitions[{from_state, symbol}] = to_state;}return min_dfa;
}// 打印DFA
void printDFA(const DFA& dfa) {cout << "DFA\n";cout << " 状态集:{";for (auto it = dfa.states.begin(); it != dfa.states.end(); ++it) {if (it != dfa.states.begin()) cout << ",";cout << *it;}cout << "}\n";cout << " 符号表:{";for (auto it = dfa.alphabet.begin(); it != dfa.alphabet.end(); ++it) {if (it != dfa.alphabet.begin()) cout << ",";cout << *it;}cout << "}\n";cout << " 状态转换:\n";for (const auto& trans : dfa.transitions) {cout << " (" << trans.first.first << "," << trans.first.second << ")->" << trans.second << "\n";}cout << " 开始状态:" << dfa.start_state << "\n";cout << " 结束状态集:{";for (auto it = dfa.accept_states.begin(); it != dfa.accept_states.end(); ++it) {if (it != dfa.accept_states.begin()) cout << ",";cout << *it;}cout << "}\n";
}int main() {// 从文件读取NFANFA nfa1 = readNFAFromFile("experiment02_input1.txt");NFA nfa2 = readNFAFromFile("experiment02_input2.txt");// 合并NFANFA merged_nfa = mergeNFAs(nfa1, nfa2);// 转换为DFADFA dfa = convertNFAtoDFA(merged_nfa);// 最小化DFADFA min_dfa = minimizeDFA(dfa);// 输出结果printDFA(min_dfa);return 0;
}
实验报告
接下来是实验报告的内容,希望能帮助读者理解词法分析程序的设计思路,以及完成实验报告的撰写。
一.实验目的
- 理解和掌握把问题中的实体转换成抽象模型中数据结构的能力,设计确定有穷自动机DFA和非确定有穷自动机NFA描述的对象模型或数据结构,实现DFA和NFA的基本操作(输入和输出);
- 掌握将多个NFA合并的方法;
- 掌握将NFA确定化成DFA的方法;
- 掌握将DFA最小化的方法。
加深对自动机的理解。
二.问题描述
-
需要实现的功能
(1)设计一个函数(方法),实现把两个NFA的合并;
(2)设计一个函数(方法),实现把NFA确定化成一个DFA;
(3)设计一个函数(方法),实现把DFA最小化;
(4)输入多个NFA:NFA描述存储在文本文件中,文件名作为命令行参数输入;
(5)输出合并、最小化以后的DFA到标准输出设备。 -
实现原理
2.1 NFA合并
(1)创建新的开始状态
(2)通过ε-转换连接到原NFA的开始状态
(3)合并状态集、字母表、转移函数和接受状态
2.2 NFA确定化
(1)使用子集构造法
(2)计算ε-闭包
(3)构造新的状态转移函数
2.3 DFA最小化
(1)基于等价类的划分算法
(2)初始划分为接受状态和非接受状态
(3)迭代细化状态划分直至稳定
三.软件设计方法的选择
-
设计方法
采用结构化设计方法,主要是考虑到了:
(1)问题本身具有清晰的数据流向和处理流程
(2)功能模块划分明确
(3)算法实现较为直观 -
各阶段创建的模型
2.1 分析阶段:
(1)系统流程图
(2)数据流图
(3)数据字典
2.2 设计阶段:
(4)模块结构图
(5)数据结构设计
(6)算法流程图 -
开发环境
编程语言: C++11
编译器: g++
开发工具: CLion
依赖库: STL标准模板库
四.分析模型
- 系统流程图
描述:
开始:实验的起始点,系统准备开始执行。
读取NFA1与读取NFA2:从输入文件中读取两个非确定有穷自动机(NFA)的描述信息。
合并NFA:将两个NFA进行合并,创建一个新的NFA,合并后的NFA需要合并状态集、字母表、转移函数等。
转换为DFA:通过子集构造法将NFA转换为确定性有限自动机(DFA)。
最小化DFA:对转换后的DFA进行最小化,减少状态数并简化自动机结构。
输出结果:输出最小化后的DFA描述,显示其状态集、转移函数等信息。
结束:实验完成。

2.数据流图
描述:
NFA描述文件:输入文件,包含NFA的定义,如状态集、转移函数、字母表、起始状态和接受状态等。
读取NFA:读取输入文件中的NFA描述并将其转化为程序内部使用的NFA数据结构。
NFA合并:将多个NFA合并为一个新的NFA,该步骤是通过创建一个新的起始状态并加入适当的ε转移来实现的。
NFA转DFA:通过子集构造法将NFA转换成DFA。此过程涉及计算状态的ε-闭包,并构建转移函数。
DFA最小化:对DFA进行最小化处理,使用等价类划分法将DFA的状态集划分为等价类,合并等价状态,减少状态数。
标准输出:输出最小化后的DFA的各项信息,包括状态集、接受状态、转移函数。
3.数据字典
3.1 NFA结构
NFA = {states: 状态集合alphabet: 字母表transitions: 转移函数start_state: 初始状态accept_states: 接受状态集合
}
描述:
states:NFA中的状态集合。
alphabet:NFA的输入字母表,包含所有可能的输入符号。
transitions:转移函数,定义了从一个状态出发,在特定输入下转移到哪些状态(包括ε转移)。
start_state:NFA的起始状态,是计算开始的状态。
accept_states:NFA的接受状态集合,表示能接受输入字符串的状态。
3.2 DFA结构
DFA = {states: 状态集合alphabet: 字母表transitions: 转移函数start_state: 初始状态accept_states: 接受状态集合
}
描述:
states:DFA中的状态集合。
alphabet:DFA的输入字母表,通常与NFA的字母表相同。
transitions:转移函数,定义了在每个状态下,输入符号如何转移到另一个状态。
start_state:DFA的起始状态。
accept_states:DFA的接受状态集合,与NFA的接受状态集合可能不同。
五.设计模型
- 模块结构图
描述:
主程序:程序的核心模块,负责协调其他模块的工作。
文件读取模块:负责从文件中读取NFA的描述,并将其转化为NFA结构。
readNFAFromFile:实现从文件中读取NFA信息,并构造NFA数据结构。
NFA处理模块:负责处理NFA的操作。
mergeNFAs:合并两个NFA,生成一个新的NFA。
getEpsilonClosure:计算NFA状态的ε-闭包。
DFA处理模块:负责将NFA转换为DFA,并对DFA进行最小化。
convertNFAtoDFA:将NFA转换为DFA,使用子集构造法。
minimizeDFA:对DFA进行最小化,减少冗余状态。
输出模块:负责输出最小化后的DFA描述。
printDFA:输出DFA的状态集、接受状态、转移函数等信息。
- 主要数据结构
// NFA结构体
struct NFA {set<int> states; set<int> alphabet; map<pair<int,int>, set<int>> transitions; int start_state; set<int> accept_states;
};// DFA结构体
struct DFA {set<int> states; set<int> alphabet; map<pair<int,int>, int> transitions; int start_state; set<int> accept_states;
};
描述:
NFA结构体:定义了NFA的数据结构,其中包括状态集、字母表、转移函数、起始状态和接受状态集。
DFA结构体:定义了DFA的数据结构,类似于NFA,但其转移函数是确定性的,即每个状态对于每个输入符号只有一个确定的转移。
- 主要函数接口
(1)readNFAFromFile:从文件中读取NFA描述,并返回构建好的NFA结构。
(2)getEpsilonClosure:计算给定状态集的ε-闭包,返回一个新的状态集。
(3)mergeNFAs:合并两个NFA,返回合并后的NFA。
(4)convertNFAtoDFA:将给定的NFA转换为DFA。
(5)minimizeDFA:对给定的DFA进行最小化处理,返回最小化后的DFA。
(6)printDFA:输出最小化后的DFA的状态集、转移函数、接受状态等。
六.主要算法描述
- NFA合并算法
NFA算法描述:
寻找最大状态编号:在合并NFA时,首先需要确定新的NFA中的状态编号,应避免与原有NFA中的状态编号冲突。
创建新起始状态:为合并后的NFA创建一个新的起始状态。
合并状态集、字母表、转移函数、接受状态:将两个NFA的状态、字母表、转移函数和接受状态集合并成一个新的NFA。
添加ε转移:通过添加适当的ε转移连接原NFA的起始状态。

- NFA转化DFA算法
NFA转化DFA算法描述:
计算初始状态ε-闭包:使用ε-闭包计算NFA初始状态的所有可达状态。
初始化DFA:创建一个新的DFA,并将NFA的ε-闭包作为DFA的初始状态。
判断未处理状态队列空:DFA构造过程中需要遍历所有状态,直到没有状态可处理为止。
处理所有输入符号:对于当前状态,遍历所有输入符号,计算对应的下一状态。
更新DFA转移函数:将状态和符号的转移结果添加到DFA的转移函数中。

- DFA最小化算法
DFA最小化算法描述:
初始划分:最初将状态划分为接受状态和非接受状态。
需要继续划分:检查当前划分是否需要进一步细化。若有相同的转移模式的状态,则需要进一步划分。
对每个划分进行细化:将状态按照它们的转移特征进行细化。
更新划分映射:更新状态划分的映射关系,确保最小化后的状态集满足等价类划分的要求。

-
测试数据格式
输入多个NFA:NFA描述存储在文本文件中,文件名作为命令行参数输入 -
测试数据与测试效果
(1)测试数据1
NFA1:
0 1 2 3 4 5 6 7 8 9 10
0 110
0 -1 1 7
1 -1 2 4
2 0 3
3 -1 6
4 1 5
5 -1 6
6 -1 1 7
7 0 8
8 1 9
9 1 100
10NFA2:
11 12 13 14 15 16 17 18
0 18
11 -1 12 13
12 0 14
13 1 15
14 -1 16
15 -1 16
16 0 17
17 1 1811
18
测试效果:
(2)测试数据2
NFA1:
0 1 2 3 4 5 6 7 8 9 10
0 16
0 -1 1 5
1 -1 2 4
2 0 3
3 -1 4
4 1 5
5 -1 60
6NFA2:
0 1 2 3 4 5 6 7 8 9 10
0 16
0 -1 1 5
1 -1 2 4
2 0 3
3 -1 4
4 1 5
5 -1 60
6
测试效果:
根据实验效果,成功输出合并、最小化以后的DFA到标准输出设备。
八.实验总结
-
遇到的问题及解决方法
(1)ε-闭包计算问题:
问题:初始实现时未考虑递归计算ε-闭包
解决:使用队列实现广度优先搜索,确保完整计算闭包
(2)DFA最小化过程中的等价类划分:
问题:划分过程中状态映射更新不及时
解决:每次划分后立即更新状态映射关系
(3)内存管理问题:
问题:大规模NFA转换时内存占用过大
解决:使用STL容器自动管理内存,避免手动内存管理 -
收获与体会
深入理解了自动机理论的实际应用,掌握了复杂算法的工程实现方法,提高了数据结构和算法设计能力,学会了使用STL容器进行高效的数据处理。
相关文章:
【编译原理实验二】——自动机实验:NFA转DFA并最小化
本篇适用于ZZU的编译原理课程实验二——自动机实验:NFA转DFA并最小化,包含了实验代码和实验报告的内容,读者可根据需要参考完成自己的程序设计。 如果是ZZU的学弟学妹看到这篇,那么恭喜你,你来对地方啦! 如…...
Hive:复杂数据类型之Map函数
Map函数 是Hive里面的一种复杂数据类型, 用于存储键值对集合。Map中的键和值可以是基础类型或复合类型,这使得Map在处理需要关联存储信息的数据时非常有用。 定义map时,需声明2个属性: key 和 value , map中是 key value 组成一个元素 key-value, key必须为原始类…...
C++ 中的引用(Reference)
在 C 中,引用(Reference)是一种特殊的变量类型,它提供了一个已存在变量的别名。引用在很多场景下都非常有用,比如函数参数传递、返回值等。下面将详细介绍 C 引用的相关知识。 1. 引用的基本概念和语法 引用是已存在…...
密码学的数学基础1-整数 素数 和 RSA加密
数学公式推导是密码学的基础, 故开一个新的课题 – 密码学的数学基础系列 素数 / 质数 质数又称素数。 一个大于1的自然数,除了1和它自身外,不能被其他自然数整除的数叫做质数;否则称为合数(规定1既不是质数也不是合数࿰…...
Java锁自定义实现到aqs的理解
专栏系列文章地址:https://blog.csdn.net/qq_26437925/article/details/145290162 本文目标: 理解锁,能自定义实现锁通过自定义锁的实现复习Thread和Object的相关方法开始尝试理解Aqs, 这样后续基于Aqs的的各种实现将能更好的理解 目录 锁的…...
STM32-时钟树
STM32-时钟树 时钟 时钟...
android 圆形弹窗摄像头开发踩坑——源码————未来之窗跨平台操作
一、飘窗刷脸,拍照采用飘窗 刷脸认证安卓接口采用飘窗具有在不干扰用户主要操作的前提下以醒目方式引导用户完成认证,且能灵活定制样式以提升用户体验和认证效率的优点 二、踩坑只有一个扇形 <?xml version"1.0" encoding"utf-8&quo…...
markdown公式特殊字符
个人学习笔记 根号 在 Markdown 中,要表示根号 3,可以使用 LaTeX 语法来实现。常见的有以下两种方式: 行内公式形式:使用一对美元符号 $ 将内容包裹起来,即 $\sqrt{3}$ ,在支持 LaTeX 语法渲染的 Markdow…...
Web-3.0学习路线
方向学习内容✅ 区块链基础区块链、智能合约、共识机制✅ 智能合约Solidity / Rust(Ethereum / Solana)✅ 前端React.js, Next.js, Web3.js, ethers.js✅ 后端Node.js, Python, Golang(链上数据)✅ 存储IPFS, Arweave, Filecoin&a…...
【算法设计与分析】实验5:贪心算法—装载及背包问题
目录 一、实验目的 二、实验环境 三、实验内容 四、核心代码 五、记录与处理 六、思考与总结 七、完整报告和成果文件提取链接 一、实验目的 掌握贪心算法求解问题的思想;针对不同问题,会利用贪心算法进行问题建模、求解以及时间复杂度分析&#x…...
使用 cmake
使用前注意 : CMake是一种跨平台的构建系统,它用于管理软件构建过程,尤其适合多语言、多配置的项目。CMake不直接构建软件,而是生成特定构建工具(如Makefile或Visual Studio项目)所需的配置文件。 如果仅仅使用 qt 编…...
万物皆有联系:驼鸟和布什
布什?一块布十块钱吗?不是,大家都知道,美国有两个总统,叫老布什和小布什,因为两个布什总统(父子俩),大家就这么叫来着,目的是为了好区分。 布什总统的布什&a…...
PHP实现混合加密方式,提高加密的安全性(代码解密)
代码1: <?php // 需要加密的内容 $plaintext 授权服务器拒绝连接;// 1. AES加密部分 $aesKey openssl_random_pseudo_bytes(32); // 生成256位AES密钥 $iv openssl_random_pseudo_bytes(16); // 生成128位IV// AES加密(CBC模式)…...
分层多维度应急管理系统的设计
一、系统总体架构设计 1. 六层体系架构 #mermaid-svg-QOXtM1MnbrwUopPb {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-QOXtM1MnbrwUopPb .error-icon{fill:#552222;}#mermaid-svg-QOXtM1MnbrwUopPb .error-text{f…...
FFmpeg源码:av_base64_decode函数分析
一、引言 Base64(基底64)是一种基于64个可打印字符来表示二进制数据的表示方法。由于log2 646,所以每6个比特为一个单元,对应某个可打印字符。3个字节相当于24个比特,对应于4个Base64单元,即3个字节可由4个…...
算法随笔_30: 去除重复字母
上一篇:算法随笔_29:最大宽度坡_方法3-CSDN博客 题目描述如下: 给你一个字符串 s ,请你去除字符串中重复的字母,使得每个字母只出现一次。需保证 返回结果的字典序最小(要求不能打乱其他字符的相对位置)。 示例 1: …...
fpga系列 HDL:XILINX Vivado Vitis 高层次综合(HLS) 实现 EBAZ板LED控制(上)
目录 创建工程创建源文件并编写C代码C仿真综合仿真导出RTL CG导出RTL错误处理: 创建工程 创建源文件并编写C代码 创建源文件(Souces下的hlsv.h和hlsv.cpp,Test Bench下的test_hlsv1.cpp): hlsv1.h #ifndef HLSV1 #define HLSV1 #include &l…...
企业微信SCRM推动企业数字化转型实现高效客户管理与营销效益提升
内容概要 在当今数字化转型的大背景下,企业微信SCRM逐渐成为推动企业高效客户管理和提升营销效益的重要工具。说到SCRM,首先要了解它的定义。SCRM即社交化客户关系管理,通过整合社交媒体与客户管理,帮助企业更好地理解和服务客户…...
C++ 7
vector 底层原理和扩容过程 底层原理 ● vector 是 C 标准库中的一个容器,可以看作是一个动态数组,它的大小可以根据元素的增加而增长。它通过在堆上分配一段连续的内存空间存放元素,支持时间复杂度为 O(1 ) 的随机访问。 ● vec…...
论文阅读(七):贝叶斯因果表型网络解释遗传变异和生物学知识
1.论文链接:Bayesian Causal Phenotype Network Incorporating Genetic Variation and Biological Knowledge 摘要: 在分离群体中,数量性状基因座(QTL)定位可以确定对表型有因果效应的QTL。这些方法的一个共同特点是Q…...
数据库之PostgreSQL详解
一、PostgreSQL介绍 PostgreSQL是一个功能强大的 开源 的关系型数据库。底层基于C实现。 PostgreSQL的开源协议和Linux内核版本的开源协议是一样的。。BDS协议,这个协议基本和MIT开源协议一样,说人话,就是你可以对PostgreSQL进行一些封装&a…...
计算机毕业设计Python+CNN卷积神经网络高考推荐系统 高考分数线预测 高考爬虫 协同过滤推荐算法 Vue.js Django Hadoop 大数据毕设
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...
可被electron等调用的Qt截图-录屏工具【源码开放】
1. 工具功能简介: (1)、QT5.15.2截图工具(exe)可单独使用或嵌入IM(嵌入方法参照:https://gitee.com/lykiao/yfscreenshot_release) (2)、支持通过Windows消息通知截图成功或取消 (3)、支持圆形、矩形、线条…...
【Python蓝桥杯备赛宝典】
文章目录 一、基础数据结构1.1 链表1.2 队列1.3 栈1.4 二叉树1.5 堆二、基本算法2.1 算法复杂度2.2 尺取法2.3 二分法2.4 三分法2.5 倍增法和ST算法2.6 前缀和与差分2.7 离散化2.8 排序与排列2.9 分治法2.10贪心法1.接水时间最短问题2.糖果数量有限问题3.分发时间最短问题4.采摘…...
.cc扩展名是什么语言?C语言必须用.c为扩展名吗?主流编程语言扩展名?Java为什么不能用全数字的文件名?
.cc扩展名是什么语言? .cc是C语言使用的扩展名,一种说法是它是c with class的简写,当然C语言使用的扩展名不止.cc和.cpp, 还包含.cxx, .c, .C等,这些在不同编译器系统采用的默认设定不同,需要区分使用。当然,编译器提…...
基于 Redis GEO 实现条件分页查询用户附近的场馆列表
🎯 本文档详细介绍了如何使用Redis GEO模块实现场馆位置的存储与查询,以支持“附近场馆”搜索功能。首先,通过微信小程序获取用户当前位置,并将该位置信息与场馆的经纬度数据一同存储至Redis中。利用Redis GEO高效的地理空间索引能…...
项目练习:重写若依后端报错cannot be cast to com.xxx.model.LoginUser
文章目录 一、情景说明二、解决办法 一、情景说明 在重写若依后端服务的过程中 使用了Redis存放LoginUser对象数据 那么,有存就有取 在取值的时候,报错 二、解决办法 方法1、在TokenService中修改如下 getLoginUser 方法中:LoginUser u…...
Python算法详解:贪心算法
贪心算法(Greedy Algorithm)是一种通过选择当前最优解以期望达到全局最优解的算法思想。它在每一步选择时只考虑当前状态下的局部最优,而不关心全局问题的复杂性。这种算法简单高效,适用于某些特定问题,尤其是存在贪心…...
5.3.2 软件设计原则
文章目录 抽象模块化信息隐蔽与独立性衡量 软件设计原则:抽象、模块化、信息隐蔽。 抽象 抽象是抽出事物本质的共同特性。过程抽象是指将一个明确定义功能的操作当作单个实体看待。数据抽象是对数据的类型、操作、取值范围进行定义,然后通过这些操作对数…...
sublime_text的快捷键
sublime_text的快捷键 向下复制, 复制光标所在整行并插入到下一行:通过 CtrlShiftD 实现快速复制当前行的功能。 可选多行, 不选则复制当前行 ctrl Shift D 删除当前行:通过 CtrlShiftK 实现快速删除当前行的功能。 可选多行, 不选则删当前行 ctrl S…...
【项目集成Husky】
项目集成Husky 安装初始化 Husky在.husky → pre-commit文件中添加想要执行的命令 安装 使用 Husky 可以帮助你在 Git 钩子中运行脚本,例如在提交代码前运行测试或格式化代码pnpm add --save-dev husky初始化 Husky npx husky init这会在项目根目录下创建一个 .hu…...
本地运行大模型效果及配置展示
电脑上用ollama安装了qwen2.5:32b,deepseek-r1:32b,deepseek-r1:14b,llama3.1:8b四个模型,都是Q4_K_M量化版。 运行过程中主要是cpu和内存负载比较大,qwen2.5:32b大概需要22g,deepseek-r1:32b类…...
数据结构与算法 —— 常用算法模版
数据结构与算法 —— 常用算法模版 二分查找素数筛最大公约数与最小公倍数 二分查找 人间若有天堂,大马士革必在其中;天堂若在天空,大马士革必与之齐名。 —— 阿拉伯谚语 算法若有排序,二分查找必在其中;排序若要使用…...
进阶数据结构——高精度运算
目录 前言一、高精度运算的定义与背景二、高精度运算的实现方式三、高精度运算的算法实现四、高精度运算的应用场景五、代码模版(c)六、经典例题1.[高精度加法](https://www.lanqiao.cn/problems/1516/learning/?page1&first_category_id1&name…...
第一届“启航杯”网络安全挑战赛WP
misc PvzHE 去这个文件夹 有一张图片 QHCTF{300cef31-68d9-4b72-b49d-a7802da481a5} QHCTF For Year 2025 攻防世界有一样的 080714212829302316092230 对应Q 以此类推 QHCTF{FUN} 请找出拍摄地所在位置 柳城 顺丰 forensics win01 这个软件 云沙盒分析一下 md5 ad4…...
前端八股CSS:盒模型、CSS权重、+与~选择器、z-index、水平垂直居中、左侧固定,右侧自适应、三栏均分布局
一、盒模型 题目:简述CSS的盒模型 答:盒模型有两种类型,可以通过box-sizing设置 1.标准盒模型(content-box):默认值,宽度和高度只包含内容区域,不包含内边距、边框和外边距。 2.边框盒模型&a…...
使用 Tauri 2 + Next.js 开发跨平台桌面应用实践:Singbox GUI 实践
Singbox GUI 实践 最近用 Tauri Next.js 做了个项目 - Singbox GUI,是个给 sing-box 用的图形界面工具。支持 Windows、Linux 和 macOS。作为第一次接触这两个框架的新手,感觉收获还蛮多的,今天来分享下开发过程中的一些经验~ 为啥要做这个…...
Windows程序设计10:文件指针及目录的创建与删除
文章目录 前言一、文件指针是什么?二、设置文件指针的位置:随机读写,SetFilePointer函数1.函数说明2.函数实例 三、 目录的创建CreateDirectory四、目录的删除RemoveDirectory总结 前言 Windows程序设计10:文件指针及目录的创建与…...
Rk3588芯片介绍(含数据手册)
芯片介绍:RK3588是一款低功耗,高性能的处理器,适用于基于arm的PC和边缘计算设备,个人移动互联网设备和其他数字多媒体应用,集成了四核Cortex-A76和四核Cortex-A55以及单独的NEON协处理器 视频处理方面:提供…...
golang 使用双向链表作为container/heap的载体
MyHeap:container/heap的数据载体,需要实现以下方法: Len:堆中数据个数 Less:第i个元素 是否必 第j个元素 值小 Swap:交换第i个元素和 第j个元素 Push:向堆中追加元素 Pop:从堆…...
HTML DOM 修改 HTML 内容
HTML DOM 修改 HTML 内容 引言 HTML DOM(文档对象模型)是浏览器内部用来解析和操作HTML文档的一种机制。通过DOM,我们可以轻松地修改HTML文档的结构、样式和行为。本文将详细介绍如何使用HTML DOM来修改HTML内容,包括元素的增删改查、属性修改以及事件处理等。 1. HTML …...
基于51单片机和WS2812B彩色灯带的流水灯
目录 系列文章目录前言一、效果展示二、原理分析三、各模块代码四、主函数总结 系列文章目录 前言 用彩色灯带按自己想法DIY一条流水灯,谁不喜欢呢? 所用单片机:STC15W204S (也可以用其他1T单片机,例如,S…...
React第二十八章(css modules)
css modules 什么是 css modules 因为 React 没有Vue的Scoped,但是React又是SPA(单页面应用),所以需要一种方式来解决css的样式冲突问题,也就是把每个组件的样式做成单独的作用域,实现样式隔离,而css modules就是一种…...
瑞芯微方案:RV1126定制开发板方案定制
产品简介 RV1126 核心板是常州海图电子科技有限公司推出的一款以瑞芯微 RV1126处理器为核心的通用产品,其丰富的设计资源、稳定的产品性能、强力的设计支持,为客户二次开发快速转化产品提供强有力的技术保障。RV1126 核心板集多种优势于一身,…...
吴恩达深度学习——有效运作神经网络
内容来自https://www.bilibili.com/video/BV1FT4y1E74V,仅为本人学习所用。 文章目录 训练集、验证集、测试集偏差、方差正则化正则化参数为什么正则化可以减少过拟合Dropout正则化Inverted Dropout其他的正则化方法数据增广Early stopping 归一化梯度消失与梯度爆…...
ollama改模型的存盘目录解决下载大模型报c:盘空间不足的问题
使用Ollama和Open WebUI快速玩转大模型:简单快捷的尝试各种llm大模型,比如DeepSeek r1,非常简单方便,参见:使用Ollama和Open WebUI快速玩转大模型:简单快捷的尝试各种llm大模型,比如DeepSeek r1…...
springboot集成钉钉,发送钉钉日报
目录 1.说明 2.示例 3.总结 1.说明 学习地图 - 钉钉开放平台 在钉钉开放文档中可以查看有关日志相关的api,主要用到以下几个api: ①获取模板详情 ②获取用户发送日志的概要信息 ③获取日志接收人员列表 ④创建日志 发送日志时需要根据模板规定日志…...
Day49:添加字典元素
在 Python 中,字典是一个可变的数据类型,这意味着你可以随时添加新的键值对。今天我们将学习如何向字典中添加元素。 1. 使用方括号 ([]) 添加新元素 最简单的方法是通过字典的键,使用方括号 [] 来添加新的键值对。如果该键已经存在&#x…...
Keepalived 安装
环境介绍 操作系统Kylin Linux Advanced Server V10 (Lance)Kylin Linux Advanced Server V10 (Lance)Kylin Linux Advanced Server V10 (Lance)内核版本Linux 4.19.90-52.22.v2207.ky10.aarch64Linux 4.19.90-52.22.v2207.ky10.aarch64Linux 4.19.90-52.22.v2207.ky10.aarch64…...
AI应用部署——streamlit
如何把项目部署到一个具有公网ip地址的服务器上,让他人看到? 可以利用 streamlit 的社区云免费部署 1、生成requirements.txt文件 终端输入pip freeze > requirements.txt即可 requirements.txt里既包括自己安装过的库,也包括这些库的…...