算法背景

        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;则返回空字符串。 示例…