A*(Astar)算法詳解與應用

算法背景

? ? ? ??A*(A-Star)算法是一種在圖形平面上,有多個節點的路徑中,求出最低通過成本的算法。其歷史可以追溯到早期的圖搜索算法,如Dijkstra算法和貪心最佳優先搜索(Greedy Best-First Search)。是人工智能、游戲開發、機器人路徑規劃等領域中最經典、最有效的尋路算法之一。

算法解釋

????????A*算法的核心思想是結合了Dijkstra算法和Best-First-Search(BFS)算法的特點。他給每個格子賦予了一個權重 f(n) = g(n) + h(n) ,通過權重來決定遍歷的順序。

詳細解釋

? ? ? ? 首先得理解廣度優先搜索(BFS),在方格中,BFS通過遍歷起始格子上下左右的四個格子,將其壓入一個隊列,通過循環 出隊-搜索-入隊 來尋找最短路。

? ? ? ? 而A*算法則是在BFS的基礎上使用了貪心策略,給每個格子賦予一個權重f(n),其中f(n) =?g(n) + h(n) 。

? ? ? ? -? g(n)為起點到當前點的最短距離

? ? ? ? -? h(n)為當前點到終點的估計距離

????????A*算法則使用了優先隊列,通過尋找路徑代價最小的節點來尋找目標點,它保證了如果h(n)的估計值是準確的,那么找到的路徑也是最短的。同時,A*算法比BFS減少了遍歷點的數量,加快了路線尋找的速度。

? ? ? ? 【很好理解,當你要從一個地方到另一個地方,并且你已經知道終點的方位,及時沒有導航,你很自然會優先朝著終點方向前進,即使和道路方向并不相同】

? ? ? ? 下圖是每個格子的 g(n) 也就是當前距離起點的步數,這個很好理解。

? ? ? ? 下圖是每個格子的 h(n) ,也就是每個格子距離終點的估計距離,下圖使用了曼哈頓距離

? ? ? ? 你會發現,在上圖沒有障礙的時候,每個格子的權重是一樣的,無法體現路線的優化。如果使用歐幾里得距離,就能體現出權重的作用,如下圖:

距離的估計

????????有很多計算方式,比如:

1.曼哈頓距離

  • 定義:只能沿著網格的橫縱方向移動,不能斜向移動。? ? ? ? ? ? ? ? ??

?D_{Manhattan}=\left | x_{1}-x_{2} \right |+\left | y_{1}-y_{2} \right |

  • 適用場景:城市街區網格、只能走直線(上下左右)的環境,比如A*算法中常用的啟發函數

  • 特點:簡單、計算快,但可能高估實際距離。

2.歐幾里得距離:

  • 定義:兩點之間的“直線距離”,也就是我們日常生活中說的“最短距離”。

D_{Euclidean}=\sqrt{(x_{1}-x_{2})^{2}+(y_{1}-y_{2})^{2}}

  • 適用場景:連續空間、物理世界中的距離計算,比如機器人導航、圖像識別。

  • 特點:真實距離,但計算稍慢,且可能包含浮點運算。

3. 切比雪夫距離:

  • 定義:允許八方向移動(上下左右+對角線),取各坐標差值的最大值。

D_{Chebyshev}=max(\left | x_{1}-x_{2} \right |,\left | y_{1}-y_{2} \right |)

  • 適用場景:可以斜向移動的游戲地圖(如象棋中的國王移動),或某些特殊路徑規劃。

  • 特點:比曼哈頓更靈活,但可能低估實際步數。

?4. 八方向距離:

  • 定義:允許八方向移動,但斜向移動的代價更高(比如√2倍),更接近真實情況。

D_{Octile}= max(\left | x_{1}-x_{2} \right |,\left | y_{1}-y_{2} \right |)\times\sqrt{2}+min(\left | x_{1}-x_{2} \right |,\left | y_{1}-y_{2} \right |)\times(1-\sqrt{2})

? ? ? ? 近似寫法:

?D_{Octile}\approx max(\left | dx \right |,\left | dy \right |)\times\sqrt{2}+(\left | max(\left | dx \right |,\left | dy \right |)-min(\left | dx \right |,\left | dy \right |) \right |)?

  • 適用場景:允許斜向移動但代價更高的地圖,比如某些策略游戲或真實地形路徑規劃。

  • 特點:比切比雪夫更精確,適合八方向移動的A*啟發函數。

最短路證明

? ? ? ? 所以,A*算法得到的路徑一定是最短路嗎?答案是否定的。

? ? ? ? 我們已知BFS得到的路徑一定是最短路,但是BFS的時間效率過低,我們添加了貪心的策略,權衡速度與最優性。

? ? ? ? 但是,如果我們處理好啟發式函數的大小,可以保證A*路徑的最優性,即啟發項小于等于最優的那條路徑

????????h(n)\leq ^{*}h(n)

? ? ? ? 在之前的方格示例中,A*得到的一定是最優路徑,因為每個點的估計價值是通過當前點與終點的相對位置計算得出的,其有絕對性,即不同點的估計價值在同個距離算法下是絕對的。

? ? ? ? 但是在實際應用中,直接使用相對坐標來計算估計價值是不合理的,比如:

? ? ? ? 其中藍色的為河流,我們預估通過河流所需時間需要10個單位,假設通過一個格子所需時間1個單位。

? ? ? ? 從起點開始,到河流的權重f(n)為16,而走上面路線權重為14,則走上面路線到達終點。但是事實上河流上有橋,可以直接通過,而河流路徑明顯比上面更短,喪失了最優性

? ? ? ? 發現當h<=4的時候,其最短路徑都是正確的,這就是啟發項大小要小于等于最優路徑。

? ? ? ? 在實際情況中,要合理設置初始權值,才能避免某些不必要的麻煩。

? ? ? ? 當然,比如你想要某個敵人不能走某條路,直接將初始權值設為無限大,就可以實現該功能。

算法示例

? ? ? ? 寫了一個html文件,可以直觀展現A*算法的過程與結果,可以復制到本地嘗試一下:

? 代碼:

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>A* 算法可視化演示(優化版)</title><style>body {font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;background-color: #f5f5f5;margin: 0;padding: 20px;color: #333;display: flex;flex-direction: column;align-items: center;}h1 {color: #2c3e50;margin-bottom: 10px;}.description {max-width: 700px;text-align: center;margin-bottom: 20px;color: #555;}.controls {margin-bottom: 20px;display: flex;gap: 10px;flex-wrap: wrap;justify-content: center;align-items: center;}button {padding: 10px 15px;border: none;border-radius: 5px;background-color: #3498db;color: white;cursor: pointer;font-size: 16px;transition: background-color 0.3s;}button:hover {background-color: #2980b9;}button:disabled {background-color: #bdc3c7;cursor: not-allowed;}.grid-container {display: flex;flex-direction: column;align-items: center;margin-bottom: 20px;}.grid {display: grid;grid-template-columns: repeat(20, 25px);grid-template-rows: repeat(20, 25px);gap: 1px;border: 1px solid #ccc;background-color: #ddd;position: relative;}.cell {width: 25px;height: 25px;background-color: #fff;display: flex;align-items: center;justify-content: center;cursor: pointer;font-size: 10px;transition: background-color 0.2s;position: relative;}.cell:hover {opacity: 0.8;}.wall { background-color: #333; }.start { background-color: #2ecc71; }.end { background-color: #e74c3c; }.visited { background-color: #3498db; }.path { background-color: #f1c40f; }.current { background-color: #9b59b6; }.frontier { background-color: #95a5a6; opacity: 0.6; }.legend {display: flex;gap: 15px;margin-top: 10px;font-size: 14px;flex-wrap: wrap;}.legend-item {display: flex;align-items: center;gap: 5px;}.legend-color {width: 15px;height: 15px;border: 1px solid #ccc;}.info {margin-top: 20px;max-width: 600px;background-color: white;padding: 15px;border-radius: 5px;box-shadow: 0 2px 5px rgba(0,0,0,0.1);}.info h3 {margin-top: 0;color: #2c3e50;}.info p {margin: 5px 0;font-size: 14px;}.mode-selector {margin-bottom: 10px;display: flex;gap: 10px;align-items: center;}select, input {padding: 5px;font-size: 16px;}.speed-control {display: flex;align-items: center;gap: 10px;}.stats {display: flex;gap: 20px;flex-wrap: wrap;}.stat-item {font-size: 14px;}.stat-value {font-weight: bold;color: #3498db;}.heuristic-selector {display: flex;align-items: center;gap: 10px;}</style>
</head>
<body><h1>A* 尋路算法可視化(優化版)</h1><div class="description">點擊網格設置起點(綠色)、終點(紅色)和障礙物(黑色)。支持拖拽繪制障礙物,可切換不同啟發式函數。</div><div class="mode-selector"><label for="mode">編輯模式: </label><select id="mode"><option value="start">設置起點</option><option value="end">設置終點</option><option value="wall">繪制障礙墻</option><option value="erase">擦除障礙</option></select><div class="heuristic-selector"><label for="heuristic">啟發式函數: </label><select id="heuristic"><option value="manhattan">曼哈頓距離</option><option value="euclidean">歐幾里得距離</option><option value="chebyshev">切比雪夫距離</option><option value="octile">八方向距離</option></select></div></div><div class="controls"><button id="startBtn">開始尋路</button><button id="stepBtn" disabled>單步執行</button><button id="autoBtn" disabled>自動播放</button><button id="resetBtn">重置網格</button><button id="clearPathBtn">清除路徑</button><button id="mazeBtn">生成迷宮</button><div class="speed-control"><label for="speed">速度: </label><input type="range" id="speed" min="10" max="500" value="100" step="10"><span id="speedValue">100ms</span></div></div><div class="grid-container"><div class="grid" id="grid"></div><div class="legend"><div class="legend-item"><div class="legend-color start"></div><span>起點</span></div><div class="legend-item"><div class="legend-color end"></div><span>終點</span></div><div class="legend-item"><div class="legend-color wall"></div><span>障礙</span></div><div class="legend-item"><div class="legend-color frontier"></div><span>待探索</span></div><div class="legend-item"><div class="legend-color visited"></div><span>已訪問</span></div><div class="legend-item"><div class="legend-color current"></div><span>當前節點</span></div><div class="legend-item"><div class="legend-color path"></div><span>最終路徑</span></div></div></div><div class="info"><h3>算法信息</h3><p id="currentInfo">設置起點和終點,然后點擊"開始尋路"。</p><div class="stats"><div class="stat-item">路徑長度: <span class="stat-value" id="pathLength">-</span></div><div class="stat-item">訪問節點數: <span class="stat-value" id="visitedCount">-</span></div><div class="stat-item">開放列表大小: <span class="stat-value" id="openListSize">-</span></div><div class="stat-item">執行時間: <span class="stat-value" id="executionTime">-</span></div></div></div><script>// 網格配置const ROWS = 20;const COLS = 20;let grid = [];let startCell = null;let endCell = null;let isRunning = false;let isAutoRunning = false;let openList = [];let closedSet = new Set(); // 使用Set提高查找效率let path = [];let currentAlgorithmState = null;let autoInterval = null;let isMouseDown = false;let startTime = 0;let animationSpeed = 100;// 優先隊列實現(最小堆)class PriorityQueue {constructor() {this.heap = [];}push(element) {this.heap.push(element);this.bubbleUp(this.heap.length - 1);}pop() {if (this.heap.length === 0) return null;const min = this.heap[0];const end = this.heap.pop();if (this.heap.length > 0) {this.heap[0] = end;this.bubbleDown(0);}return min;}bubbleUp(index) {while (index > 0) {const parentIndex = Math.floor((index - 1) / 2);if (this.heap[index].f < this.heap[parentIndex].f) {[this.heap[index], this.heap[parentIndex]] = [this.heap[parentIndex], this.heap[index]];index = parentIndex;} else {break;}}}bubbleDown(index) {while (true) {let minIndex = index;const leftChild = 2 * index + 1;const rightChild = 2 * index + 2;if (leftChild < this.heap.length && this.heap[leftChild].f < this.heap[minIndex].f) {minIndex = leftChild;}if (rightChild < this.heap.length && this.heap[rightChild].f < this.heap[minIndex].f) {minIndex = rightChild;}if (minIndex !== index) {[this.heap[index], this.heap[minIndex]] = [this.heap[minIndex], this.heap[index]];index = minIndex;} else {break;}}}get length() {return this.heap.length;}contains(element) {return this.heap.includes(element);}update(element) {const index = this.heap.indexOf(element);if (index !== -1) {this.bubbleUp(index);this.bubbleDown(index);}}}// 初始化網格function initGrid() {const gridElement = document.getElementById('grid');gridElement.innerHTML = '';grid = [];for (let row = 0; row < ROWS; row++) {const gridRow = [];for (let col = 0; col < COLS; col++) {const cell = document.createElement('div');cell.className = 'cell';cell.dataset.row = row;cell.dataset.col = col;cell.addEventListener('mousedown', () => {isMouseDown = true;handleCellClick(row, col);});cell.addEventListener('mouseenter', () => {if (isMouseDown) {handleCellDrag(row, col);}});cell.addEventListener('mouseup', () => {isMouseDown = false;});gridElement.appendChild(cell);gridRow.push({element: cell,row,col,isWall: false,f: Infinity,g: Infinity,h: 0,parent: null,inOpenList: false});}grid.push(gridRow);}// 全局鼠標釋放事件document.addEventListener('mouseup', () => {isMouseDown = false;});resetAlgorithmState();}function resetAlgorithmState() {openList = new PriorityQueue();closedSet = new Set();path = [];isRunning = false;isAutoRunning = false;currentAlgorithmState = null;startTime = 0;document.getElementById('startBtn').disabled = false;document.getElementById('stepBtn').disabled = true;document.getElementById('autoBtn').disabled = true;clearInterval(autoInterval);updateInfoText('設置起點和終點,然后點擊"開始尋路"。');updateStats();// 清除之前的訪問和路徑標記,但保留墻、起點和終點for (let row = 0; row < ROWS; row++) {for (let col = 0; col < COLS; col++) {const cell = grid[row][col];cell.element.classList.remove('visited', 'path', 'current', 'frontier');cell.f = Infinity;cell.g = Infinity;cell.h = 0;cell.parent = null;cell.inOpenList = false;}}}function handleCellClick(row, col) {if (isRunning) return;const cell = grid[row][col];const mode = document.getElementById('mode').value;switch (mode) {case 'start':if (startCell) {startCell.element.classList.remove('start');}cell.element.classList.remove('wall', 'end');cell.element.classList.add('start');cell.isWall = false;startCell = cell;break;case 'end':if (endCell) {endCell.element.classList.remove('end');}cell.element.classList.remove('wall', 'start');cell.element.classList.add('end');cell.isWall = false;endCell = cell;break;case 'wall':if (cell !== startCell && cell !== endCell) {cell.isWall = true;cell.element.classList.add('wall');}break;case 'erase':if (cell !== startCell && cell !== endCell) {cell.isWall = false;cell.element.classList.remove('wall');}break;}}function handleCellDrag(row, col) {const cell = grid[row][col];const mode = document.getElementById('mode').value;if (mode === 'wall' && cell !== startCell && cell !== endCell) {cell.isWall = true;cell.element.classList.add('wall');} else if (mode === 'erase' && cell !== startCell && cell !== endCell) {cell.isWall = false;cell.element.classList.remove('wall');}}// 啟發式函數function heuristic(a, b) {const type = document.getElementById('heuristic').value;const dx = Math.abs(a.row - b.row);const dy = Math.abs(a.col - b.col);switch (type) {case 'manhattan':return dx + dy;case 'euclidean':return Math.sqrt(dx * dx + dy * dy);case 'chebyshev':return Math.max(dx, dy);case 'octile':return Math.max(dx, dy) + (Math.sqrt(2) - 1) * Math.min(dx, dy);default:return dx + dy;}}function getNeighbors(cell) {const neighbors = [];const { row, col } = cell;// 八個方向(包括對角線)const directions = [[-1, 0, 1], [0, 1, 1], [1, 0, 1], [0, -1, 1],  // 上右下左[-1, -1, 1.414], [-1, 1, 1.414], [1, 1, 1.414], [1, -1, 1.414]  // 對角線];const allowDiagonal = document.getElementById('heuristic').value !== 'manhattan';const limit = allowDiagonal ? 8 : 4;for (let i = 0; i < limit; i++) {const [dr, dc, cost] = directions[i];const newRow = row + dr;const newCol = col + dc;if (newRow >= 0 && newRow < ROWS && newCol >= 0 && newCol < COLS) {const neighbor = grid[newRow][newCol];if (!neighbor.isWall) {// 對角線移動時檢查兩邊是否有墻if (i >= 4) {const side1 = grid[row + directions[i-4][0]][col + directions[i-4][1]];const side2 = grid[row + directions[(i-3)%4][0]][col + directions[(i-3)%4][1]];if (side1.isWall || side2.isWall) continue;}neighbors.push({cell: neighbor, cost});}}}return neighbors;}function startAlgorithm() {if (!startCell || !endCell) {alert('請先設置起點和終點!');return;}resetAlgorithmState();isRunning = true;startTime = performance.now();// 初始化開放列表,加入起點openList = new PriorityQueue();startCell.g = 0;startCell.h = heuristic(startCell, endCell);startCell.f = startCell.h;startCell.inOpenList = true;openList.push(startCell);document.getElementById('startBtn').disabled = true;document.getElementById('stepBtn').disabled = false;document.getElementById('autoBtn').disabled = false;currentAlgorithmState = {found: false,noPath: false};updateStats();updateInfoText('A* 算法已啟動,準備開始搜索...');}function stepAlgorithm() {if (!isRunning || currentAlgorithmState.found || currentAlgorithmState.noPath) return;if (openList.length === 0) {currentAlgorithmState.noPath = true;updateInfoText('搜索完成:沒有找到路徑。');finishAlgorithm();return;}// 從優先隊列中取出f值最小的節點const current = openList.pop();current.inOpenList = false;// 標記當前節點if (current !== startCell && current !== endCell) {current.element.classList.add('current');current.element.classList.remove('frontier');if (current.parent && current.parent !== startCell) {current.parent.element.classList.remove('current');}}// 如果找到終點if (current === endCell) {currentAlgorithmState.found = true;reconstructPath(current);const executionTime = (performance.now() - startTime).toFixed(2);updateInfoText(`搜索完成:已找到最短路徑!用時 ${executionTime}ms`);finishAlgorithm();return;}// 將當前節點移到關閉列表closedSet.add(current);if (current !== startCell && current !== endCell) {current.element.classList.remove('current');current.element.classList.add('visited');}updateInfoText(`正在檢查節點 (${current.row}, ${current.col}) - f=${current.f.toFixed(2)}`);// 檢查所有鄰居const neighbors = getNeighbors(current);for (const {cell: neighbor, cost} of neighbors) {if (closedSet.has(neighbor)) continue;const tentativeG = current.g + cost;if (tentativeG < neighbor.g) {neighbor.parent = current;neighbor.g = tentativeG;neighbor.h = heuristic(neighbor, endCell);neighbor.f = neighbor.g + neighbor.h;if (!neighbor.inOpenList) {openList.push(neighbor);neighbor.inOpenList = true;if (neighbor !== endCell) {neighbor.element.classList.add('frontier');}} else {openList.update(neighbor);}}}updateStats();}function reconstructPath(current) {path = [];let temp = current;while (temp) {path.push(temp);temp = temp.parent;}path.reverse();// 動畫顯示路徑path.forEach((cell, index) => {if (cell !== startCell && cell !== endCell) {setTimeout(() => {cell.element.classList.add('path');cell.element.classList.remove('visited');}, index * 20);}});}function finishAlgorithm() {isRunning = false;isAutoRunning = false;document.getElementById('stepBtn').disabled = true;document.getElementById('autoBtn').disabled = true;clearInterval(autoInterval);// 移除所有當前標記document.querySelectorAll('.current').forEach(el => el.classList.remove('current'));document.querySelectorAll('.frontier').forEach(el => el.classList.remove('frontier'));const executionTime = (performance.now() - startTime).toFixed(2);document.getElementById('executionTime').textContent = `${executionTime}ms`;document.getElementById('pathLength').textContent = path.length > 0 ? path.length - 1 : '-';}function startAutoRun() {if (isAutoRunning) {stopAutoRun();return;}isAutoRunning = true;document.getElementById('autoBtn').textContent = '停止自動播放';autoInterval = setInterval(() => {if (currentAlgorithmState.found || currentAlgorithmState.noPath) {stopAutoRun();return;}stepAlgorithm();}, animationSpeed);}function stopAutoRun() {isAutoRunning = false;clearInterval(autoInterval);document.getElementById('autoBtn').textContent = '自動播放';}function updateInfoText(text) {document.getElementById('currentInfo').textContent = text;}function updateStats() {document.getElementById('visitedCount').textContent = closedSet.size;document.getElementById('openListSize').textContent = openList.length;document.getElementById('pathLength').textContent = path.length > 0 ? path.length - 1 : '-';}// 生成隨機迷宮function generateMaze() {if (isRunning) return;// 清除所有墻for (let row = 0; row < ROWS; row++) {for (let col = 0; col < COLS; col++) {const cell = grid[row][col];if (cell !== startCell && cell !== endCell) {cell.isWall = false;cell.element.classList.remove('wall');}}}// 隨機生成障礙物(30%概率)for (let row = 0; row < ROWS; row++) {for (let col = 0; col < COLS; col++) {const cell = grid[row][col];if (cell !== startCell && cell !== endCell && Math.random() < 0.3) {cell.isWall = true;cell.element.classList.add('wall');}}}resetAlgorithmState();}// 速度控制document.getElementById('speed').addEventListener('input', (e) => {animationSpeed = parseInt(e.target.value);document.getElementById('speedValue').textContent = `${animationSpeed}ms`;if (isAutoRunning) {stopAutoRun();startAutoRun();}});// 事件監聽器document.getElementById('startBtn').addEventListener('click', startAlgorithm);document.getElementById('stepBtn').addEventListener('click', stepAlgorithm);document.getElementById('autoBtn').addEventListener('click', startAutoRun);document.getElementById('resetBtn').addEventListener('click', initGrid);document.getElementById('clearPathBtn').addEventListener('click', resetAlgorithmState);document.getElementById('mazeBtn').addEventListener('click', generateMaze);// 初始化initGrid();// 設置默認起點和終點handleCellClick(5, 2);document.getElementById('mode').value = 'end';handleCellClick(15, 17);document.getElementById('mode').value = 'wall';</script>
</body>
</html>

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

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

相關文章

word刪除指定頁面

常規程序因為wps的 .docx 文件是基于段落和節的結構&#xff0c;而不是“物理頁”&#xff0c;所以無法直接按“第幾頁”刪除在普通程序里面無法讀取到他的頁碼&#xff0c;但是在宏編程里面他能讀取到頁碼&#xff0c;我們就根據宏編程來這樣刪除。程序會自動打開選擇要刪除的…

RK3568平臺開發系列講解:瑞芯微平臺4G模塊篇移植

更多內容可以加入Linux系統知識庫套餐(教程+視頻+答疑) ??返回專欄總目錄 文章目錄 一、硬件圖片 二、功能宏 三、增加PID/VID 支持 3.1、usb_device_id 結構體 3.2、usb_device_id 的注冊 沉淀、分享、成長,讓自己和他人都能有所收獲!?? 一、硬件圖片 目標: 結果…

面試 (一)

目錄 1. HashMap是怎么解決沖突的 是用什么數據結構實現的 2. 為什么hashmap的初始長度為16 3. 多線程的核心參數 4. 多線程怎么工作的 5. CISCS是怎么實現的 6. JUC知識 7. C和java的區別 8. JVM底層編譯的東西 9. 公平鎖和非公平鎖 10. 有人惡意攻擊你的平臺每秒發送…

計算機畢設選題:基于Python+Django的健康飲食管理系統設計【源碼+文檔+調試】

精彩專欄推薦訂閱&#xff1a;在 下方專欄&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f496;&#x1f525;作者主頁&#xff1a;計算機畢設木哥&#x1f525; &#x1f496; 文章目錄 一、項目介紹二…

vscode terminal遠程連接linux服務器GUI圖形界面

看了很多教程&#xff0c;不清楚具體原理&#xff0c;但總之自己是摸索出來了&#xff1a; 1.下載vcxsrv&#xff0c;最后雙擊exe程序啟動&#xff1a; 每一步的配置如下&#xff1a;2.修改配置 vscode中按下“ctrlshiftp”&#xff0c;然后輸入“Remote-SSH: Open SSH Configu…

文檔外發管理產品哪個品牌強?安全與效率雙優產品推薦

在企業間協作日益加深的今天&#xff0c;企業對文檔外發管理相關產品的安全性和效率要求越來越高。無論是日常業務協作&#xff0c;還是跨組織數據交換&#xff0c;如何確保文件在傳輸過程中不被泄露、篡改&#xff0c;同時又能高效流轉&#xff0c;成為企業IT管理的重要課題。…

【教程】2025 IDEA 快速創建springboot(maven)項目

第一步&#xff1a;【新建】-【module】&#xff0c;左邊選擇springboot&#xff0c;右邊填寫相關信息。第二步&#xff1a;選擇相關依賴。第三步&#xff1a;刪掉一些無關的文件&#xff0c;保持項目簡潔創建springboot項目已經結束了&#xff0c;下面是構建項目的架構以及環境…

【小白筆記】移動硬盤為什么總比電腦更容易滿?

我明明只復制了10個G的文件&#xff0c;為什么我的移動硬盤就滿了&#xff1f; 大家好&#xff0c;我是個剛入門的小白&#xff0c;最近遇到了一個讓我百思不得其解的問題。我把電腦里的一些文件&#xff0c;總共加起來也就10個G左右&#xff0c;心想移動硬盤還有幾十個G的空位…

單獨一篇云原生介紹

云原生&#xff08;Cloud Native&#xff09;?不是單一技術&#xff0c;而是一套構建和運行應用程序的完整方法論?&#xff0c;旨在充分利用云計算的優勢&#xff08;彈性、按需資源、分布式環境&#xff09;來構建?高韌性、可擴展、易于管理的應用?。它的核心思想是讓應用…

Git如何查看提交行數與刪除行數:統計代碼貢獻量的完整指南

Git如何查看提交行數與刪除行數&#xff1a;統計代碼貢獻量的完整指南 在軟件開發中&#xff0c;代碼行數統計是衡量團隊協作效率和項目進度的重要指標。通過Git的命令行工具&#xff0c;開發者可以輕松查看提交的代碼行數、刪除的代碼行數以及凈增行數。本文將詳細介紹多種方…

DVWA靶場通關筆記-SQL注入(SQL Injection Impossible級別)

目錄 一、源碼分析 1、index.php 2、impossible.php 二、SQL注入防范分析 1、Anti-CSRF 令牌 2、參數化查詢 3、輸入驗證 本系列為通過《DVWA靶場通關筆記》的SQL Injection 關卡(low&#xff0c;medium&#xff0c;high&#xff0c;impossible共4關&#xff09;滲透集合…

MySQL中事務的作用和定義

在 MySQL 中&#xff0c;??事務&#xff08;Transaction&#xff09;?? 是一組數據庫操作的邏輯單元&#xff0c;這些操作要么??全部成功執行??&#xff0c;要么??全部失敗回滾??&#xff0c;以確保數據庫從一個一致狀態轉換到另一個一致狀態。事務是關系型數據庫&…

華為Fit4:腕間助手,守護你的健康,帶你開啟智慧生活

清晨被鬧鐘吵醒&#xff0c;想要關掉卻怎么也找不到手機&#xff1f;經常睡很久卻還是很困&#xff1f;重要的事情常常因為沒有被記錄和規劃清楚最終變得手忙腳亂&#xff1f;人流擁擠的地鐵上半天找不到刷碼信息&#xff1f;這些問題是不是生活中常常遇到&#xff0c;別擔心&a…

【交易系統系列36】揭秘幣安(Binance)技術心臟:從公開信息拼湊“MatchBox”撮合引擎架構

一、 核心概覽:從白皮書的豪言到監管文件的“實錘” 要理解幣安的架構,我們首先要抓住幾個關鍵的、有據可查的事實: 性能目標:在其早期白皮書中,幣安宣稱其撮合引擎能夠達到每秒140萬筆訂單的處理能力。這串數字,無論是在當時還是現在,都代表著對系統吞吐量和低延遲的極…

SpringBoot08-Spring Boot 多環境配置

方式一&#xff1a;純 Spring Profile&#xff08;最常用&#xff0c;官方推薦&#xff09;思路&#xff1a;用 application-{profile}.yml 切分配置&#xff0c;運行時指定用哪個 profile。 如何做&#xff1a;src/main/resources/application.ymlapplication-dev.ymlapplicat…

蒼穹外賣項目筆記day04--Redis入門

Redis 概念 Redis 是一個開源的、高性能的鍵值存儲系統&#xff0c;常被歸類為NoSQL數據庫。它支持多種數據結構&#xff0c;如字符串、列表、哈希、集合和有序集合&#xff0c;適用于緩存、消息隊列、實時分析等多種場景。Redis是一個基于內存的key-value結構數據庫。Redis …

OpenCV 圖像處理實戰與命令行參數配置:從輪廓檢測到模板匹配

在計算機視覺領域&#xff0c;OpenCV 是一款功能強大且應用廣泛的開源庫&#xff0c;它提供了豐富的 API&#xff0c;支持圖像讀取、預處理、特征檢測等多種操作。本文將結合實際代碼案例&#xff0c;詳細講解如何使用 OpenCV 實現輪廓檢測、輪廓近似、模板匹配等常用功能&…

深度學習---pytorch卷積神經網絡保存和使用最優模型

在深度學習模型訓練過程中&#xff0c;如何提升模型性能、精準保存最優模型并實現高效推理&#xff0c;是每個開發者必須攻克的關鍵環節。本文結合實際項目經驗與完整代碼示例&#xff0c;詳細拆解模型訓練優化、最優模型保存與加載、圖像預測全流程&#xff0c;幫助大家避開常…

FPGA實現Aurora 64B66B圖像視頻點對點傳輸,基于GTY高速收發器,提供2套工程源碼和技術支持

目錄 1、前言Aurora 64B66B是啥&#xff1f;官方有Example&#xff0c;為何要用你這個&#xff1f;工程概述免責聲明 2、相關方案推薦我已有的所有工程源碼總目錄----方便你快速找到自己喜歡的項目我這里已有的 GT 高速接口解決方案本方案在Aurora 8B10B上的應用 3、工程詳細設…

LeetCode 524.通過刪除字母匹配到字典里最長單詞

給你一個字符串 s 和一個字符串數組 dictionary &#xff0c;找出并返回 dictionary 中最長的字符串&#xff0c;該字符串可以通過刪除 s 中的某些字符得到。 如果答案不止一個&#xff0c;返回長度最長且字母序最小的字符串。如果答案不存在&#xff0c;則返回空字符串。 示例…