人工智能原理实验二:搜索方法

一、实验目的

本实验课程是计算机、智能、物联网等专业学生的一门专业课程,通过实验,帮助学生更好地掌握人工智能相关概念、技术、原理、应用等;通过实验提高学生编写实验报告、总结实验结果的能力;使学生对智能程序、智能算法等有比较深入的认识。

  1. 掌握人工智能中涉及的相关概念、算法。
  2. 熟悉人工智能中的知识表示方法;
  3. 熟悉盲目搜索和启发式搜索算法的应用;
  4. 掌握问题表示、求解及编程实现。
  5. 掌握不同搜索策略的设计思想、步骤、性能。

二、基本要求

1、实验前,复习《人工智能》课程中的有关内容。

2、准备好实验数据。

3、编程要独立完成,程序应加适当的注释。

4、完成实验报告。

三、实验软件

使用C或C++(Visual studio)(不限制语言使用)。

四、实验内容:

(1)

1、在图1,3*3的方格棋盘上,摆放着1到8这八个数码,有1个方格是空。

2、如图1所示,要求对空格执行空格左移、空格右移、空格上移和空格下移这四个操作使得棋盘从初始状态(图1左)到目标状态(图1右)。

3、可自行设计初始状态。目标状态为数字从小到大按顺时针排列。

4、分别用广度优先搜索策略、深度优先搜索策略和启发式搜索算法(A*算法)求解八数码问题;分析估价函数对启发式搜索算法的影响;探究各个搜索算法的特点。

 

(2)罗马尼亚问题

根据上图以Zerind为初始状态,Bucharest为目标状态实现搜索,分别以贪婪搜索(只考虑直线距离)和A*算法求解最短路径。 按顺序列出贪婪算法探索的扩展节点和其估价函数值,A*算法探索的扩展节点和其估计值。

选做部分:自行设计一个新的启发式函数,并分析该函数的可采纳性和优势(与启发式函数定义为“Zerind到Bucharest的直线距离”相比较)。

五、学生实验报告要求

1、实验报告需要包含以下几个部分

(1)状态表示的数据结构

实验一:

struct State {int puzzle[3][3];int cost;  // 从起始状态到当前状态的代价int heuristic;  // 启发式估计的代价
};

结构体包含一个3x3的整数数组 puzzle 表示八数码的状态,以及两个整数 cost 和 heuristic 分别表示从起始状态到当前状态的代价和启发式估计的代价。

实验二:

状态使用图(city_graph)和字典(to_B_distance)表示。图(city_graph)是一个列表的列表,其中每个内部列表表示两个城市之间的边以及相应的距离。字典(to_B_distance)存储每个城市到目标城市 'B' 的直线距离。起始城市和结束城市分别定义为 'start_city' 和 'end_city'。

(2)状态扩展规则的表示

状态扩展规则在 greedy 和 A_plus 函数中表示。这些函数基于特定的准则探索相邻城市。对于贪婪算法,它选择距离目标最近的城市。在A*算法中,考虑总成本函数 f(x) = g(x) + h(x),其中 g(x) 是从开始到当前节点的成本,h(x) 是启发式(到目标的直线距离)

(3)搜索产生的状态空间图

八数码:

罗马尼亚问题:

(4)OPEN表和CLOSE表变化过程

Expnd.node

Open list

{Z}

Z

{O,A}

A

{S,T}

S

{R,F}

R

{P,C}

P

{C,B}

B(goal)

{G,U}

(5)程序清单

实验(1)

#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>using namespace std;// 表示八数码状态的结构体
struct State {int puzzle[3][3];int cost;  // 从起始状态到当前状态的代价int heuristic;  // 启发式估计的代价
};// 定义操作:空格左移、右移、上移、下移
State moveLeft(const State& s) {State newState = s;int emptyRow, emptyCol;// 找到空格的位置for (int i = 0; i < 3; ++i) {for (int j = 0; j < 3; ++j) {if (s.puzzle[i][j] == 0) {emptyRow = i;emptyCol = j;break;}}}// 左移操作,交换空格和左侧数字的位置if (emptyCol > 0) {swap(newState.puzzle[emptyRow][emptyCol], newState.puzzle[emptyRow][emptyCol - 1]);}return newState;
}State moveRight(const State& s) {State newState = s;int emptyRow, emptyCol;// 找到空格的位置for (int i = 0; i < 3; ++i) {for (int j = 0; j < 3; ++j) {if (s.puzzle[i][j] == 0) {emptyRow = i;emptyCol = j;break;}}}// 右移操作,交换空格和右侧数字的位置if (emptyCol < 2) {swap(newState.puzzle[emptyRow][emptyCol], newState.puzzle[emptyRow][emptyCol + 1]);}return newState;
}State moveUp(const State& s) {State newState = s;int emptyRow, emptyCol;// 找到空格的位置for (int i = 0; i < 3; ++i) {for (int j = 0; j < 3; ++j) {if (s.puzzle[i][j] == 0) {emptyRow = i;emptyCol = j;break;}}}// 上移操作,交换空格和上方数字的位置if (emptyRow > 0) {swap(newState.puzzle[emptyRow][emptyCol], newState.puzzle[emptyRow - 1][emptyCol]);}return newState;
}State moveDown(const State& s) {State newState = s;int emptyRow, emptyCol;// 找到空格的位置for (int i = 0; i < 3; ++i) {for (int j = 0; j < 3; ++j) {if (s.puzzle[i][j] == 0) {emptyRow = i;emptyCol = j;break;}}}// 下移操作,交换空格和下方数字的位置if (emptyRow < 2) {swap(newState.puzzle[emptyRow][emptyCol], newState.puzzle[emptyRow + 1][emptyCol]);}return newState;
}// 检查两个状态是否相等
bool isEqual(const State& s1, const State& s2) {return memcmp(s1.puzzle, s2.puzzle, sizeof(s1.puzzle)) == 0;
}// 输出状态
void printState(const State& s) {for (int i = 0; i < 3; ++i) {for (int j = 0; j < 3; ++j) {cout << s.puzzle[i][j] << " ";}cout << endl;}cout << endl;
}// 广度优先搜索算法
bool bfs(const State& start, const State& goal) {queue<State> pq;pq.push(start);while (!pq.empty()) {State current = pq.front();pq.pop();if (isEqual(current, goal)) {// 找到目标状态cout << "Found solution!" << endl;return true;}// 执行四个操作,将新状态加入优先队列State next;next = moveLeft(current);if (!isEqual(next, current)) {pq.push(next);cout << "Move Left:" << endl;printState(next);}next = moveRight(current);if (!isEqual(next, current)) {pq.push(next);cout << "Move Right:" << endl;printState(next);}next = moveUp(current);if (!isEqual(next, current)) {pq.push(next);cout << "Move Up:" << endl;printState(next);}next = moveDown(current);if (!isEqual(next, current)) {pq.push(next);cout << "Move Up:" << endl;printState(next);}}// 未找到解决方案cout << "No solution found." << endl;return false;
}int main() {// 设计初始状态和目标状态State start = { 2, 1, 3, 8, 0, 4, 7, 6, 5 };State goal = { 1, 2, 3, 8, 0, 4, 7, 6, 5 };cout << "Initial State:" << endl;printState(start);cout << "Goal State:" << endl;printState(goal);// 调用广度优先搜索算法bfs(start, goal);return 0;
}

深度优先搜索算法部分代码(其他代码不变)

// 深度优先搜索算法
bool dfs(const State& start, const State& goal) {stack<State> s;s.push(start);while (!s.empty()) {State current = s.top();s.pop();if (isEqual(current, goal)) {// 找到目标状态cout << "Found solution!" << endl;return true;}// 执行四个操作,将新状态加入栈State next;next = moveLeft(current);// 检查是否为合法状态,避免重复搜索if (!isEqual(next, current)) {s.push(next);}next = moveRight(current);if (!isEqual(next, current)) {s.push(next);}next = moveUp(current);if (!isEqual(next, current)) {s.push(next);}next = moveDown(current);if (!isEqual(next, current)) {s.push(next);}}
}

 

A*算法

...
// 曼哈顿距离估价函数
int manhattanDistance(const State& s, const State& goal) {int distance = 0;for (int i = 0; i < 3; ++i) {for (int j = 0; j < 3; ++j) {int value = s.puzzle[i][j];if (value != 0) {int goalRow, goalCol;goalRow = (value - 1) / 3;goalCol = (value - 1) % 3;distance += abs(i - goalRow) + abs(j - goalCol);}}}return distance;
}
// 输出状态
void printState(const State& s) {for (int i = 0; i < 3; ++i) {for (int j = 0; j < 3; ++j) {cout << s.puzzle[i][j] << " ";}cout << "   ";}cout << "Cost: " << s.cost << " Heuristic: " << s.heuristic << endl << endl;
}// 用于优先队列的比较函数,用于A*算法中选择最小代价的节点
struct CompareStates {bool operator()(const State& s1, const State& s2) {return (s1.cost + s1.heuristic) > (s2.cost + s2.heuristic);}
};// A*搜索算法
bool aStar(const State& start, const State& goal) {priority_queue<State, vector<State>, CompareStates> pq;pq.push(start);while (!pq.empty()) {State current = pq.top();pq.pop();if (isEqual(current, goal)) {// 找到目标状态cout << "Found solution!" << endl;return true;}// 执行四个操作,将新状态加入优先队列State next;next = moveLeft(current);if (!isEqual(next, current)) {next.cost = current.cost + 1;next.heuristic = manhattanDistance(next, goal);pq.push(next);}next = moveRight(current);if (!isEqual(next, current)) {next.cost = current.cost + 1;next.heuristic = manhattanDistance(next, goal);pq.push(next);}next = moveUp(current);if (!isEqual(next, current)) {next.cost = current.cost + 1;next.heuristic = manhattanDistance(next, goal);pq.push(next);}next = moveDown(current);if (!isEqual(next, current)) {next.cost = current.cost + 1;next.heuristic = manhattanDistance(next, goal);pq.push(next);}}// 未找到解决方案cout << "No solution found." << endl;return false;
}
...

实验(二)

# 罗马利亚图存储
city_graph = [['A', 'Z', 75],['A', 'T', 118],['Z', 'O', 71],['O', 'S', 151],['A', 'S', 140],['S', 'F', 99],['S', 'R', 80],['R', 'P', 97],['R', 'C', 146],['T', 'L', 111],['L', 'M', 70],['M', 'D', 75],['D', 'C', 120],['C', 'P', 138],['P', 'B', 101],['F', 'B', 211],['B', 'G', 90],['B', 'U', 85],['U', 'V', 142],['V', 'I', 92],['I', 'N', 87],['U', 'H', 98],['H', 'E', 86]]
# B到其他城市的直线距离
to_B_distance = {'A': 366,'B': 0,'C': 160,'D': 242,'E': 161,'F': 176,'G': 77,'H': 151,'I': 226,'L': 244,'M': 241,'N': 234,'O': 380,'P': 100,'R': 163,'S': 253,'T': 329,'U': 80,'V': 199,'Z': 374}
start_city = 'Z'
end_city = 'B'
# 判断某条路是否走过
def is_visited(A_city, B_city, went_road):for i in range(len(went_road)):if went_road[i][0] == A_city and went_road[i][1] == B_city:return 1if went_road[i][0] == B_city and went_road[i][1] == A_city:return 1return 0
# 根据访问结点打印结果
def print_ans(ans, distance):print("路径为:", end="")for i in range(len(ans)):if i != len(ans) - 1:print(ans[i], "->", end="")else:print(ans[i])print("路径长度之和为:",distance)
# 贪婪算法,每次寻找离目标城市直线距离最短的城市走
def greedy(start, end):print("贪婪算法:")went_road = []ans_road = [start]while 1:# 查找开始结点的所有临近城市及边start_near_city = []for item in city_graph:if item[0] == start:start_near_city.append([item[1], item[2]])if item[1] == start:start_near_city.append([item[0], item[2]])# 挑选到目标结点直接距离最短的城市direct_distance = 999for item in start_near_city:if to_B_distance[item[0]] < direct_distance and is_visited(start, item[0], went_road) == 0:direct_distance = to_B_distance[item[0]]min_distance = item[1]min_city = item[0]# 如果找到一条直线距离最短的路且没有访问过,则选择走这条路并记录走过的这条路if direct_distance != 999:went_road.append([start, min_city, min_distance])print(min_city, direct_distance)start = min_cityans_road.append(start)else:print("终点不可达!")return 0# 找到终点返回路径及总长度if start == end:ans_distance = 0for i in range(len(went_road)):ans_distance += went_road[i][2]print_ans(ans_road, ans_distance)return 1
# A*算法,每次寻找f=g+h最小的值走
def A_plus(start, end):print("A*算法:")went_road = []ans_road = [start]while 1:# 扫描图,获取与start相连的所有边go_to_city = []for item in city_graph:if item[0] == start:go_to_city.append([item[1], item[2]])if item[1] == start:go_to_city.append([item[0], item[2]])# 寻找fx最小的可达城市和距离,不能走回访问过的路hx = 0for j in went_road:hx += j[2]fx_distance = 999for item in go_to_city:if hx+item[1]+to_B_distance[item[0]] < fx_distance and is_visited(start, item[0], went_road) == 0:fx_distance = hx+item[1]+to_B_distance[item[0]]min_distance = item[1]min_city = item[0]# 如果找到可达的最小城市,则将其访问过的路径加入went_roadif fx_distance != 999:went_road.append([start, min_city, min_distance])print(min_city, fx_distance)start = min_cityans_road.append(start)else:print("终点不可达!")return 0# 找到终点返回路径及总长度if start == end:ans_distance = 0for i in range(len(went_road)):ans_distance += went_road[i][2]print_ans(ans_road, ans_distance)return 1greedy(start_city, end_city)
A_plus(start_city, end_city)

(6)实验结果讨论

实验(1)

manhattanDistance函数计算了曼哈顿距离,并且在每个状态扩展时计算了启发式估价函数。

估价函数的选择会影响算法的性能。

各搜索算法特点:

BFS:保证找到最短路径,但可能需要较多内存。

DFS:内存需求较低,但可能找到的路径不是最短的。

A*算法:结合了BFS和DFS的优点,通过选择合适的估价函数,可以找到最短路径,并在性能上进行优化。

实验(二)

思考并解答以下问题

1、你所采用的估价函数f(n) = g(n) + h(n)中,g(n)和h(n)的主要作用是什么?

g(n)表示从起始节点到当前节点n的实际代价,h(n)表示从节点n到目标节点的启发式估计代价。g(n)的主要作用是衡量已经花费的代价,h(n)则是启发式地估计剩余的代价。通过这两者的和f(n),我们可以评估当前节点n的总代价。在A*搜索算法中,选择下一个扩展的节点时,会选择f(n)最小的节点,即综合考虑实际代价和启   发式估计代价。

2、结合本实验举例说明不同启发策略对实验的效果有何影响?(可列出图表说明)

不同的启发策略对实验的效果会产生影响。在A*算法中,估价函数的选择会影响搜索的效率。例如,在罗马尼亚问题中,使用直线距离作为启发式函数可能会更快地找到解决方案,因为它提供了一种更直接的路径选择。然而,选择不同的启发式函数可能导致不同的搜索效果,有的可能更接近实际最优解,有的可能更快速但代价稍高。

3、若问题的初始状态是随机产生的,你的实验程序应该如何改进?(如图形属性的设置、图形队列存入文件等)添加代码,根据实际需要添加其他辅助函数。

  1. 将实验数据存入文件:可以将每个实验的数据(如搜索路径、代价等)存储在文件中,以便后续分析或可视化。
  2. 设置随机种子:如果使用了随机数生成器,可以设置随机种子以确保实验可重复。
  3. 添加统计信息:在程序中加入统计信息,如搜索节点的数量、路径的长度等,以便更全面地评估算法性能。

4、尝试使用一致代价(等代价)搜索, 迭代加深的深度优先搜索算法求解上述问题,并根据实验结果分析深度优先搜索,一致代价(等代价)搜索,迭代加深的深度优先搜索算法, A*搜索的时间和空间复杂度。

  1. 深度优先搜索:时间复杂度为O(b^m),其中b是分支因子,m是最大搜索深度;空间复杂度为O(bm)。
  2. 一致代价(等代价)搜索:时间复杂度和空间复杂度均为O(b^(C*/ε)),其中C*是最优解的代价,ε是最小的代价步长。
  3. 迭代加深的深度优先搜索:时间复杂度为O(b^d),其中d是最优解的深度;空间复杂度为O(bd)。
  4. A*搜索:在最坏情况下,时间复杂度为O(b^d),其中d是最优解的深度;空间复杂度为O(b^d)。

(5)指出无信息搜索策略和有信息搜索策略的不同并比较其性能。

  • 完备性:无信息搜索策略可能在某些情况下无法找到解决方案,而有信息搜索策略通常是完备的。
  • 最优性:有信息搜索策略通常更容易找到最优解,而无信息搜索策略通常只能找到可行解。
  • 时间和空间效率:有信息搜索策略通常能更快找到解决方案,但在计算和存储信息方面可能需要更多的时间和空间。
  • 领域知识要求:有信息搜索策略通常需要对问题领域有一定的了解,而无信息搜索策略对领域知识的要求较低。
  • 在实际应用中,选择无信息搜索还是有信息搜索通常取决于问题的性质和要求。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/58604.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【AI应用】大模型工具如何助力文字创意工作(提示词Prompt+谷歌NotebookLM)

出发点&#xff1a;身处信息碎片和过载的时代&#xff0c;如何在日常工作学习中汇总并高效梳理知识&#xff1f;普通用户又如何激发AI大模型产出高质量的结果呢&#xff1f;本文将给出这两个问题的一些解决思路。 0、提纲&#xff1a; 提示词工程应知应会NotebookLM惊艳登场总…

springboot 使用 weixin-java-pay 支付demo

springboot引入依赖 <dependency><groupId>com.github.binarywang</groupId><artifactId>weixin-java-pay</artifactId><version>4.6.0</version></dependency>配置 wx:pay:appId: *********mchId: ********apiV3Key: ******…

剧本杀门店预约小程序,在线一键预约体验

剧本杀作为集社交、角色扮演、休闲娱乐为一体的游戏&#xff0c;吸引了年轻人的目光。当下&#xff0c;随着市场的发展&#xff0c;剧本杀行业正面临挑战&#xff0c;对于门店来说&#xff0c;如何找到新的发展方向&#xff0c;在市场中脱颖而出是重中之重&#xff01; 线上线…

SpringBoot技术:闲一品交易的新机遇

摘 要 随着科学技术的飞速发展&#xff0c;社会的方方面面、各行各业都在努力与现代的先进技术接轨&#xff0c;通过科技手段来提高自身的优势&#xff0c;闲一品交易平台当然也不能排除在外。闲一品交易平台是以实际运用为开发背景&#xff0c;运用软件工程原理和开发方法&…

柔性数组的使用

1.只有一个malloc的情况 //柔性数组的使用 #include<stdio.h> #include<stdlib.h> #include<errno.h> struct s {int i;int a[]; }; int main() {struct s* ps (struct s*)malloc(sizeof(struct s) 20 * sizeof(int));if (ps NULL){perror("malloc&…

从0到1,用Rust轻松制作电子书

我之前简单提到过用 Rust 做电子书&#xff0c;今天分享下如何用Rust做电子书。制作电子书其实用途广泛&#xff0c;不仅可以用于技术文档&#xff08;对技术人来说非常方便&#xff09;&#xff0c;也可以制作用户手册、笔记、教程等&#xff0c;还可以应用于文学创作。 如果…

计算机毕业设计django+大模型租房推荐系统 租房可视化 租房大屏可视化 租房爬虫 spark 58同城租房爬虫 房源推荐系统

开题报告&#xff1a;《Django大模型租房推荐系统》 一、研究背景与意义 随着城市化进程的加快&#xff0c;房屋租赁市场日益繁荣。然而&#xff0c;传统的房屋租赁方式存在信息不对称、交易流程繁琐等问题&#xff0c;给租户和房主带来了诸多不便。因此&#xff0c;开发一套…

ubuntu进程相关操作

进程相关操作 1.查看进程top/htop top 命令输出解释 在 top 命令中&#xff0c;字段通常表示如下&#xff1a; USER&#xff1a;进程的所有者。PR&#xff1a;优先级。NI&#xff1a;nice 值&#xff08;优先级调整&#xff09;。VIRT&#xff1a;进程使用的虚拟内存总量。…

如何在算家云搭建LongWriter(长文创作)

一、 LongWriter 简介 在自然语言处理领域&#xff0c;随着对长文本处理需求的不断增加&#xff0c;能够高效生成长文本的语言模型变得至关重要。LongWriter 的推出正是为了打破传统语言模型在生成超长文本时的限制。LongWriter-glm4-9b 是基于glm-4-9b进行训练的&#xff0c;…

C语言串口接收GPS数据

要在C语言中接收GPS数据&#xff0c;需要使用串口通信来与GPS设备进行数据交互。一个简单的串口通信代码主要包含了以下几个部分&#xff1a; 1.标准库头文件 stdio.h&#xff1a;包含输入输出函数&#xff0c;如 printf string.h&#xff1a;包含字符串处理函数&#xff0c…

【天线&空中农业】蜜蜂检测系统源码&数据集全套:改进yolo11-ASF

改进yolo11-dysample等200全套创新点大全&#xff1a;蜜蜂检测系统源码&#xff06;数据集全套 1.图片效果展示 项目来源 人工智能促进会 2024.10.30 注意&#xff1a;由于项目一直在更新迭代&#xff0c;上面“1.图片效果展示”和“2.视频效果展示”展示的系统图片或者视频可…

NavVis LX系列产品典型应用—现有住宅装修改造-沪敖3D

现有住宅装修改造项目的 数据捕捉和测量技术 当Jay Ure着手翻新和美化自己的新家时&#xff0c;他敏锐地发现这是现场测试NavVis VLX的绝佳机会。 为了全面评估&#xff0c;他聘请了一位工程师&#xff0c;采用传统的全站仪技术进行地形测绘。之后&#xff0c;他用移动扫描设…

点评项目-12-好友关注

好友关注主要有三个功能&#xff1a; 1.关注和取关 2.共同关注 3.关注推送 关注和取关 涉及到的表&#xff0c;中间表&#xff1a;tb_follow,是博主 User 和粉丝 User 的中间表 请求一&#xff0c;查询是否关注了该用户&#xff1a; 请求路径&#xff1a;follow/or/not/…

Android 在github网站下载项目:各种很慢怎么办?比如gradle下载慢;访问github慢;依赖下载慢

目录 访问github慢gradle下载慢依赖下载慢 前言 大家好&#xff0c;我是前期后期&#xff0c;在网上冲浪的一名程序员。 为什么要看这篇文章呢&#xff1f;问题是什么&#xff1f; 我们在Github上面看到一些好的项目的时候&#xff0c;想下载下来研究学习一下。但经常遇到各…

阿里面试:为什么MySQL不建议使用Delete删除数据?

MySQL有建议过不要使用他们家的DELETE吗&#xff1f;在MySQL 8.0的官方文档里没有找到不建议使用DELETE的文字。 DELETE VS NOT DELETE&#xff0c;这是由来已久的问题 时间回到2009的8月30号。大佬Ayende Rahien——也被称为Oren Eini&#xff0c;Hibernating Rhinos公司的C…

机器学习中的嵌入是什么?

一、说明 嵌入是真实世界对象的数字表示&#xff0c;机器学习&#xff08;ML&#xff09;和人工智能&#xff08;AI&#xff09;系统利用它来像人类一样理解复杂的知识领域。例如&#xff0c;计算算法了解 2 和 3 之间的差为 1&#xff0c;这表明与 2 和 100 相比&#xff0c;2…

Python | Leetcode Python题解之第517题超级洗衣机

题目&#xff1a; 题解&#xff1a; class Solution:def findMinMoves(self, machines: List[int]) -> int:tot sum(machines)n len(machines)if tot % n:return -1avg tot // nans, s 0, 0for num in machines:num - avgs numans max(ans, abs(s), num)return ans

若依框架部署到服务器后头像资源访问404

排错过程 第一开始以为是代理出问题了 官网给出的解决方案 第一种是用代理后端接口&#xff0c;第二种是重写路径直接访问静态文件 接口通过捕获profile开头的路径/profile/avatar…&#xff0c;转为/home…/avatar找到我们在该路径下的文件 但是我想了一下&#xff0c;我ngin…

《手写Spring渐进式源码实践》实践笔记(第十二章 aop融入bean生命周期)

提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 第十二章 将AOP融入Bean生命周期背景目标设计实现代码结构类图实现步骤 测试事先准备自定义拦截方法Spring.xml 配置AOP测试用例测试结果&#xff1a; 总结 第十二章…

Three.js 快速入门构建你的第一个 3D 应用

![ 开发领域&#xff1a;前端开发 | AI 应用 | Web3D | 元宇宙 技术栈&#xff1a;JavaScript、React、Three.js、WebGL、Go 经验经验&#xff1a;6年 前端开发经验&#xff0c;专注于图形渲染和AI技术 开源项目&#xff1a;github 晓智元宇宙、数字孪生引擎、前端面试题 大家好…