【編譯原理實驗二】——自動機實驗: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;
}

實驗報告

接下來是實驗報告的內容,希望能幫助讀者理解詞法分析程序的設計思路,以及完成實驗報告的撰寫


一.實驗目的

  1. 理解和掌握把問題中的實體轉換成抽象模型中數據結構的能力,設計確定有窮自動機DFA和非確定有窮自動機NFA描述的對象模型或數據結構,實現DFA和NFA的基本操作(輸入和輸出);
  2. 掌握將多個NFA合并的方法;
  3. 掌握將NFA確定化成DFA的方法;
  4. 掌握將DFA最小化的方法。
    加深對自動機的理解。

二.問題描述

  1. 需要實現的功能
    (1)設計一個函數(方法),實現把兩個NFA的合并;
    (2)設計一個函數(方法),實現把NFA確定化成一個DFA;
    (3)設計一個函數(方法),實現把DFA最小化;
    (4)輸入多個NFA:NFA描述存儲在文本文件中,文件名作為命令行參數輸入;
    (5)輸出合并、最小化以后的DFA到標準輸出設備。

  2. 實現原理
    2.1 NFA合并
    (1)創建新的開始狀態
    (2)通過ε-轉換連接到原NFA的開始狀態
    (3)合并狀態集、字母表、轉移函數和接受狀態
    2.2 NFA確定化
    (1)使用子集構造法
    (2)計算ε-閉包
    (3)構造新的狀態轉移函數
    2.3 DFA最小化
    (1)基于等價類的劃分算法
    (2)初始劃分為接受狀態和非接受狀態
    (3)迭代細化狀態劃分直至穩定

三.軟件設計方法的選擇

  1. 設計方法
    采用結構化設計方法,主要是考慮到了:
    (1)問題本身具有清晰的數據流向和處理流程
    (2)功能模塊劃分明確
    (3)算法實現較為直觀

  2. 各階段創建的模型
    2.1 分析階段:
    (1)系統流程圖
    (2)數據流圖
    (3)數據字典
    2.2 設計階段:
    (4)模塊結構圖
    (5)數據結構設計
    (6)算法流程圖

  3. 開發環境
    編程語言: C++11
    編譯器: g++
    開發工具: CLion
    依賴庫: STL標準模板庫

四.分析模型

  1. 系統流程圖
    描述:
    開始:實驗的起始點,系統準備開始執行。
    讀取NFA1與讀取NFA2:從輸入文件中讀取兩個非確定有窮自動機(NFA)的描述信息。
    合并NFA:將兩個NFA進行合并,創建一個新的NFA,合并后的NFA需要合并狀態集、字母表、轉移函數等。
    轉換為DFA:通過子集構造法將NFA轉換為確定性有限自動機(DFA)。
    最小化DFA:對轉換后的DFA進行最小化,減少狀態數并簡化自動機結構。
    輸出結果:輸出最小化后的DFA描述,顯示其狀態集、轉移函數等信息。
    結束:實驗完成。
圖2.1 系統流程圖

2.數據流圖
在這里插入圖片描述

圖2.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的接受狀態集合可能不同。

五.設計模型

  1. 模塊結構圖
    在這里插入圖片描述
圖2.3 模塊結構圖

描述:
主程序:程序的核心模塊,負責協調其他模塊的工作。
文件讀取模塊:負責從文件中讀取NFA的描述,并將其轉化為NFA結構。
readNFAFromFile:實現從文件中讀取NFA信息,并構造NFA數據結構。
NFA處理模塊:負責處理NFA的操作。
mergeNFAs:合并兩個NFA,生成一個新的NFA。
getEpsilonClosure:計算NFA狀態的ε-閉包。
DFA處理模塊:負責將NFA轉換為DFA,并對DFA進行最小化。
convertNFAtoDFA:將NFA轉換為DFA,使用子集構造法。
minimizeDFA:對DFA進行最小化,減少冗余狀態。
輸出模塊:負責輸出最小化后的DFA描述。
printDFA:輸出DFA的狀態集、接受狀態、轉移函數等信息。

  1. 主要數據結構
// 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. 主要函數接口
    (1)readNFAFromFile:從文件中讀取NFA描述,并返回構建好的NFA結構。
    (2)getEpsilonClosure:計算給定狀態集的ε-閉包,返回一個新的狀態集。
    (3)mergeNFAs:合并兩個NFA,返回合并后的NFA。
    (4)convertNFAtoDFA:將給定的NFA轉換為DFA。
    (5)minimizeDFA:對給定的DFA進行最小化處理,返回最小化后的DFA。
    (6)printDFA:輸出最小化后的DFA的狀態集、轉移函數、接受狀態等。

六.主要算法描述

  1. NFA合并算法
    NFA算法描述:
    尋找最大狀態編號:在合并NFA時,首先需要確定新的NFA中的狀態編號,應避免與原有NFA中的狀態編號沖突。
    創建新起始狀態:為合并后的NFA創建一個新的起始狀態。
    合并狀態集、字母表、轉移函數、接受狀態:將兩個NFA的狀態、字母表、轉移函數和接受狀態集合并成一個新的NFA。
    添加ε轉移:通過添加適當的ε轉移連接原NFA的起始狀態。
圖2.4 NFA算法流程圖
  1. NFA轉化DFA算法
    NFA轉化DFA算法描述:
    計算初始狀態ε-閉包:使用ε-閉包計算NFA初始狀態的所有可達狀態。
    初始化DFA:創建一個新的DFA,并將NFA的ε-閉包作為DFA的初始狀態。
    判斷未處理狀態隊列空:DFA構造過程中需要遍歷所有狀態,直到沒有狀態可處理為止。
    處理所有輸入符號:對于當前狀態,遍歷所有輸入符號,計算對應的下一狀態。
    更新DFA轉移函數:將狀態和符號的轉移結果添加到DFA的轉移函數中。
圖2.5 NFA轉化DFA算法流程圖
  1. DFA最小化算法
    DFA最小化算法描述:
    初始劃分:最初將狀態劃分為接受狀態和非接受狀態。
    需要繼續劃分:檢查當前劃分是否需要進一步細化。若有相同的轉移模式的狀態,則需要進一步劃分。
    對每個劃分進行細化:將狀態按照它們的轉移特征進行細化。
    更新劃分映射:更新狀態劃分的映射關系,確保最小化后的狀態集滿足等價類劃分的要求。
圖2.6 DFA最小化算法流程圖 ## 七.測試數據與測試效果
  1. 測試數據格式
    輸入多個NFA:NFA描述存儲在文本文件中,文件名作為命令行參數輸入

  2. 測試數據與測試效果
    (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.7 測試效果1

(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

測試效果:

在這里插入圖片描述

圖2.8 測試效果2

根據實驗效果,成功輸出合并、最小化以后的DFA到標準輸出設備。

八.實驗總結

  1. 遇到的問題及解決方法
    (1)ε-閉包計算問題:
    問題:初始實現時未考慮遞歸計算ε-閉包
    解決:使用隊列實現廣度優先搜索,確保完整計算閉包
    (2)DFA最小化過程中的等價類劃分:
    問題:劃分過程中狀態映射更新不及時
    解決:每次劃分后立即更新狀態映射關系
    (3)內存管理問題:
    問題:大規模NFA轉換時內存占用過大
    解決:使用STL容器自動管理內存,避免手動內存管理

  2. 收獲與體會
    深入理解了自動機理論的實際應用,掌握了復雜算法的工程實現方法,提高了數據結構和算法設計能力,學會了使用STL容器進行高效的數據處理。


本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/67471.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/67471.shtml
英文地址,請注明出處:http://en.pswp.cn/web/67471.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【redis進階】分布式鎖

目錄 一、什么是分布式鎖 二、分布式鎖的基礎實現 三、引入過期時間 四、引入校驗 id 五、引入lua 六、引入 watch dog (看門狗) 七、引入 Redlock 算法 八、其他功能 redis學習&#x1f973; 一、什么是分布式鎖 在一個分布式的系統中&#xff0c;也會涉及到多個節點訪問同一…

wordpress每隔24小時 隨機推薦一個指定分類下的置頂內容。

在WordPress中實現每隔24小時隨機推薦一個指定分類下的置頂內容&#xff0c;可以通過以下步驟實現&#xff1a; 1. 創建自定義函數 在主題的functions.php文件中添加以下代碼&#xff0c;用于創建一個定時任務&#xff0c;每隔24小時隨機選擇一個置頂文章并存儲到選項中&…

Blazor-@bind

數據綁定 帶有 value屬性的標記都可以使用bind 綁定&#xff0c;<div>、<span>等非輸入標記&#xff0c;無法使用bind 指令的&#xff0c;默認綁定了 onchange 事件&#xff0c;onchange 事件是指在輸入框中輸入內容之后&#xff0c;當失去焦點時執行。 page &qu…

RK3568 opencv播放視頻

文章目錄 一、opencv相關視頻播放類1. cv::VideoCapture 類主要構造方法&#xff1a;主要方法&#xff1a; 2. 視頻播放基本流程代碼示例&#xff1a; 3. 獲取和設置視頻屬性4. 結合 FFmpeg 使用5. OpenCV 視頻播放的局限性6. 結合 Qt 實現更高級的視頻播放總結 二、QT中的代碼…

pytorch邏輯回歸實現垃圾郵件檢測

完整代碼&#xff1a; import torch import torch.nn as nn import torch.optim as optim from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score import numpy as…

【 CVE-2025-21298】 通過ghidriff查看完整補丁差異

ole32_dec24.dll-ole32.dll 差異 目錄 視覺圖表差異元數據 Ghidra 差異引擎 命令行二進制元數據差異程序選項

洛谷P3383 【模板】線性篩素數

題目鏈接&#xff1a;P3383 【模板】線性篩素數 - 洛谷 | 計算機科學教育新生態 題目難度&#xff1a;普及一 題目分析&#xff1a;本題是模板題&#xff0c;用到了線性篩法&#xff0c;其中原理是保證范圍內的每個合數都被刪掉&#xff08;在 bool 數組里面標記為非素數…

STM32標準庫移植RT-Thread nano

STM32標準庫移植RT-Thread Nano 嗶哩嗶哩教程鏈接&#xff1a;STM32F1標準庫移植RT_Thread Nano 移植前的準備 stm32標準庫的裸機代碼&#xff08;最好帶有點燈和串口&#xff09;RT-Thread Nano Pack自己的開發板 移植前的說明 本人是在讀學生&#xff0c;正在學習階段&a…

JVM--類加載器

概念 類加載器&#xff1a;只參與加載過程中的字節碼獲取并加載到內存中的部分&#xff1b;java虛擬機提供給應用程序去實現獲取類和接口字節碼數據的一種技術&#xff0c;也就是說java虛擬機是允許程序員寫代碼去獲取字節碼信息 類加載是加載的第一步&#xff0c;主要有以下三…

ECMAScript 6語法

1.ES6簡介 ECMAScript 6&#xff08;簡稱ES6&#xff09;是于2015年6月正式發布的JavaScript語言的標準&#xff0c;正式名為ECMAScript 2015&#xff08;ES2015&#xff09;。它的目標是使得JavaScript語言可以用來編寫復雜的大型應用程序&#xff0c;成為企業級開發語言 。 …

聯想Y7000+RTX4060+i7+Ubuntu22.04運行DeepSeek開源多模態大模型Janus-Pro-1B+本地部署

直接上手搓了&#xff1a; conda create -n myenv python3.10 -ygit clone https://github.com/deepseek-ai/Janus.gitcd Januspip install -e .pip install webencodings beautifulsoup4 tinycss2pip install -e .[gradio]pip install pexpect>4.3python demo/app_januspr…

Tez 0.10.1安裝

個人博客地址&#xff1a;Tez 0.10.1安裝 | 一張假鈔的真實世界 具體安裝步驟參照官網安裝手冊即可。此處只對官網手冊進行補充。 從官網下載apache-tez-0.10.1-bin.tar.gz進行安裝未成功&#xff0c;出現下面的異常。最終按照官網源代碼編譯的方式安裝測試成功。 環境 Had…

FastAPI + GraphQL + SQLAlchemy 實現博客系統

本文將詳細介紹如何使用 FastAPI、GraphQL&#xff08;Strawberry&#xff09;和 SQLAlchemy 實現一個帶有認證功能的博客系統。 技術棧 FastAPI&#xff1a;高性能的 Python Web 框架Strawberry&#xff1a;Python GraphQL 庫SQLAlchemy&#xff1a;Python ORM 框架JWT&…

微服務入門(go)

微服務入門&#xff08;go&#xff09; 和單體服務對比&#xff1a;里面的服務僅僅用于某個特定的業務 一、領域驅動設計&#xff08;DDD&#xff09; 基本概念 領域和子域 領域&#xff1a;有范圍的界限&#xff08;邊界&#xff09; 子域&#xff1a;劃分的小范圍 核心域…

深入解析 Linux 內核內存管理核心:mm/memory.c

在 Linux 內核的眾多組件中,內存管理模塊是系統性能和穩定性的關鍵。mm/memory.c 文件作為內存管理的核心實現,承載著頁面故障處理、頁面表管理、內存區域映射與取消映射等重要功能。本文將深入探討 mm/memory.c 的設計思想、關鍵機制以及其在內核中的作用,幫助讀者更好地理…

安卓通過網絡獲取位置的方法

一 方法介紹 1. 基本權限設置 首先需要在 AndroidManifest.xml 中添加必要權限&#xff1a; xml <uses-permission android:name"android.permission.INTERNET" /> <uses-permission android:name"android.permission.ACCESS_NETWORK_STATE" /&g…

【B站保姆級視頻教程:Jetson配置YOLOv11環境(二)SSH連接的三種方式】

B站同步視頻教程&#xff1a;https://www.bilibili.com/video/BV1m5wUeyEQD/ 在Jetson設備上配置YOLOv11環境時&#xff0c;SSH連接是實現遠程高效開發與管理的關鍵一環。不同的網絡環境和硬件配置可能會影響SSH連接的方式&#xff0c;本文將結合相關視頻內容&#xff0c;詳細…

視頻拼接,拼接時長版本

目錄 視頻較長&#xff0c;分辨率較大&#xff0c;這個效果很好&#xff0c;不耗用內存 ffmpeg imageio&#xff0c;適合視頻較短 視頻較長&#xff0c;分辨率較大&#xff0c;這個效果很好&#xff0c;不耗用內存 ffmpeg import subprocess import glob import os from nats…

Vue.js 什么是 Composition API?

Vue.js 什么是 Composition API&#xff1f; 今天我們來聊聊 Vue 3 引入的一個重要特性&#xff1a;組合式 API&#xff08;Composition API&#xff09;。如果你曾在開發復雜的 Vue 組件時感到代碼難以維護&#xff0c;那么組合式 API 可能正是你需要的工具。 什么是組合式 …

Selenium配合Cookies實現網頁免登錄

文章目錄 前言1 方案一&#xff1a;使用Chrome用戶數據目錄2 方案二&#xff1a;手動獲取并保存Cookies&#xff0c;后續使用保存的Cookies3 注意事項 前言 在進行使用Selenium進行爬蟲、網頁自動化操作時&#xff0c;登錄往往是一個必須解決的問題&#xff0c;但是Selenium每次…