蚁群算法ACS处理旅行商问题TSP【Java实现】

1. 介绍

蚁群算法是一种群体智能算法,模拟了蚂蚁寻找食物时的行为,通过蚂蚁之间的信息交流和合作,最终实现全局最优解的寻找【是否找得到和迭代次数有关】。

蚁群算法的基本思想是将搜索空间看作一个由节点组成的图,每个节点代表一种可能的解决方案,而边则代表两个节点之间的关联关系。在这个图中,一只蚂蚁在搜索过程中会沿着路径移动,每经过一个节点,它会留下信息素,这些信息素会吸引其他蚂蚁跟随它的路径。同时,蚂蚁在移动的过程中也会依据信息素的浓度,更有可能选择路径上信息素浓度较高的节点。

蚂蚁群算法的核心是信息素的更新和挥发。信息素的更新是指当一只蚂蚁找到一个更优的解决方案时,它会在路径上留下更多的信息素,这样其他蚂蚁就更有可能跟随这个路径,从而加速搜索过程。信息素的挥发则是指在一定的时间间隔内,信息素会自然挥发,这样可以避免信息素积累过多导致搜索过早收敛。

蚂蚁群算法是一种高效的全局优化算法,广泛应用于组合优化、信号处理、机器学习、图形图像处理等领域。

2. 主体公式

蚁群算法中,蚂蚁到达每个点的概率是根据该点的信息素浓度和距离来计算的。具体地说,设当前蚂蚁所在的城市为 i i i ,已经访问过的城市集合为 J J J ,未访问过的城市集合为 K K K ,则蚂蚁由 i i i 到达城市 j j j 的概率可以用如下公式计算:
p i , j = [ τ i , j ] α [ η i , j ] β ∑ k ∈ K [ τ i , k ] α [ η i , k ] β p_{i,j}=\frac{[\tau_{i,j}]^{\alpha}[\eta_{i,j}]^{\beta}}{\sum\limits_{k\in K} [\tau_{i,k}]^{\alpha}[\eta_{i,k}]^{\beta}} pi,j=kK[τi,k]α[ηi,k]β[τi,j]α[ηi,j]β

其中, τ i , j \tau_{i,j} τi,j 表示从城市 i i i 到城市 j j j 的信息素浓度, η i , j \eta_{i,j} ηi,j 表示从城市 i i i 到城市 j j j 的距离的倒数, α \alpha α β \beta β 分别表示信息素重要程度因子和距离重要程度因子,通常 α \alpha α β \beta β 的值都为正数。在选择下一个要访问的城市时,蚂蚁会根据这个概率进行随机选择,不难发现,信息素浓度越大,城市间的距离越短,对应的概率就会越大。

对于一只蚂蚁走过的路径上的每条边 $ (i,j)$ ,它在该边上留下的信息素增量为:
Δ τ i j = q L k \Delta \tau_{ij} = \dfrac{q}{L_k} Δτij=Lkq
其中, q q q 是信息素增加强度系数, L k L_k Lk 表示第 k k k 只蚂蚁完成路径的长度。

参数代码形式如下👇:

	// 距离矩阵
	private double[][] distance;
	// 信息素矩阵
	private double[][] pheromone; 
	// 城市数量
	private int cityNum; 
	// 蚂蚁数量
	private int antNum; 
	// 最大迭代次数
	private int maxGen; 
	// 信息素因子
	private double alpha; 
	// 距离重要程度因子
	private double beta; 
	// 信息素挥发因子
	private double rho; 
	// 信息素增加强度系数
	private double q; 
	// 最优路径
	private int[] bestTour; 
	// 最优路径长度
	private double bestLength; 

3. 算法实现步骤

  1. 初始化信息素矩阵,初始时各路径上信息素浓度相同

    	// 初始化信息素矩阵
    	private void initPheromone() {
    		// 信息素初始值
    		double initPheromone = 1.0 / (cityNum * distance[0][1]);
    		for (int i = 0; i < cityNum; i++) {
    			for (int j = 0; j < cityNum; j++) {
    				// 各条路径的信息素浓度
    				pheromone[i][j] = initPheromone;
    			}
    		}
    	}
    
  2. 第一只蚂蚁从随机一个城市出发,综合信息素浓度以及到达各城市的距离,计算得到公式 p i j p_{ij} pij 的分子,存储在数组 p p p 中,同时将分母用累加和 s u m sum sum 存储

    一个圆形转盘区域标记为 [ 0 , 1 ) [0,1) [0,1) ∑ p i j = 1 \sum p_{ij} = 1 pij=1

  3. 选择下一个城市采用轮盘赌的方式,即产生一个在 [ 0 , 1 ) [0,1) [0,1) 随机数 r a n d rand rand 作为圆形转盘的指针指向的位置,利用 t m p tmp tmp p i j p_{ij} pij 累加和】锁定轮盘上的各个区域,一旦 t m p ≥ r a n d tmp \ge rand tmprand ,说明当前城市就是下一个目的地,将此城市在 v i s i t e d visited visited 中标记,同时将其加入访问顺序 t o u r tour tour 中。

    4e1eb5ff3c9d479796116fc341ac826e

    用此方法访问所有节点后,返回范围顺序 t o u r tour tour

    	// 蚂蚁移动
    	// pheromone : 信息素矩阵
    	private int[] antMove(double[][] pheromone) {
    		// 存储依次到达的城市序号
    		int[] tour = new int[cityNum];
    		boolean[] visited = new boolean[cityNum];
    		Random random = new Random();
    		// 生成一个不大于 cityNum 的伪随机整数
    		int start = random.nextInt(cityNum);
    		// 从 start 城市出发,此城市标记为已访问
    		visited[start] = true;
    		// 第一个访问 start 城市
    		tour[0] = start;
    		for (int i = 1; i < cityNum; i++) {
    			// 当前蚂蚁所在的城市
    			int curCity = tour[i - 1];
    			// p 存放概率公式的分子部分
    			double[] p = new double[cityNum];
    			// sum 存放概率公式的分母部分
    			double sum = 0.0;
    			for (int j = 0; j < cityNum; j++) {
    				// 城市 j 没有被访问过
    				if (!visited[j]) {
    					p[j] = Math.pow(pheromone[curCity][j], alpha) * Math.pow(1.0 / distance[curCity][j], beta);
    					sum += p[j];
    				}
    			}
    			// 用 [0,1] 范围的伪随机数代替轮盘针转到的位置
    			double rand = random.nextDouble()  ;
    			double tmp = 0.0;
    			int nextCity = 0;
    			for (int j = 0; j < cityNum; j++) {
    				if (!visited[j]) {
    					// 每个概率 p[j]/sum 代表轮盘上的一块区域
    					// tmp 累积值代表指针到的位置
    					tmp += p[j]/sum;
    					// 超过 rand ,说明来到了这个区间,即当前城市就是目标城市
    					if (tmp >= rand) {
    						nextCity = j;
    						break;
    					}
    				}
    			}
    			// 标记城市为已访问
    			visited[nextCity] = true;
    			// 记录蚂蚁的下个城市目的地
    			tour[i] = nextCity;
    		}
    		return tour;
    	}
    
  4. 由于知道城市访问顺序 t o u r tour tour ,因此不难得到访问总距离 t o u r L e n g t h tourLength tourLength ,如果这个总距离小于 b e s t T o u r bestTour bestTour ,说明这是更优的解法,因此更新 b e s t T o u r bestTour bestTour b e s t L e n g t h bestLength bestLength

  5. 蚂蚁访问完各个城市后开始更新各路径上的信息素增量 Δ τ \Delta\tau Δτ Δ τ \Delta\tau Δτ 由是信息素增加强度系数 Q Q Q,和 L L L 蚂蚁完成路径的长度决定

  6. 当所有蚂蚁都将城市访问完毕后,将信息素增量 Δ τ \Delta\tau Δτ 更新至原路径上的信息素中,同时还需要考虑信息素的挥发,最终得到第一次迭代后的信息素矩阵情况

    	// 更新信息素矩阵
    	private void updatePheromone() {
    		// 蚂蚁在经过路径上留下的信息增量
    		double[][] deltaPheromone = new double[cityNum][cityNum];
    		for (int i = 0; i < antNum; i++) {
    			// 单只蚂蚁根据当前信息素矩阵而得到的访问顺序
    			int[] tour = antMove(pheromone);
    			// 当前蚂蚁访问的长度
    			double tourLength = getTourLength(tour);
    			// 说明可以优化
    			if (tourLength < bestLength) {
    				// 更新最短路径
    				bestLength = tourLength;
    				// 由于有了最短路径,因此存储的访问路径也需要更新
    				System.arraycopy(tour, 0, bestTour, 0, cityNum);
    			}
    			for (int j = 0; j < cityNum - 1; j++) {
    				int city1 = tour[j];
    				int city2 = tour[j + 1];
    				// 信息素增量Δτ,只要有蚂蚁经过就会变多
    				deltaPheromone[city1][city2] += q / tourLength;
    				deltaPheromone[city2][city1] = deltaPheromone[city1][city2];
    			}
    			deltaPheromone[tour[cityNum - 1]][tour[0]] += q / tourLength;
    			deltaPheromone[tour[0]][tour[cityNum - 1]] = deltaPheromone[tour[cityNum - 1]][tour[0]];
    		}
    		for (int i = 0; i < cityNum; i++) {
    			for (int j = 0; j < cityNum; j++) {
    				// 原来的信息素挥发了部分,同时新的信息素又增加了一些
    				pheromone[i][j] = pheromone[i][j] * (1.0 - rho) + deltaPheromone[i][j];
    			}
    		}
    	}
    
  7. 多次迭代后可以得到最优路径,存放在 b e s t T o u r bestTour bestTour 中,最短路径长度存放于 b e s t L e n g t h bestLength bestLength

    	// 运行蚁群算法
    	public void run() {
    		initPheromone();
    		// 迭代次数
    		for (int i = 0; i < maxGen; i++) {
    			updatePheromone();
    		}
    		System.out.println("迭代"+maxGen+"次后,最优的路径长度:" + bestLength);
    		System.out.println("最优路径为:");
    		for (int i = 0; i < cityNum; i++) {
    			System.out.print(bestTour[i] + " ");
    		}
    	}
    
  8. 样例测试

    public class AntColonyOptimizationTest {
        public static void main(String[] args) {
            // 城市数目
            int cityNum = 4;
            // 蚂蚁数目
            int antNum = 20;
            // 迭代次数
            int maxGen = 100;
            // 信息素重要程度因子
            double alpha = 1.0;
            // 距离的重要程度因子
            double beta = 5.0;
            // 信息素挥发因子
            double rho = 0.5;
            // 信息素增加强度系数
            double q = 1;
    
            // 初始化距离矩阵【无向图】
            double[][] distance= {{0,58,87,64},{58,0,53,49},{87,53,0,88},{64,49,88,0}};
            System.out.println("距离矩阵distance为:");
            for (double[] ds : distance) {
    			System.out.println(Arrays.toString(ds));
    		}
            AntColonyOptimization aco=new AntColonyOptimization(distance, antNum, maxGen, alpha, beta, rho, q);
            aco.run();
        }
    }
    
    image-20230513181850250

4. 完整代码

4.1 AntColonyOptimization类

package aca;

import java.util.Random;
public class AntColonyOptimization {
	 // 距离矩阵
	private double[][] distance;
	// 信息素矩阵
	private double[][] pheromone; 
	// 城市数量
	private int cityNum; 
	// 蚂蚁数量
	private int antNum; 
	// 最大迭代次数
	private int maxGen; 
	// 信息素因子
	private double alpha; 
	// 距离重要程度因子
	private double beta; 
	// 信息素挥发因子
	private double rho; 
	// 信息素增加强度系数
	private double q; 
	// 最优路径
	private int[] bestTour; 
	// 最优路径长度
	private double bestLength; 

	public AntColonyOptimization(double[][] distance, int antNum, int maxGen, double alpha, double beta, double rho,
			double q) {
		this.distance = distance;
		this.cityNum = distance.length;
		this.antNum = antNum;
		this.maxGen = maxGen;
		this.alpha = alpha;
		this.beta = beta;
		this.rho = rho;
		this.q = q;
		this.pheromone = new double[cityNum][cityNum];
		this.bestTour = new int[cityNum];
		this.bestLength = Double.MAX_VALUE;
	}

	// 初始化信息素矩阵
	private void initPheromone() {
		// 信息素初始值
		double initPheromone = 1.0 / (cityNum * distance[0][1]);
		for (int i = 0; i < cityNum; i++) {
			for (int j = 0; j < cityNum; j++) {
				// 各条路径的信息素浓度
				pheromone[i][j] = initPheromone;
			}
		}
	}

	/**
	 * 
	 * @param pheromone 信息素矩阵
	 * @return 蚂蚁依次访问城市的顺序
	 */
	// 蚂蚁移动
	private int[] antMove(double[][] pheromone) {
		// 存储依次到达的城市序号
		int[] tour = new int[cityNum];
		boolean[] visited = new boolean[cityNum];
		Random random = new Random();
		// 生成一个不大于 cityNum 的伪随机整数
		int start = random.nextInt(cityNum);
		// 从 start 城市出发,此城市标记为已访问
		visited[start] = true;
		// 第一个访问 start 城市
		tour[0] = start;
		for (int i = 1; i < cityNum; i++) {
			// 当前蚂蚁所在的城市
			int curCity = tour[i - 1];
			// p 存放概率公式的分子部分
			double[] p = new double[cityNum];
			// sum 存放概率公式的分母部分
			double sum = 0.0;
			for (int j = 0; j < cityNum; j++) {
				// 城市 j 没有被访问过
				if (!visited[j]) {
					p[j] = Math.pow(pheromone[curCity][j], alpha) * Math.pow(1.0 / distance[curCity][j], beta);
					sum += p[j];
				}
			}
			// 用 [0,1] 范围的伪随机数代替轮盘针转到的位置
			double rand = random.nextDouble()  ;
			double tmp = 0.0;
			int nextCity = 0;
			for (int j = 0; j < cityNum; j++) {
				if (!visited[j]) {
					// 每个概率 p[j]/sum 代表轮盘上的一块区域
					// tmp 累积值代表指针到的位置
					tmp += p[j]/sum;
					// 超过 rand ,说明来到了这个区间,即当前城市就是目标城市
					if (tmp >= rand) {
						nextCity = j;
						break;
					}
				}
			}
			// 标记城市为已访问
			visited[nextCity] = true;
			// 记录蚂蚁的下个城市目的地
			tour[i] = nextCity;
		}
		return tour;
	}

	// 更新信息素矩阵
	private void updatePheromone() {
		// 蚂蚁在经过路径上留下的信息增量
		double[][] deltaPheromone = new double[cityNum][cityNum];
		for (int i = 0; i < antNum; i++) {
			// 单只蚂蚁根据当前信息素矩阵而得到的访问顺序
			int[] tour = antMove(pheromone);
			// 当前蚂蚁访问的长度
			double tourLength = getTourLength(tour);
			// 说明可以优化
			if (tourLength < bestLength) {
				// 更新最短路径
				bestLength = tourLength;
				// 由于有了最短路径,因此存储的访问路径也需要更新
				System.arraycopy(tour, 0, bestTour, 0, cityNum);
			}
			for (int j = 0; j < cityNum - 1; j++) {
				int city1 = tour[j];
				int city2 = tour[j + 1];
				// 信息素增量Δτ,只要有蚂蚁经过就会变多
				deltaPheromone[city1][city2] += q / tourLength;
				deltaPheromone[city2][city1] = deltaPheromone[city1][city2];
			}
			deltaPheromone[tour[cityNum - 1]][tour[0]] += q / tourLength;
			deltaPheromone[tour[0]][tour[cityNum - 1]] = deltaPheromone[tour[cityNum - 1]][tour[0]];
		}
		for (int i = 0; i < cityNum; i++) {
			for (int j = 0; j < cityNum; j++) {
				// 原来的信息素挥发了部分,同时新的信息素又增加了一些
				pheromone[i][j] = pheromone[i][j] * (1.0 - rho) + deltaPheromone[i][j];
			}
		}
	}

	/**
	 * 
	 * @param tour 蚂蚁访问顺序
	 * @return 当前访问顺序对应的路径长度
	 */
	// 获取路径长度
	private double getTourLength(int[] tour) {
		double tourLength = 0.0;
		for (int i = 0; i < cityNum - 1; i++) {
			// 将路径中城市间的距离相加
			tourLength += distance[tour[i]][tour[i + 1]];
		}
		// 最后还要返回起点
		tourLength += distance[tour[cityNum - 1]][tour[0]];
		return tourLength;
	}

	// 运行蚁群算法
	public void run() {
		initPheromone();
		// 迭代次数
		for (int i = 0; i < maxGen; i++) {
			updatePheromone();
		}
		System.out.println("迭代"+maxGen+"次后,最优的路径长度:" + bestLength);
		System.out.print("最优路径为:");
		for (int i = 0; i < cityNum; i++) {
			System.out.print(bestTour[i] + " ");
		}
	}
}

4.2 AntColonyOptimizationTest类

package aca;
import java.util.Arrays;

import aca.AntColonyOptimization;
public class AntColonyOptimizationTest {
    public static void main(String[] args) {
        // 城市数目
        int cityNum = 4;
        // 蚂蚁数目
        int antNum = 20;
        // 迭代次数
        int maxGen = 100;
        // 信息素重要程度因子
        double alpha = 1.0;
        // 距离的重要程度因子
        double beta = 5.0;
        // 信息素挥发因子
        double rho = 0.5;
        // 信息素增加强度系数
        double q = 1;

        // 初始化距离矩阵【无向图】
        double[][] distance= {{0,58,87,64},{58,0,53,49},{87,53,0,88},{64,49,88,0}};
//        double[][] distance = new double[cityNum][cityNum];
//        for (int i = 0; i < cityNum; i++) {
//            for (int j = i; j < cityNum; j++) {
//                if (i == j) {
//                    distance[i][j] = 0.0;
//                } else {
//                    double d = Math.random() * 100;
//                    // 城市 i 到城市 j 的距离
//                    distance[i][j] = d;
//                    distance[j][i] = d;
//                }
//            }
//        }
        System.out.println("距离矩阵distance为:");
        for (double[] ds : distance) {
			System.out.println(Arrays.toString(ds));
		}
        AntColonyOptimization aco=new AntColonyOptimization(distance, antNum, maxGen, alpha, beta, rho, q);
        aco.run();
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【软件开发】Memcached(理论篇)

Memcached&#xff08;理论篇&#xff09; 1.Memcached 简介 Memcached 是一个开源的&#xff0c;支持高性能&#xff0c;高并发的分布式内存缓存系统&#xff0c;由 C 语言编写&#xff0c;总共 2000 多行代码。从软件名称上看&#xff0c;前 3 个字符 Mem 就是内存的意思&am…

港科夜闻|香港科大与香港科大(广州)管理层联席会议顺利召开

关注并星标 每周阅读港科夜闻 建立新视野 开启新思维 1、香港科大与香港科大(广州)管理层联席会议顺利召开。这是自内地和香港全面恢复通关以来&#xff0c;两校的高级管理团队首次举行线下的联席会议&#xff0c;面对面交流、讨论有关两校协同发展的重要议题。两校持续深入推进…

「——全部文章专栏汇总——」

欢迎来到我的博客 天喜Studio 在这里&#xff0c;我会分享我在 c语言、操作系统、计算机网络等方面的学习和经验&#xff0c;希望能对读者有所帮助。以下是我写的所有专栏 如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 如有疑问欢迎大家指正讨论…

SQL注入(一)联合查询 报错注入

目录 1.sql注入漏洞是什么 2.联合查询&#xff1a; 2.1注入思想 2.2 了解information_schema 数据库及表 3.可替代information_schema的表 3.1 sys库中重要的表 4. 无列名注入 利用 join-using 注列名。 4. 报错注入 4.1 常用函数&#xff1a;updatexml、extractvalue…

C/C++每日一练(20230517) 排序问题、查找小值、寻找峰值

目录 1. 排序问题 &#x1f31f; 2. 查找小值 &#x1f31f; 3. 寻找峰值 &#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日一练 专栏 1. 排序问题 输入10个数&#…

【Linux学习笔记】设备驱动模型详解——总线、设备、驱动和类

学习内容 设备驱动模型视频讲解 简介 设备驱动是计算机系统中的重要组成部分&#xff0c;它们允许操作系统与硬件交互。设备驱动模型是一种通用的抽象框架&#xff0c;用于描述操作系统如何管理硬件设备。这里我们将介绍设备驱动模型中的四个关键概念&#xff1a;总线、设备…

条款1:理解模板类型推导

现代C中被广泛应用的auto是建立在模板类型推导的基础上的。而当模板类型推导规则应用于auto环境时&#xff0c;有时不如应用于模板中那么直观。由于这个原因&#xff0c;真正理解auto基于的模板类型推导的方方面面非常重要。 在c中声明一个模板函数的伪代码基本如下&#xff1…

六、IDEAJ同一个服务启动多台服务器的方法

目录 1、打开启动类配置窗口--->选择Edit Configurations进入配置窗口 2、从左侧Springboot应用选择需要启动的多台服务器&#xff08;服务只要启动一次就会在此窗口有显示&#xff09;--->勾选Allow parallel run菜单&#xff08;默认不勾选&#xff0c;则只能启动一台…

Springboot +Flowable,会签、或签简单使用(一)

一.简介 **会签&#xff1a;**在一个流程中的某一个 Task 上&#xff0c;这个 Task 需要多个用户审批&#xff0c;当多个用户全部审批通过&#xff0c;或者多个用户中的某几个用户审批通过&#xff0c;就算通过。 例如&#xff1a;之前的请假流程&#xff0c;假设这个请假流程…

板材激光切割机切割穿孔时注意的几个问题

激光切割设备广泛应用于钣金、五金制品、钢结构、汽车配件、广告、工艺品等行业&#xff0c;成为加工行业不可缺少的环节。在厚板加工中穿孔时间占很大比重&#xff0c;随着加工板材越来越厚&#xff0c;板材激光切割机切割穿孔也会相应地增加难度。 激光切割机两种常见的穿孔方…

Linux环境下编程遇到“fatal error:stdio.h:没有那个文件或目录”错误解决办法

我是荔园微风&#xff0c;作为一名在IT界整整25年的老兵&#xff0c;今天总结一下linux环境下如何解决一个常见的问题&#xff0c;也就是“fatal error:stdio.h:没有那个文件或目录”错误。 不少初学者在linux环境下用gcc编译C语言时&#xff0c;经常会遇到这个问题。 比如当…

涂鸦智能生活App SDK:全量级灵活定制,让你的App更具差异化

之前一期&#xff0c;我们介绍过涂鸦 OEM App 开发方案&#xff08;点击查看往期介绍&#xff09;&#xff0c;它集品牌 UI 自定义、服务、运营、商城营销于一体&#xff0c;无需代码&#xff0c;开发者点选拖拽即可快速配置想要的常用功能&#xff0c;最快 10 分钟即可完成一款…

一文打尽目标检测NMS(1): 精度提升篇

文章来自于&#xff1a;曲終人不散丶知乎&#xff0c; 连接&#xff1a;https://zhuanlan.zhihu.com/p/151914931&#xff0c; 本文仅用于学术分享&#xff0c;如有侵权&#xff0c;前联系后台做删文处理。 众所周知&#xff0c;非极大值抑制NMS是目标检测常用的后处理算法&…

测试的分类(按测试对象、是否查看代码、开发阶段、实施组织...... + 思维导图总结)

目录 一、测试的分类 1. 按测试对象划分 2. 按照是否查看代码划分 3. 按照开发阶段划分 4. 按照测试实施组织划分 5. 按照是否运行划分 6. 按照是否手工划分 7. 按照地域划分 二、总结 一、测试的分类 1. 按测试对象划分 &#xff08;1&#xff09;界面测试 简称UI测…

自学黑客(网络安全)如何入门?收藏这篇就够了

前言&#xff1a; 趁着今天下班&#xff0c;我花了几个小时整理了下&#xff0c;非常不易&#xff0c;希望大家可以点赞收藏支持一波&#xff0c;谢谢。 我的经历&#xff1a; 我19年毕业&#xff0c;大学专业是物联网工程&#xff0c;我相信很多人在象牙塔里都很迷茫&#x…

从零开始写一个Vue3+Element Plus的后台管理系统

写在开始之前 接触Vue3也有一年的时间了&#xff0c;除了刚开始用Vue3做了一个小小的项目&#xff0c;其后一直没有机会在项目中真正使用Vue3&#xff0c;反而一直维护Vue2的老项目。作为一个有追求&#xff08;wuliao&#xff09;的前端&#xff0c;那就自己开一个git仓库练手…

基于AT89C51单片机的电子琴设计与仿真

点击链接获取Keil源码与Project Backups仿真图&#xff1a; https://download.csdn.net/download/qq_64505944/87765092?spm1001.2014.3001.5503 源码获取 运用单片机&#xff0c;将音乐的大部分音符与相应按键相匹配&#xff0c;让音乐爱好者利用单片机也可以进行演奏。 基…

前端架构师-week6- ejs源码讲解

ejs 源码详解——彻底搞懂模版动态渲染原理 ejs 执行流程 源码实现思路非常有趣&#xff0c;实现难度实现流程不如前几章源码复杂。但是思路完全不同&#xff0c;用了一套新的思路来实现 ejs 模版渲染。重要的是通过这种思路开阔自己的眼界。ejs 的思路在 vue 中也有用到。 核…

VMware安装Ubuntu系统

VMware安装Ubuntu系统 1.首先选择文件&#xff0c;点击新建虚拟机 2.选择自定义&#xff0c;点击下一步 3.点击下一步 4.选择稍后安装操作系统&#xff0c;点击下一步 5.选择Linus操作系统&#xff0c;版本选择Ubuntu64位&#xff0c;点击下一位 6.自己看图 7. 这里根据自…

Leetcode50. Pow(x, n)

Every day a Leetcode 题目来源&#xff1a;50. Pow(x, n) 解法1&#xff1a;递归 代码&#xff1a; /** lc appleetcode.cn id50 langcpp** [50] Pow(x, n)*/// lc codestart class Solution { public:double myPow(double x, int n){if (n 0)return 1.0;if (n < 0)re…