算法之回溯法
回溯法
- 回溯法
- 定义与概念
- 核心思想
- 回溯法的一般框架
- 伪代码表示
- C语言实现框架
- 回溯法的优化技巧
- 剪枝策略
- 实现剪枝的C语言示例
- 记忆化搜索
- 案例分析
- N皇后问题
- 子集和问题
- 全排列问题
- 寻路问题
- 回溯法的可视化理解
- 决策树
- 状态空间树
- 回溯过程
- 回溯法与其他算法的比较
- 回溯法与动态规划的区别
- 回溯法与贪心算法的区别
- 总结
- 应用场景总结
- 优化技巧总结
回溯法
定义与概念
回溯法是一种通过探索所有可能的候选解来找出所有解的算法。它采用试错的思想,尝试分步解决一个问题,在分步解决问题的过程中,当发现现有的分步答案不能得到有效的正确的解答时,它将取消上一步甚至是上几步的计算,再通过其它的可能的分步解答再次尝试寻找问题的答案。
回溯法通常用最简单的递归方法来实现,在反复重复上述的步骤后可能出现两种情况:
- 找到一个可能存在的正确答案
- 在尝试了所有可能的分步方法后宣告该问题无解
核心思想
典型的回溯算法通常包括以下步骤:
选择:在解空间中,进行一次选择,生成一个可能的解。
约束条件:检查当前的选择是否满足问题的限制条件。
判断:判断当前的选择是否是问题的解决方案。
回溯:如果当前选择不符合约束条件或者不是最终解,就撤销这次选择,回到之前的状态,并尝试其他的选择。
重复:重复上述步骤,直到找到问题的解决方案或者穷尽所有可能性。
典型的应用场景包括:
- 组合求和问题:寻找集合中符合特定条件的子集合或组合。
- 排列问题:如全排列、字符串排列等。
- 棋盘游戏:例如数独、八皇后等问题。
- 图搜索:在图中寻找路径、回路等问题。
回溯算法在解决组合优化问题时通常具有高效的灵活性,但随着问题规模的增加,其时间复杂度可能会指数级增长。因此,在实际应用中,通常会对算法进行优化,比如剪枝、启发式搜索等方法,以提高效率。
回溯法的一般框架
伪代码表示
回溯法的一般框架可以用以下伪代码表示:
void backtrack(Candidate* candidate) {// 检查是否找到解决方案if (find_solution(candidate)) {output_solution(candidate);return;}// 获取候选列表Candidate next_candidates[MAX_CANDIDATES];int candidate_count = 0;generate_candidates(candidate, next_candidates, &candidate_count);// 尝试每个候选解for (int i = 0; i < candidate_count; i++) {if (is_valid(&next_candidates[i])) {// 放置候选解place_candidate(candidate, &next_candidates[i]);// 递归搜索backtrack(candidate);// 移除候选解(回溯)remove_candidate(candidate, &next_candidates[i]);}}
}
其中:
find_solution()
:检查当前候选解是否是一个完整的解output_solution()
:输出找到的解决方案generate_candidates()
:生成当前可以选择的候选解列表is_valid()
:检查当前候选解是否满足约束条件place_candidate()
:将当前候选解放入解集合中remove_candidate()
:将当前候选解从解集合中移除(回溯)MAX_CANDIDATES
:候选解数组的最大容量Candidate
:表示候选解的数据结构
C语言实现框架
以下是回溯法的C语言通用框架实现:
#include <stdio.h>
#include <stdbool.h>// 问题的状态结构
typedef struct {// 问题特定的状态变量int n; // 问题规模int* solution; // 当前解int depth; // 当前搜索深度// 其他需要的状态变量
} State;// 初始化状态
void initState(State* state, int n) {state->n = n;state->depth = 0;state->solution = (int*)malloc(n * sizeof(int));// 初始化其他状态变量
}// 检查是否找到解
bool isSolution(State* state) {// 实现检查当前状态是否是一个完整的解的逻辑return state->depth == state->n; // 示例:当深度等于问题规模时找到解
}// 处理找到的解
void processSolution(State* state) {printf("找到一个解: ");for (int i = 0; i < state->n; i++) {printf("%d ", state->solution[i]);}printf("\n");
}// 生成候选
void generateCandidates(State* state, int candidates[], int* count) {// 实现生成候选的逻辑*count = 0;// 填充candidates数组并更新count
}// 检查候选是否有效
bool isValid(State* state, int candidate) {// 实现检查候选是否有效的逻辑return true; // 示例:所有候选都有效
}// 做出选择
void makeMove(State* state, int candidate) {// 实现做出选择的逻辑state->solution[state->depth] = candidate;state->depth++;
}// 撤销选择(回溯)
void unmakeMove(State* state) {// 实现撤销选择的逻辑state->depth--;
}// 回溯算法主体
void backtrack(State* state) {if (isSolution(state)) {processSolution(state);return;}int candidates[100]; // 假设最多100个候选int candidateCount;generateCandidates(state, candidates, &candidateCount);for (int i = 0; i < candidateCount; i++) {if (isValid(state, candidates[i])) {makeMove(state, candidates[i]);backtrack(state);unmakeMove(state);}}
}// 主函数
int main() {int n = 4; // 问题规模State state;initState(&state, n);backtrack(&state);free(state.solution);return 0;
}
回溯法的优化技巧
剪枝策略
剪枝是回溯法中最重要的优化技巧,它可以显著减少搜索空间,提高算法效率。常见的剪枝策略包括:
-
可行性剪枝:在搜索过程中,如果当前状态已经不可能产生有效解,则立即回溯。
-
最优性剪枝:在求解最优化问题时,如果当前状态的解不可能优于已知的最优解,则立即回溯。
-
对称性剪枝:利用问题的对称性,避免搜索等价的状态。
-
启发式剪枝:使用启发式函数估计当前状态的潜力,优先搜索更有希望的状态。
实现剪枝的C语言示例
以下是在子集和问题中实现剪枝的示例:
// 子集和问题的结构定义
typedef struct {int* set; // 原始集合int set_size; // 集合大小int target_sum; // 目标和int current_sum; // 当前和int* current; // 当前选择状态
} SubsetSum;// 打印子集
void printSubset(SubsetSum* problem) {printf("{ ");for (int i = 0; i < problem->set_size; i++) {if (problem->current[i]) {printf("%d ", problem->set[i]);}}printf("}\n");
}// 带剪枝的子集和问题回溯函数
void subsetSumBacktrackWithPruning(SubsetSum* problem, int index, int* solutions_count) {// 剪枝1:如果当前和已经等于目标和,直接输出解if (problem->current_sum == problem->target_sum) {(*solutions_count)++;printf("解决方案 %d: ", *solutions_count);printSubset(problem);return;}// 剪枝2:如果当前和已经超过目标和,直接回溯if (problem->current_sum > problem->target_sum) {return;}// 剪枝3:如果即使将剩余所有元素都选上也无法达到目标和,直接回溯int remaining_sum = 0;for (int i = index; i < problem->set_size; i++) {remaining_sum += problem->set[i];}if (problem->current_sum + remaining_sum < problem->target_sum) {return;}// 基本情况:已经处理完所有元素if (index == problem->set_size) {return;}// 选择当前元素problem->current[index] = 1;problem->current_sum += problem->set[index];subsetSumBacktrackWithPruning(problem, index + 1, solutions_count);// 回溯,不选当前元素problem->current_sum -= problem->set[index];problem->current[index] = 0;subsetSumBacktrackWithPruning(problem, index + 1, solutions_count);
}
记忆化搜索
记忆化搜索是一种结合了动态规划思想的回溯优化技术,它通过存储已经计算过的状态结果,避免重复计算。
// 记忆化搜索示例(斐波那契数列)
int memo[100] = {0}; // 记忆数组,初始化为0int fibonacci(int n) {// 基本情况if (n <= 1) return n;// 如果已经计算过,直接返回结果if (memo[n] != 0) return memo[n];// 计算结果并存储memo[n] = fibonacci(n-1) + fibonacci(n-2);return memo[n];
}
案例分析
N皇后问题
N皇后问题是一个经典的问题:在N×N格的棋盘上放置N个皇后,使得它们不能互相攻击。按照国际象棋的规则,皇后可以攻击同一行、同一列或同一斜线上的棋子。
以下是N皇后问题的C语言实现:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>#define N 8 // 棋盘大小和皇后数量// 打印棋盘
void printSolution(int board[N][N]) {for (int i = 0; i < N; i++) {for (int j = 0; j < N; j++) {printf("%c ", board[i][j] ? 'Q' : '.');}printf("\n");}printf("\n");
}// 检查在board[row][col]位置放置皇后是否安全
bool isSafe(int board[N][N], int row, int col) {int i, j;// 检查这一行的左侧for (i = 0; i < col; i++) {if (board[row][i]) {return false;}}// 检查左上对角线for (i = row, j = col; i >= 0 && j >= 0; i--, j--) {if (board[i][j]) {return false;}}// 检查左下对角线for (i = row, j = col; j >= 0 && i < N; i++, j--) {if (board[i][j]) {return false;}}return true;
}// 使用回溯法解决N皇后问题
bool solveNQUtil(int board[N][N], int col, int* solutionCount) {// 基本情况:如果所有皇后都被放置if (col >= N) {(*solutionCount)++;printf("解决方案 %d:\n", *solutionCount);printSolution(board);return true; // 找到一个解决方案}bool res = false;// 考虑这一列并尝试将皇后放在这一列的所有行中for (int i = 0; i < N; i++) {// 检查皇后是否可以放在board[i][col]if (isSafe(board, i, col)) {// 放置皇后在board[i][col]board[i][col] = 1;// 递归放置其余的皇后// 修改这里以找到所有解决方案,而不是只找到一个就返回solveNQUtil(board, col + 1, solutionCount);res = true; // 标记找到了至少一个解决方案// 回溯,移除皇后,继续尝试其他位置board[i][col] = 0; // 回溯}}// 如果皇后不能放在这一列的任何行,则返回falsereturn res;
}// 解决N皇后问题的包装函数
void solveNQ() {int board[N][N] = {0}; // 初始化棋盘int solutionCount = 0;if (!solveNQUtil(board, 0, &solutionCount)) {printf("没有解决方案\n");} else {printf("总共找到 %d 个解决方案\n", solutionCount);}
}int main() {solveNQ();return 0;
}
子集和问题
子集和问题是指:给定一个整数集合和一个目标和,找出集合中所有和为目标值的子集。
/*** 回溯法解决子集和问题* @param problem 子集和问题结构* @param index 当前处理的元素索引* @param solutions_count 找到的解决方案计数*/
void subsetSumBacktrack(SubsetSum* problem, int index, int* solutions_count) {// 基本情况:已经处理完所有元素if (index == problem->set_size) {// 检查是否找到一个解if (problem->current_sum == problem->target_sum) {(*solutions_count)++;printf("解决方案 %d: ", *solutions_count);printSubset(problem);}return;}// 不选当前元素problem->current[index] = 0;subsetSumBacktrack(problem, index + 1, solutions_count);// 选择当前元素(只有当不超过目标和时才选择)if (problem->current_sum + problem->set[index] <= problem->target_sum) {problem->current[index] = 1;problem->current_sum += problem->set[index];subsetSumBacktrack(problem, index + 1, solutions_count);// 回溯problem->current_sum -= problem->set[index];problem->current[index] = 0;}
}
全排列问题
全排列问题是指:给定一个不含重复数字的序列,返回其所有可能的全排列。
// 全排列问题的结构定义
typedef struct {int* nums; // 原始数字序列int size; // 序列大小int* result; // 当前排列结果bool* used; // 标记数字是否已使用int depth; // 当前深度
} Permutation;// 打印排列
void printPermutation(Permutation* problem) {printf("{ ");for (int i = 0; i < problem->size; i++) {printf("%d ", problem->result[i]);}printf("}\n");
}/*** 回溯法解决全排列问题* @param problem 全排列问题结构* @param solutions_count 找到的解决方案计数*/
void permutationBacktrack(Permutation* problem, int* solutions_count) {// 基本情况:已经生成完整的排列if (problem->depth == problem->size) {(*solutions_count)++;printf("排列 %d: ", *solutions_count);printPermutation(problem);return;}// 尝试在当前位置放置每个未使用的数字for (int i = 0; i < problem->size; i++) {// 如果数字未被使用if (!problem->used[i]) {// 选择当前数字problem->result[problem->depth] = problem->nums[i];problem->used[i] = true;problem->depth++;// 递归生成下一个位置的数字permutationBacktrack(problem, solutions_count);// 回溯problem->depth--;problem->used[i] = false;}}
}
寻路问题
寻路问题是指在一个迷宫中找出从起点到终点的路径。以下是一个简单的迷宫寻路问题的C语言实现:
#include <stdio.h>
#include <stdbool.h>#define N 5 // 迷宫大小// 迷宫:0表示可以通过的路径,1表示墙
int maze[N][N] = {{0, 1, 0, 0, 0},{0, 1, 0, 1, 0},{0, 0, 0, 0, 0},{0, 1, 1, 1, 0},{0, 0, 0, 1, 0}
};// 解决方案:记录路径,1表示路径的一部分
int solution[N][N] = {0};// 检查(x,y)是否是迷宫中的有效位置
bool isValidPosition(int x, int y) {return (x >= 0 && x < N && y >= 0 && y < N && maze[x][y] == 0);
}// 使用回溯法解决迷宫问题
bool solveMazeUtil(int x, int y) {// 如果(x,y)是目标位置,返回trueif (x == N-1 && y == N-1) {solution[x][y] = 1;return true;}// 检查(x,y)是否是有效位置if (isValidPosition(x, y)) {// 标记(x,y)为路径的一部分solution[x][y] = 1;// 向右移动if (solveMazeUtil(x+1, y)) {return true;}// 向下移动if (solveMazeUtil(x, y+1)) {return true;}// 向左移动if (solveMazeUtil(x-1, y)) {return true;}// 向上移动if (solveMazeUtil(x, y-1)) {return true;}// 如果没有方向可以到达目标,回溯solution[x][y] = 0;return false;}return false;
}// 解决迷宫问题的包装函数
bool solveMaze() {if (!solveMazeUtil(0, 0)) {printf("没有解决方案\n");return false;}// 打印解决方案printf("解决方案:\n");for (int i = 0; i < N; i++) {for (int j = 0; j < N; j++) {printf("%d ", solution[i][j]);}printf("\n");}return true;
}int main() {solveMaze();return 0;
}
回溯法的可视化理解
回溯法本质上是一种深度优先搜索(DFS)的过程,通过可视化工具可以更直观地理解其工作原理。
决策树
回溯法可以通过决策树来可视化理解。每个节点代表一个状态,每条边代表一个选择。回溯法就是在这棵树上进行深度优先搜索,寻找满足条件的路径。
[Root]/ | \/ | \[A] [B] [C] <- 第一层选择/ \ / \ / \/ \ / \ / \[D] [E][F] [G][H] [I] <- 第二层选择
在这个决策树中:
- 从根节点开始,我们有三个可能的选择:A、B或C
- 选择A后,我们可以进一步选择D或E
- 选择B后,我们可以进一步选择F或G
- 选择C后,我们可以进一步选择H或I
回溯法会先尝试一条路径(如Root→A→D),如果发现这条路径不满足条件,就回溯到上一个节点(A),然后尝试另一条路径(Root→A→E),依此类推。
状态空间树
状态空间树是回溯法中另一种重要的可视化工具,它展示了问题的所有可能状态及其转换关系。
对于N皇后问题,状态空间树的每一层代表在棋盘的一列中放置皇后,每个节点的子节点代表在下一列的不同行中放置皇后的选择。
[空棋盘]/ | \/ | \[第1行] [第2行] [第3行] ... [第N行] <- 第1列的选择/ | \ / | \ / | \/ | \ / | \ / | \[第1行] [第2行] [第3行]... <- 第2列的选择(根据约束条件筛选)
在这个状态空间树中:
- 第一层表示在第1列的N个可能位置放置皇后
- 第二层表示在第2列的可能位置放置皇后,但这些位置必须满足不与第1列的皇后相互攻击
- 依此类推,每一层的选择都受到之前所有选择的约束
回溯过程
以3皇后问题为例,回溯过程可以表示为:
- 在第1列放置皇后(尝试第1行)
- 在第2列放置皇后(由于第1行已被攻击,尝试第2行)
- 在第3列放置皇后(由于第1行和第2行已被攻击,尝试第3行)
- 发现无法放置所有皇后,回溯到第2步
- 在第2列移除皇后,尝试第3行
- 在第3列放置皇后(由于第1行和第3行已被攻击,尝试第2行)
- 找到一个解决方案
这个过程可以用以下棋盘序列来可视化:
步骤1: 在第1列第1行放置皇后
Q . .
. . .
. . .步骤2: 在第2列第2行放置皇后
Q . .
. Q .
. . .步骤3: 尝试在第3列放置皇后,但没有有效位置
(回溯到步骤2)步骤4: 移除第2列的皇后
Q . .
. . .
. . .步骤5: 在第2列第3行放置皇后
Q . .
. . .
. Q .步骤6: 在第3列第2行放置皇后
Q . .
. . Q
. Q .找到解决方案!
通过这种可视化方式,我们可以清晰地看到回溯法如何系统地探索解空间,并在遇到死胡同时如何回溯并尝试其他路径。
回溯法与其他算法的比较
算法 | 特点 | 适用场景 | 典型问题 | 时间复杂度 | 空间复杂度 |
---|---|---|---|---|---|
回溯法 | 尝试所有可能的解,遇到不满足条件的解则回溯 | 需要找到所有可能的解 | 八皇后问题、数独、全排列 | 指数级 O(b^d) | O(d) |
贪心算法 | 每一步选择当前最优解 | 问题具有贪心选择性质 | 最小生成树、哈夫曼编码 | 多项式级 | O(n) |
动态规划 | 将问题分解为子问题,存储子问题的解 | 问题具有重叠子问题和最优子结构 | 背包问题、最长公共子序列 | 多项式级 | O(n^2) |
分治法 | 将问题分解为独立的子问题,合并子问题的解 | 问题可以分解为独立的子问题 | 归并排序、快速排序 | O(n log n) | O(log n) |
分支限界法 | 类似回溯但使用队列而非栈,可以找到最优解 | 求解最优化问题 | 旅行商问题、作业调度 | 指数级 | 指数级 |
回溯法与动态规划的区别
-
问题类型:
- 回溯法:适用于找出所有可能解或所有满足条件的解。
- 动态规划:适用于找出最优解。
-
重叠子问题:
- 回溯法:通常不处理重叠子问题,可能会重复计算。
- 动态规划:通过记忆化存储子问题的解,避免重复计算。
-
搜索方式:
- 回溯法:深度优先搜索。
- 动态规划:通常是自底向上或自顶向下的方式构建解。
回溯法与贪心算法的区别
-
决策方式:
- 回溯法:考虑所有可能的选择,并在需要时回溯。
- 贪心算法:每一步都选择当前看起来最好的选择,不会回溯。
-
最优性:
- 回溯法:可以找到全局最优解。
- 贪心算法:只能保证局部最优,不一定能找到全局最优解。
-
效率:
- 回溯法:时间复杂度通常较高,可能是指数级的。
- 贪心算法:时间复杂度通常较低,多为多项式级别。
总结
回溯法是一种强大的算法设计技术,适用于需要探索所有可能解的问题。它通过系统地尝试所有可能的解,并在发现当前路径不可行时回溯到上一步,继续探索其他可能的路径。虽然回溯法的时间复杂度可能很高,但通过合理的剪枝策略,可以显著提高算法的效率。
回溯法的核心思想是"试探+回溯",它是解决组合优化问题、约束满足问题等的有效方法。在实际应用中,回溯法常常与其他算法技术(如动态规划、贪心算法等)结合使用,以解决更复杂的问题。
应用场景总结
- 组合问题:如子集和问题、组合总和问题等。
- 排列问题:如全排列、字符串排列等。
- 棋盘问题:如N皇后问题、数独问题等。
- 图搜索问题:如迷宫寻路、图的着色问题等。
- 约束满足问题:如数独、填字游戏等。
优化技巧总结
- 剪枝:通过各种策略减少搜索空间。
- 启发式搜索:优先搜索更有希望的状态。
- 记忆化:存储已计算过的状态结果,避免重复计算。
- 位运算优化:使用位运算加速状态表示和操作。
- 并行化:在多核环境下并行搜索不同的状态空间。
相关文章:
算法之回溯法
回溯法 回溯法定义与概念核心思想回溯法的一般框架伪代码表示C语言实现框架 回溯法的优化技巧剪枝策略实现剪枝的C语言示例记忆化搜索 案例分析N皇后问题子集和问题全排列问题寻路问题 回溯法的可视化理解决策树状态空间树回溯过程 回溯法与其他算法的比较回溯法与动态规划的区…...
Linux 内核中 cgroup(控制组) 作用是什么?
cgroup(Control Groups) 是 Linux 内核提供的一种机制,用于对 进程(或线程)组 进行资源限制、优先级分配、统计监控和任务控制。通过将进程分组管理,可以实现对 CPU、内存、磁盘 I/O、网络等系统资源的精细…...
Relay IR的核心数据结构
在 Apache TVM 的 Relay IR 中,基础节点(Var、Const、Call、Function 和 Expr)是构建计算图的核心数据结构。以下是对它们的详细解析,包括定义、作用、内部组成及相互关系: 1. Expr(表达式基类)…...
【MCP Node.js SDK 全栈进阶指南】初级篇(4):MCP工具开发基础
在MCP(模型上下文协议)的生态系统中,工具(Tools)是一种强大的扩展机制,允许AI模型执行各种操作并获取结果。本文将深入探讨MCP TypeScript-SDK中的工具开发基础,包括工具定义与参数验证、Zod模式详解与高级用法、异步工具处理与错误管理以及工具调用与结果格式化。通过学…...
3Blue1Brown/videos - 数学视频生成代码库
本文翻译整理自:https://github.com/3b1b/videos 文章目录 一、关于本项目相关链接资源关键功能特性 二、注意事项三、工作流1、核心原理2、Sublime 专用配置 四、快捷键功能说明 一、关于本项目 本项目包含用于生成 3Blue1Brown 数学解说视频的代码。 相关链接资源…...
vue3 + element-plus中el-drawer抽屉滚动条回到顶部
el-drawer抽屉滚动条回到顶部 <script setup lang"ts" name"PerformanceLogQuery"> import { ref, nextTick } from "vue"; ...... // 详情 import { performanceLogQueryByIdService } from "/api/performanceLog"; const onD…...
【inlining failed in call to always_inline ‘_mm_aesenclast_si128’】
gcc编译错误:inlining failed in call to always_inline ‘_mm_aesenclast_si128’: target specific option mismatch 消除方法: 假如是GCC,则CFLAGS添加如下编译选项:-maes 假如是cmake,参加如下脚本: …...
DB-GPT支持mcp协议配置说明
简介 在 DB-GPT 中使用 MCP(Model Context Protocol)协议,主要通过配置 MCP 服务器和智能体协作实现外部工具集成与数据交互。 开启mcp服务,这里以网页抓取为例 npx -y supergateway --stdio "uvx mcp-server-fetch" …...
前端之勇闯DOM关
一、DOM简介 1.1什么是DOM 文档对象类型(Document Object Model,简称DOM),是W3C组织推荐的处理课扩展标记语言(HTML或者XML)的标准编程接口 W3C已经定义了一系列的DOM接口,通过这些DOM接口可…...
实现鼠标拖拽图片效果
我们需要一个图片 可以是你的女朋友 可以是男朋友 ,我就拿窝的偶像 一个大佬——>甘为例吧! 哈哈哈哈哈 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport&q…...
nodejs模块暴露数据的方式,和引入(导入方式)方式
在 Node.js 中,模块之间通过 模块导出(exports) 和 模块导入(require 或 ESModule 的 import) 来进行数据和功能的共享。下面我详细总结一下两种主要的模块系统: 一、CommonJS 模块(Node.js 默认…...
AXOP33552: 400MHz 高速双通道运算放大器
AXOP33552是一款通用型高速双通道运算放大器,产品的工作电压为2V至5.5V,具有400MHz的带宽,f0.1dB的带宽为 120MHz,单通道静态电流为10mA。产品特别对噪声和THD做了优化,其噪声为5nV/√Hz 1MHz,2次谐波为-85…...
Spring Boot日志配置
目录 logback 使用logback 获取日志对象 日志级别 控制日志输出级别 日志输出格式控制 配置方式 日志转存 示例 日志是应用程序不可或缺的一部分,记录着程序运行的信息。主要作用有: 记录日常运营的重要信息记录应用报错信息记录过程数据等…...
不可变数据:基于持久化数据结构的状态管理
不可变数据:基于持久化数据结构的状态管理 一、 什么是不可变数据? 不可变数据是指一旦创建就无法更改的数据。在计算机科学中,不可变数据结构是指其内容或状态不能被修改的数据结构。在不可变数据中,所有修改操作都会生成新的数据副本&#…...
PyTorch卷积层填充(Padding)与步幅(Stride)详解及代码示例
本文通过具体代码示例讲解PyTorch中卷积操作的填充(Padding)和步幅(Stride)对输出形状的影响,帮助读者掌握卷积层的参数配置技巧。 一、填充与步幅基础 填充(Padding):在输入数据边缘…...
C++手撕STL-其叁
Deque 今天我们进入新的容器:deque,一般叫做双端队列。 比起传统的先入先出的队列queue,deque的出场率显然要低得多,事实上deque比起queue来说最大的特点就是多了一个push_front()和pop_front(),其他并没有太多不同。…...
AI大模型-window系统CPU版安装anaconda以及paddle详细步骤-亲测有效
window系统CPU版安装anaconda以及paddle详细步骤-亲测有效 一 安装anaconda 下载地址:anaconda下载 下载成功后,选择非C盘安装,按提示安装即可修改镜像文件 安装成功后,运行anaconda软件,若提示更新则点击更新,更新完后,修改镜像文件 找到用户目录下的.condarc文件,覆…...
UML概览
🥰名片: 🐳作者简介:乐于分享知识的大二在校生 🌳本系列专栏: (点击直达)统一建模语言UML 🫣致读者:欢迎评论与私信,对于博客内容的疑问都会尽量回复哒!!! 本文序: ⛰️本文介绍&…...
影刀填写输入框(web) 时出错: Can not convert Array to String
环境: 影刀5.26.24 Win10专业版 问题描述: [错误来源]行12: 填写输入框(web) 执行 填写输入框(web) 时出错: Can not convert Array to String. 解决方案: 1. 检查变量内容 在填写输入框之前,打印BT和NR变量的值ÿ…...
LLMs可在2位精度下保持高准确率
每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…...
C语言高频面试题——结构体和联合体区别
在 C 语言中,结构体(struct) 和 联合体(union) 是两种重要的复合数据类型,用于组织和管理多个相关的变量。尽管它们在语法上有些相似,但在内存布局、用途和行为上有显著的区别。以下是详细的对比…...
App爬虫工具篇-mitmproxy
mitmproxy 是一个支持 HTTP 和 HTTPS 的抓包程序,类似 Fiddler、Charles 的功能,它通过控制台的形式和ui界面的方式 此外,mitmproxy 还有两个关联组件,一个是 mitmdump,它是 mitmproxy 的命令行接口,利用它可以对接 Python 脚本,实现监听后的处理;另一个是 mitmweb,它…...
配置openjdk调试环境
先决条件 首先在Ubuntu 18.04上编译SlowDebug版本的openjdk。注意,这里我选择的是x86处理器的电脑。苹果M系列属于ARM芯片,指令集不一样。由于我在苹果上进行垃圾回收调试的时候会报SIGILL错误。为了了解JVM的内部工作原理,不要在这种问题上…...
加油站小程序实战教程13充值规则配置
目录 1 创建数据源2 搭建管理功能最终效果 我们目前已经实现了会员的注册以及登录功能,有了基础的认证之后就进入到了业务部分的开发。会员的话首先是可以进行充值,在充值的时候通常会有一定的赠送,本篇我们来开发一下充值规则的配置功能。 1…...
jQuery — 总结
介绍 jQuery是一款高效、轻量级的JavaScript库,旨在简化网页开发中的常见任务。自2006年发布以来,它凭借直观的语法和强大的功能迅速成为前端开发的标配工具。其核心设计理念是“写更少,做更多”,通过封装复杂的原生JavaScript操作…...
【信息安全工程师备考笔记】第二章 网络信息安全概述
第二章 网络攻击原理与常用方法 2.1 网络攻击概述 概念:损害网络 系统安全属性 的危害行为 危害行为基本类型 信息泄露攻击(机密性)完整性破坏攻击(完整性)拒绝服务攻击(可用性)非法使用攻击…...
国家自然科学基金答辩ppt制作案例模板下载
国家自然科学基金 National Natural Science Foundation of China 支持基础研究,坚持自由探索,发挥导向作用,发现和培养科学技术人才,促进科学技术进步和经济社会协调发展,逐渐形成和发展了由研究项目、人才项目和环境…...
代码随想录第三十七天|华为秋季笔试真题230823
刷题小记: 主要偏向扎实编码基础的考察,但貌似近些年题目难度有所提高,仅供参考。 卡码网136.获取连通的相邻节点列表(卡码网136.获取连通的相邻节点列表) 题目分析: 题目描述: 存在N个转发…...
KUKA机器人KR 3 D1200 HM介绍
KUKA KR 3 D1200 HM是一款小型机器人,型号中HM代表“Hygienic Machine(卫生机械)用于主副食品行业”,也是一款并联机器人。用于执行高速、高精度的抓取任务。这款机器人采用食品级不锈钢设计,额定负载为3公斤ÿ…...
从零开始创建MCP Server实战指南
一、MCP协议核心概念 1.1 什么是MCP? MCP(Model Context Protocol) 是一个标准化的“沟通规则”,由公司Anthropic提出,专门用于让大语言模型(LLM,比如通义千问、ChatGPT等)与外部工…...
C语言教程(十二):C 语言数组详解
一、引言数组的基本概念 数组是一组具有相同数据类型的元素的集合,这些元素在内存中连续存储。通过一个统一的数组名和下标来访问数组中的每个元素。使用数组可以方便地处理大量相同类型的数据,避免为每个数据单独定义变量。 二、一维数组 2.1 数组的…...
Linux[基础指令][2]
Linux[基础指令][2] cp(复制) 格式:cp [-rf] 源文件 {普通文件,目录} 拷贝 cp -r 递归拷贝目录 蓝色为目录,白色为具体文件 拷贝后面加一个不存在的文件会新建文件再拷贝 cp -ir -i是覆盖的时候询问 如果目标文件存在就会覆盖原有文件 mv(重命名/剪切) 格式:mv 源文件…...
MySQL_MCP_Server_pro接入cherry_studio实现大模型操作数据库
大模型直接与数据库交互,实现基本增删改查操作。首先贴下代码地址: https://github.com/wenb1n-dev/mysql_mcp_server_pro 安装环境:win10 1、下载代码 git clone https://github.com/wenb1n-dev/mysql_mcp_server_pro 2、使用conda创建…...
linux命令集
命令 grep -r --includeAndroid.bp libcfs ./ 参数说明 选项/参数作用-r递归搜索子目录。--includeAndroid.bp仅搜索名为 Android.bp 的文件(精确匹配文件名)。libcfs要搜索的关键字(单引号包裹特殊字符如 以避免被 Shell 解析ÿ…...
数据结构:链表
链表的概念及结构: 链表的概念: 链表是一种物理储存结构上非连续的储存结构,数据元素的逻辑顺序是通过引用链接次序实现的 那物理存储结构连续是什么意思? 之前我们讲过顺序表,顺序表的底层是数组,如下…...
【高并发内存池】从零到一的项目之高并发内存池整体框架设计及thread cache设计
个人主页 : zxctscl 专栏 【C】、 【C语言】、 【Linux】、 【数据结构】、 【算法】 如有转载请先通知 文章目录 前言1. 高并发内存池整体框架设计2. 高并发内存池--thread cache2.1 定长内存池的问题2.2 整体框架2.3 自由链表2.4 thread cache哈希桶的对齐规则2.5…...
电气动调节单座V型球阀带阀杆节流套沟槽孔板的作用-耀圣
电气动调节单座V球阀杆节流套是阀门中的一个重要组件,主要用于调节和控制流体介质的流量、压力或流速,同时兼具导向、密封和稳定阀杆运动降低流速减少冲刷的作用。以下是其具体功能和应用场景的详细说明: 1. 节流与流量控制** 作用原理**&am…...
vscode使用笔记
文章目录 安装快捷键 vscode是前端开发的一款利器。 安装 快捷键 ctrlp # 查找文件(和idea的双击shift不一样) ctrlshiftf # 搜索内容...
基于 SpringAI 整合 DeepSeek 模型实现 AI 聊天对话
目录 1、Ollama 的下载配置 与 DeepSeek 的本地部署流程 1.1 下载安装 Ollama 1.2 搜索模型并进行本地部署 2、基于 SpringAI 调用 Ollama 模型 2.1 基于OpenAI 的接口规范(其他模型基本遵循) 2.2 在 IDEA 中进行创建 SpringAI 项目并调用 DS 模型 3、基…...
Idea创建项目的搭建方式
目录 一、普通Java项目 二、普通JavaWeb项目 三、maven的JavaWeb项目 四、maven的Java项目 一、普通Java项目 1. 点击 Create New Project 2. 选择Java项目,选择JDK,点击Next 3. 输入项目名称(驼峰式命名法),可选…...
【MATLAB第115期】基于MATLAB的多元时间序列的ARIMAX的预测模型
【MATLAB第115期】基于MATLAB的多元时间序列的ARIMAX的预测模型 一、简介 ARIMAX(Autoregressive Integrated Moving Average with eXogenous inputs)模型是一种结合自回归(AR)、差分(I)、移动平均&a…...
【以太网安全】——防护高级特性配置总结
目前网络中以太网技术的应用非常广泛、然后、各种网络攻击的纯在(例如针对ARP DHCP 等攻击)不仅造成了网络合法用户无法正常访问网络资源、而且对网络信息安全构成严重威胁、以下配置是对局域网安全配置命令做详解 主要的安全威胁 MAC攻击:泛洪、欺骗 …...
微信小程序 van-dropdown-menu
点击其他按钮,关闭van-dropdown-menu下拉框 DropdownMenu 引入页面使用index.wxmlindex.scssindex.ts(重点)index.ts(全部) DropdownMenu 引入 在app.json或index.json中引入组件 "usingComponents": {"van-dropdown-menu": "vant/weapp…...
再见 Smartdaili,你好 Decodo!
我们将翻开新的篇章,推出新的名称以及更好的代理和刮擦解决方案。了解我们如何帮助全球用户构建、测试和扩展他们的公共网络数据项目。 Smartproxy,即后来的Smartdaili,由一个行业专业人士和企业家团队于2018年创立,其使命是创建一…...
海量文本中的词语距离:在 O(n) 时间内找到最近的词对
想象一个巨大的日志文件、一部鸿篇巨著或者网络爬虫抓取的数据——它们可能达到 TB 级别。现在,假设你需要找出两个特定的词(比如 词语1 和 词语2)在这段庞大文本中出现时,彼此“靠得最近”的距离是多少。 挑战: …...
TextCNN 模型文本分类实战:深度学习在自然语言处理中的应用
在自然语言处理(NLP)领域,文本分类是研究最多且应用最广泛的任务之一。从情感分析到主题识别,文本分类技术在众多场景中都发挥着重要作用。最近,我参与了一次基于 TextCNN 模型的文本分类实验,从数据准备到…...
前台调用接口的方式及速率对比
一、引言 在现代 Web 开发中,前台与后台的数据交互至关重要,而调用接口是实现这一交互的关键手段。不同的接口调用方式在速率上可能存在差异,这会影响用户体验和应用性能。本文将详细介绍几种常见的前台调用接口方式,并对它们的速…...
高级java每日一道面试题-2025年4月21日-基础篇[反射篇]-如何使用反射获取一个类的所有方法?
如果有遗漏,评论区告诉我进行补充 面试官: 如何使用反射获取一个类的所有方法? 我回答: 在Java中,反射是一种强大的机制,允许程序在运行时检查或“反射”自身,从而动态地操作类、字段、方法和构造函数等。这在需要动态调用方法…...
tomcat集成redis实现共享session
中间件:Tomcat、Redis、Nginx jar包要和tomcat相匹配 jar包:commons-pool2-2.2.jar、jedis-2.5.2.jar、tomcat-redis-session-manage-tomcat7.jar 配置Tomcat /conf/context.xml <?xml version1.0 encodingutf-8?> <!--Licensed to the A…...
2.6 递归
递归 特性: >.一递一归 >.终止条件 一般为:0 1 -1 #测试函数的返回值为函数 def test_recursion():return test_recursion() print(test_recursion()) RecursionError: maximum recursion depth exceeded #案例:计算 …...