利用改进的遗传算法(种群隔离与个体迁移)mpi并行解决tsp问题

news2024/11/26 2:40:54

关于tsp问题的概述以及如何使用遗传算法进行求解已经在上一篇文章中说明了:遗传算法解决TSP问题.

但是,作为一种演化算法,遗传算法还存在着许多问题,比如早熟的情况,很容易在算法前期就已经收敛了,大量的个体都趋向于一致,种群多样性降低。

这种情况下,就很容易陷入局部最优解。即使扩大迭代次数、种群中的个体数量,也是无济于事的。

一、优化思想

那如何进行优化呢?一种做法就是,进行种群隔离,既然一个种群会出现趋同的情况,那么我们就搞许多个种群。

如果这些种群之间不进行通信,那么是不是可以认为他们就属于两个物种呢?

如果这样的话,那么我们采用少量的个体交换,对于这些种群,他们的物种丰富度是不是就提高了?

所以,这篇文章采用并行计算的方法,我们搞16个种群,这些种群每迭代2000次进行一次个体交换,通过这种多次的交换,来丰富物种多样性。

二、迁移策略

但是,如何在16个种群间进行物种迁移呢?这就要考虑到他们迁移的拓扑结构。

2.1 环形拓扑

环形拓扑主要就是将一个种群中的某个个体迁移到其他种群中去,具体如下:

在这里插入图片描述

2.2 随机拓扑

随机拓扑是任意的,即:一个种群可以向其他任意种群中传递个体。如图:

在这里插入图片描述

看起来就很乱,而且每一次种群间交流完全都是随机的。

三、替换策略

确定了那两个种群进行交换后,如何确定交换种群中的那两个个体呢?

3.1 最优-最差替换

最优发送:即选择种群中的最优个体给其他种群。

最差替换:接收到个体的种群,用别人给的最优的,替换自己最差的个体(适应度值最小)。

3.2 随机-随机替换

随机发送:随机发送一个个体给其他种群。

随机替换:接收到其他种群发送过来的个体后,随机选择一个自己种群的个体替换掉。

四、代码实现

串行改并行,主要分为两个步骤来进行。

第一步就是写好串行的代码;

第二步把串行的代码改成并行的。

最后可能还要稍微改下bug。

4.1 串行代码

#pragma once
// 解决TSP问题的重构算法(主要是tsp0的代码风格太丑陋了,修改成C++风格)
#include<iostream>
#include<fstream>
#include<vector>
#include<time.h>
#include<string>
using namespace std;

class TSP_M {
private:
	int numCity;					 // 城市数量
	vector<vector<double>> cityXY;	 // 城市坐标
	vector<vector<double>> cityDis;  // 城市间距离的邻接矩阵

	int numColony = 100;			 // 种群数量
	vector<vector<int>> colony;		 // 种群
	vector<double> individualAdaptability; // 个体适应度

	int maxGen = 200000;			 // 最大演化代数

	int curGen = 0;					 // 当前演化代数

	double probabilityMutation = 0.02;	// 变异概率

	vector<int> bestIndividual;			// 当前最优个体
	double bestAdaptability;			// 当前最优个体的适应度

	// 用于控制是否记录结果
	bool isRecordResult2File = false;
	bool isRecordResult2Console = true;

public:

	void hello(int myrank) {
		cout << "Hello from " << myrank << endl;
	}

	// 计算种群中每个个体的适应度
	void calculateColonyAdaptability() {
		// 计算每个个体的适应度
		for (int i = 0; i < numColony; i++) {
			double sum = 0;
			for (int j = 0; j < numCity; j++) {
				sum += cityDis[colony[i][j]][colony[i][(j + 1) % numCity]];
			}
			individualAdaptability[i] = sum;
		}
	}

	// 计算个体的适应度
	double calculateIndividualAdaptability(vector<int> path) {
		double sum = 0;
		for (int i = 0; i < numCity; i++) {
			sum += cityDis[path[i]][path[(i + 1) % numCity]];
		}
		return sum;
	}

	// 计算距离矩阵
	void calculateDistanceMatrix() {
		// 初始化距离矩阵
		cityDis.resize(numCity);
		for (int i = 0; i < numCity; i++) {
			cityDis[i].resize(numCity);
		}
		// 计算距离矩阵
		for (int i = 0; i < numCity; i++) {
			for (int j = 0; j < numCity; j++) {
				cityDis[i][j] = sqrt(pow(cityXY[i][0] - cityXY[j][0], 2) + pow(cityXY[i][1] - cityXY[j][1], 2));
			}
		}
	}

	// 初始化 = 读取数据 + 计算数据
	void init(string filePath) {
		// 读取 tsp 文件
		readTspFile(filePath);

		// 初始化距离矩阵、种群等必要数据
		calculateData();
	}

	// 进行迭代计算
	void evolution() {
		for (; curGen < maxGen; curGen++) { // 迭代maxGen次
			for (int i = 0; i < numColony; i++) { // 遍历种群中所有个体

				vector<int> path = colony[i]; // 用于存放变异后的路径

				int posC1 = rand() % numCity; // 随机生成变异点1(在path中的位置)
				int posC2 = rand() % numCity; // 随机生成变异点2(在path中的位置)

				int C1, C2; // 变异点1和变异点2对应的城市编号
				C1 = path[posC1]; // 获取变异点1对应的城市

				int j = rand() % numColony;   // 用于外变异的另一个 与 i个体 不同的个体

				int pos_flag = 0; // 用于标记变异过的点的数量

				double distanceChange = 0; // 用于记录距离变化

				while (true)
				{
					// 以 probabilityMutation (default = 0.02)的概率进行内变异
					if (rand() / 32768.0 < probabilityMutation) {
						posC2 = rand() % numCity;
						while (posC1 == posC2) { // 如果两个变异点相同,则重新生成
							posC2 = rand() % numCity;
						}
						C2 = colony[i][posC2]; // 获取变异点1对应的城市
					}
					else { // 进行外变异(交叉)
						j = rand() % numColony;
						while (i == j) { // 如果两个个体相同,则重新生成
							j = rand() % numColony;
						}
						// 获取个体 j 中 变异点1 对应城市的位置
						int pos = position(colony[j], path[posC1]);
						C2 = colony[j][(pos + 1) % numCity]; // 获取变异点2对应的城市
						posC2 = position(path, C2); // 获取变异点2在个体 i 中的位置(即变异点2对应的城市在个体 i 中的位置
					}

					// 如果两个变异点相邻,continue
					if ((posC1 + 1) % numCity == posC2 || (posC1 - 1 + numCity) % numCity == posC2)break;
					//if (abs(posC1 - posC2) == 1 || abs(posC1 - posC2) == numCity - 1) {
					//	continue;
					//}

					// 否则进行倒位操作
					int C1_left = path[posC1]; // 变异点1左边的城市
					int C1_right = path[(posC1 + 1) % numCity]; // 变异点1右边的城市

					int C2_left = path[posC2]; // 变异点2左边的城市
					int C2_right = path[(posC2 + 1) % numCity]; // 变异点2右边的城市

					// 计算倒位后的路径长度
					distanceChange += cityDis[C1_left][C2_left] + cityDis[C1_right][C2_right]
						- cityDis[C1_left][C1_right] - cityDis[C2_left][C2_right];

					invert(path, posC1, posC2); // 倒位操作

					pos_flag++; // 变异点数量加一
					if (pos_flag >= numCity)break;

					posC1++; // 变异点1的位置加一
					if (posC1 >= numCity) posC1 = 0; // 如果变异点1的位置超过了numCity,则变异点1的位置为0
				}

				// 更新子个体的适应度
				individualAdaptability[numColony + i] = individualAdaptability[i] + distanceChange;
				distanceChange = 0;
				// 记录 产生的 子个体
				for (int j = 0; j < numCity; j++) {
					colony[numColony + i][j] = path[j];
				}
			}

			// 一轮迭代之后进行选择
			selection();

			bestIndividual = colony[0]; // 更新最优个体
			bestAdaptability = individualAdaptability[0]; // 更新最优个体的适应度

			for (int i = 1; i < numColony; i++) {
				if (individualAdaptability[i] < bestAdaptability) {
					bestIndividual = colony[i];
					bestAdaptability = individualAdaptability[i];
				}
			}

			if (isRecordResult2Console) {
				// cout << "第" << curGen << "代的最优个体适应度为:" << bestAdaptability << endl;
				cout << curGen << ":" << bestAdaptability << endl;
			}

			// 每 2000 代将最优个体的适应度写入文件
			if (isRecordResult2File && (curGen + 1) % 2000 == 0) {
				// 创建 outfile.txt 文件
				ofstream outfile("outfile.txt", ios::app);

				outfile << curGen << ":" << bestAdaptability << endl;

				// 关闭文件
				outfile.close();
			}
		}
	}

	// 获取城市在路径中的位置
	int position(vector<int>& path, int city) {
		for (int i = 0; i < numCity; i++) {
			if (path[i] == city) {
				return i;
			}
		}
		return -1;
	}

	void invert(vector<int>& path, int pos1, int pos2) {
		// 如果pos1在pos2的左边,为一段
		if (pos1 < pos2) {
			for (int i = pos1 + 1, j = pos2; i < j; i++, j--) {
				swap(path[i], path[j]);
			}
		}
		// 如果pos1在pos2的右边,为两段
		else {
			// 右边的段 <= 左边的段
			if (numCity - 1 - pos1 <= pos2 + 1) {
				int i, j;
				for (i = pos2 + 1, j = pos1; i <= numCity - 1; i++, j--) {
					swap(path[i], path[j]);
				}
				for (i = 0; i < j; i++, j--) {
					swap(path[i], path[j]);
				}
			}
			// 右边的段 > 左边的段
			else {
				int i, j;
				for (i = pos2 + 1, j = pos1; j >= 0; i++, j--) {
					swap(path[i], path[j]);
				}
				for (j = numCity - 1; i < j; i++, j--) {
					swap(path[i], path[j]);
				}
			}
		}
	}

	// 在父代和子代中进行一个锦标赛选择
	void selection() {
		for (int i = 0; i < numColony; i++) {
			if (individualAdaptability[i] > individualAdaptability[numColony + i]) {
				individualAdaptability[i] = individualAdaptability[numColony + i];
				for (int j = 0; j < numCity; j++) {
					colony[i][j] = colony[numColony + i][j];
				}
			}
		}
	}

	// 读取tsp文件
	bool readTspFile(string filePath) {
		fstream input(filePath, ios::in);
		if (!input) {
			cout << "文件打开失败" << endl;
			return false;
		}

		input >> numCity; // 城市数量
		cout << numCity << endl;
		// 初始化cityXY
		cityXY = vector<vector<double>>(numCity, vector<double>(2));
		// 读取城市坐标
		double x, y;
		for (int i = 0; i < numCity; i++) {
			int tmp;
			input >> tmp >> x >> y;
			cout << tmp << " " << x << " " << y << endl;
			cityXY[i][0] = x;
			cityXY[i][1] = y;
		}

		// 关闭文件
		input.close();
		return true;
	}

	// 根据tsp数据计算城市之间的距离、并随机初始化种群、同时计算适应度
	void calculateData() {
		// 初始化cityDis
		calculateDistanceMatrix();

		// 初始化colony (包括父代和子代)
		colony = vector<vector<int>>(2 * numColony, vector<int>(numCity));
		// 以时间为种子,随机生成种群
		srand((unsigned)time(NULL));

		// 建立一个用于随机生成种群的数组
		vector<int> tmp(numCity);
		for (int i = 0; i < numCity; i++) {
			tmp[i] = i;
		}
		// 随机初始化种群
		for (int i = 0; i < numColony; i++) {
			int numNeedToRand = numCity;	// 当前需要随机的次数
			for (int j = 0; j < numCity; j++) {
				int randIndex = rand() % numNeedToRand; // 随机生成下标
				colony[i][j] = tmp[randIndex]; // 将随机生成的下标对应的值赋给种群
				swap(tmp[randIndex], tmp[numNeedToRand - 1]); // 将已经随机过的下标与最后一个下标交换
				numNeedToRand--; // 需要随机的次数减一
			}
		}

		// 初始化individualAdaptability
		individualAdaptability = vector<double>(2 * numColony); // 后面的numColony个是用于存放子个体的适应度的
		// 计算种群中每个个体的适应度
		calculateColonyAdaptability();
	}

	// 替换掉当前种群中最差的个体
	void replaceWorstIndividual(vector<int> individual) {
		int worstIndex = 0;
		for (int i = 1; i < numColony; i++) {
			if (individualAdaptability[i] > individualAdaptability[worstIndex]) {
				worstIndex = i;
			}
		}
		individualAdaptability[worstIndex] = calculateIndividualAdaptability(individual);
		for (int i = 0; i < numCity; i++) {
			colony[worstIndex][i] = individual[i];
		}
	}

	// 随机替换掉当前种群中的一个个体
	void replaceRandomIndividual(vector<int> individual) {
		int randIndex = rand() % numColony;
		individualAdaptability[randIndex] = calculateIndividualAdaptability(individual);
		for (int i = 0; i < numCity; i++) {
			colony[randIndex][i] = individual[i];
		}
	}

	// get 方法

	// 获取城市数量
	int getNumCity() {
		return numCity;
	}

	// 获取城市坐标
	vector<vector<double>> getCityXY() {
		return cityXY;
	}

	// 获取最优个体
	vector<int> getBestIndividual() {
		int bestIndex = 0;
		for (int i = 1; i < numColony; i++) {
			if (individualAdaptability[i] < individualAdaptability[bestIndex]) {
				bestIndex = i;
			}
		}
		return colony[bestIndex];
	}

	// 随机获取一个个体
	vector<int> getRandomIndividual() {
		int randIndex = rand() % numColony;
		return colony[randIndex];
	}

	// 获取最大迭代次数
	int getMaxGen() {
		return maxGen;
	}

	// 获取当前迭代次数
	int getCurGen() {
		return curGen;
	}

	// set 方法
	void setNumCity(int numCity) {
		this->numCity = numCity;
	}

	void setCityXY(vector<vector<double>> cityXY) {
		this->cityXY = cityXY;
	}

	void setMaxGen(int maxGeneration) {
		this->maxGen = maxGeneration;
	}
};

4.2 并行代码

利用mpi编写的并行代码如下:

#include <stdio.h>
#include <mpi.h>
#include <iostream>
#include <algorithm>
#include "TSP_M.h"
using namespace std;

int main(int argc, char* argv[]){
    // 迁移间隔
    int migrationInterval = 2000;

    // 迁移次数
    int migrationTimes = 100;

    // tsp文件路径
    string tspFilePath = "./pcb442.tsp";

    // 当前进程的编号、进程数量
    int myrank, numProcess;

    MPI_Init(&argc, &argv);
    MPI_Comm_rank(MPI_COMM_WORLD, &myrank);
    MPI_Comm_size(MPI_COMM_WORLD, &numProcess);
    
    // 创建TSP_M对象
    TSP_M tsp;

    if (myrank == 0) { // 地主进程

        // 读取文件
        tsp.readTspFile(tspFilePath);
        // 给其他进程发送 tsp 中的数据
        for (int i = 1; i < numProcess; i++) {
            // 发送城市数量
            int numCity = tsp.getNumCity();
            MPI_Send(&numCity, 1, MPI_INT, i, 0, MPI_COMM_WORLD);
            // 发送城市坐标
            vector<vector<double>> cityXY = tsp.getCityXY();
            for (int j = 0; j < numCity; j++) {
				MPI_Send(&cityXY[j][0], 2, MPI_DOUBLE, i, 0, MPI_COMM_WORLD);
			}
        }
        // 计算距离矩阵(后边计算适应度的时候会用到)
        tsp.calculateDistanceMatrix();
    }
    else {
        // 接收地主进程发送的数据
        // 接收城市数量
        int numCity;
        MPI_Recv(&numCity, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
        // 接收城市坐标
        vector<vector<double>> cityXY(numCity, vector<double>(2));
        for (int j = 0; j < numCity; j++) {
            MPI_Recv(&cityXY[j][0], 2, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
        }

        // 给TSP_M对象赋值
        tsp.setNumCity(numCity);
        tsp.setCityXY(cityXY);
        // 计算距离矩阵等数据
        tsp.calculateData();

        // 设置最大迭代次数为2000
        tsp.setMaxGen(migrationInterval);
        // 进行迭代计算,每2000代通信一次,进行100轮
        for (int i = 0; i < migrationTimes; i++) {
            // 进行2000代迭代
			tsp.evolution();

			// 发送当前最优个体
			vector<int> sendIndividualTo0 = tsp.getBestIndividual();

            // 随机选择一个个体
            // vector<int> sendIndividualTo0 = tsp.getRandomIndividual();
                
            // 向 0 号进程发送当前最优个体
            MPI_Send(&sendIndividualTo0[0], numCity, MPI_INT, 0, 0, MPI_COMM_WORLD);

            // 接收 0 号进程发送的最优个体
            vector<int> recvIndividualFrom0(numCity);
            MPI_Recv(&recvIndividualFrom0[0], numCity, MPI_INT, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);

            // 替换掉当前最差个体
            tsp.replaceWorstIndividual(recvIndividualFrom0);

            // 随机替换一个个体
            // tsp.replaceRandomIndividual(recvIndividualFrom0);

            // 设置最大迭代次数 + 2000
            tsp.setMaxGen(tsp.getMaxGen() + migrationInterval);

		}
    }

    if (myrank == 0) {
        // 用于接收其他进程发送的个体
        vector<vector<int>> recvIndividuals(numProcess - 1, vector<int>(tsp.getNumCity()));
        // 进行100轮分发
        for (int i = 0; i < migrationTimes; i++) {
            for (int j = 1; j < numProcess; j++) {
                MPI_Recv(&recvIndividuals[j - 1][0], tsp.getNumCity(), MPI_INT, j, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
            }

            // 环形拓扑,将收到的个体按顺序传递给下一个进程
            // 1 -> 2 -> 3 -> ... -> numProcess - 1 -> 1
            //for (int j = 1; j < numProcess - 1; j++) {
            //    MPI_Send(&recvIndividuals[j - 1][0], tsp.getNumCity(), MPI_INT, j + 1, 0, MPI_COMM_WORLD);
            //}
            //MPI_Send(&recvIndividuals[numProcess - 2][0], tsp.getNumCity(), MPI_INT, 1, 0, MPI_COMM_WORLD);

            // 随机拓扑,将收到的个体打乱顺序
            vector<int> randomOrder(numProcess - 1);
            for (int j = 0; j < numProcess - 1; j++) {
                randomOrder[j] = j;
            }
            // 采用随机洗牌算法打乱顺序
            random_shuffle(randomOrder.begin(), randomOrder.end());
            // 将打乱顺序后的个体传递给农民进程
            for (int j = 0; j < numProcess - 1; j++) {
                MPI_Send(&recvIndividuals[randomOrder[j]][0], tsp.getNumCity(), MPI_INT, j + 1, 0, MPI_COMM_WORLD);
            }
        }

        // 输出 recvIndividuals 中的最优个体
        double bestFitness = tsp.calculateIndividualAdaptability(recvIndividuals[0]);
        int bestIndividualIndex = 0;
        for (int i = 1; i < numProcess - 1; i++) {
			double fitness = tsp.calculateIndividualAdaptability(recvIndividuals[i]);
            if (fitness < bestFitness) {
				bestFitness = fitness;
				bestIndividualIndex = i;
			}
		}
        cout << "best fitness: " << bestFitness << endl;
		//cout << "best individual: ";
  //      for (int i = 0; i < tsp.getNumCity(); i++) {
  //          cout << bestIndividuals[bestIndividualIndex][i] << " ";
  //      }

        // 将最优个体写入文件
        fstream outfile;
        outfile.open("bestIndividual.txt", ios::out);
        for (int i = 0; i < tsp.getNumCity(); i++) {
			outfile << recvIndividuals[bestIndividualIndex][i] << " ";
		}
        outfile.close();
    }

    MPI_Finalize();

    return 0;
}

五、结果分析与讨论

采用matlab对求解结果进行可视化,代码如下:

clear all;                %清除所有变量
close all;                %清图
clc;                      %清屏

C=load('./file/tsp442.txt');
R_best=load('./file/data/并行-随机拓扑-随机替换.txt');
n = size(C,1);

for i=1:n-1                             % 绘制历代最优路线的图
    plot([ C(R_best(i) + 1,1), C(R_best(i+1) + 1,1)],... % 绘制 n-1 条边
        [C(R_best(i) + 1,2), C(R_best(i+1) + 1,2)],'bo-');
    hold on;
end
plot([C(R_best(n) + 1,1), C(R_best(1) + 1,1)],...        % 连接首尾边
    [C(R_best(n) + 1,2), C(R_best(1) + 1,2)],'ro-');  

% 串行 51509.1

% 并行(100轮、2000次、环形拓扑、最好-最差) 51617.3
% 并行(100轮、2000次、环形拓扑、随机-随机) 51897.5
% 并行(100轮、2000次、随机拓扑、最好-最差) 51677.7
% 并行(100轮、2000次、随机拓扑、随机-随机) 51451

title(['并行(随机拓扑、最优-最差)遗传算法优化最短距离:',num2str(51677.7)]);

5.1 串行求解结果

最优路线长度:51509.1

在这里插入图片描述

5.2 并行求解结果

种群交流方式
拓扑结构环形拓扑随机拓扑
替换策略最优-最差随机-随机

拓扑结构有2种选择,替换策略也有2种选择,因此,一共有4种组合方式。

5.2.1 环形拓扑、最优-最差

最优路线长度:51617.3

在这里插入图片描述

5.2.2 环形拓扑、随机-随机

最优路线长度:51897.5

在这里插入图片描述

5.2.3 随机拓扑、最优-最差

最优路线长度:51677.7

在这里插入图片描述

5.2.4 随机拓扑、随机-随机

最优路线长度:51451

在这里插入图片描述

5.3 结果分析

上述结果是在迭代200000次得到的结果,其中每2000次进行一次种群交流。

由于计算资源有限,没有进行大量实验,但由于迭代次数较大,实验结果应当是较为稳定的。

从有限的实验结果中可以看到,随机拓扑+随机替换策略,找到的解为51451,是更优的。

这也说明,我们通过种群间交流,主要目的就是增加种群的物种多样性,而通过两种随机的策略(随机拓扑+随机替换)相互结合,相比于(环形拓扑+最优替换)增大了种群交流的随机性,较好地利用了多个种群,所以产生了较好地效果。

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

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

相关文章

得帆信息CEO张桐接受21世纪财经深度专访,表示AIGC+低代码将带来生产效率的变革

近日&#xff0c;得帆信息创始人兼CEO张桐接受了21世纪财经深度专访&#xff0c;他表示AIGC低代码的黄金组合&#xff0c;将带来生产效率的变革。 眼下&#xff0c;低代码与AI创新的联接才刚刚开始&#xff0c;也必然会带来生产效率的变革。 在AIGC汹涌的浪潮下&#xff0c;聊…

【LNMT与动静分离】

目录 一、LNMT 1.部署tomcat 1.1 单机部署 1.2 多机部署 2.部署nginx 3.部署Mariadb 4.配置Nginx 二、Nginx高级配置 1.location 2.1 应用场景 2.2 书写位置 2.3 重写条目写法 2.4 返回状态码 2.5 案例 三、动静分离 1.修改nginx配置文件 2.测试 一、LNMT 1.…

四层负载均衡的NAT模型与DR模型推导 | 京东物流技术团队

导读 本文首先讲述四层负载均衡技术的特点&#xff0c;然后通过提问的方式推导出四层负载均衡器的NAT模型和DR模型的工作原理。通过本文可以了解到四层负载均衡的技术特点、NAT模型和DR模型的工作原理、以及NAT模型和DR模型的优缺点。读者可以重点关注NAT模型到DR模型演进的原…

linux字符串处理

目录 1 C 截取字符串,截取两个子串中间的字符串2 获取该字符串后面的字符串用 strstr() 函数查找需要提取的特定字符串&#xff0c;然后通过指针运算获取该字符串后面的字符串用 strtok() 函数分割字符串&#xff0c;找到需要提取的特定字符串后&#xff0c;调用 strtok() 传入…

c语言六子棋(Alpha-Beta剪枝算法)

c语言Alpha-Beta剪枝算法六子棋介绍 Alpha-Beta剪枝算法是一种用于优化博弈树搜索的算法&#xff0c;可以在搜索过程中减少不必要的计算&#xff0c;从而提高搜索效率。该算法常用于博弈游戏&#xff0c;如六子棋。 六子棋是一种类似于五子棋的棋类游戏&#xff0c;在一个六边形…

C#: Json序列化和反序列化,集合为什么多出来一些元素?

如下面的例子&#xff0c;很容易看出问题&#xff1a; 如果类本身的无参构造函数&#xff0c; 就添加了一些元素&#xff0c;那么在序列化&#xff0c;再反序列化&#xff0c;会导致元素增加。 如果要避免&#xff0c;必须添加&#xff1a; new JsonSerializerSettings() { …

目标检测(凑字数,凑字数,为什么标题一定要五个字)

写在最前&#xff1a; 昨天七夕&#xff0c;一个人躲在家里打游戏&#xff0c;不敢出门&#xff0c;怕被喂狗粮。 一个即将步入中年的老男人&#xff0c;弱小&#xff0c;无助&#xff0c;单身&#xff0c;肥胖&#xff0c;贫穷。但是怀揣着一丢丢情怀和梦想&#xff0c;妄图吃…

运算符(个人学习笔记黑马学习)

算数运算符 加减乘除 #include <iostream> using namespace std;int main() {int a1 10;int a2 20;cout << a1 a2 << endl;cout << a1 - a2 << endl;cout << a1 * a2 << endl;cout << a1 / a2 << endl;/*double a3 …

github加速方式

windows系统访问github加速方式 第一步&#xff1a;查询dns地址第二步&#xff1a;host文件添加dns映射第三步&#xff1a;刷新dns 第一步&#xff1a;查询dns地址 查询工具&#xff1a;https://coding.tools/cn/nslookup 依次将以下网址的ip查询出来&#xff1a; github.com …

javaee之黑马乐优商城1

问题1&#xff1a;整体的项目架构与技术选型 技术选型 开发环境 域名测试 如何把项目起来&#xff0c;以及每一个目录结构大概是什么样子 通过webpack去启动了有个项目&#xff0c;这里还是热部署&#xff0c;文件改动&#xff0c;内容就会改动 Dev这个命令会生成一个本地循环…

睿趣科技:抖音开网店卖玩具怎么样

近年来&#xff0c;随着社交媒体平台的飞速发展&#xff0c;抖音作为一款短视频分享应用也迅速崭露头角。而在这个充满创业机遇的时代背景下&#xff0c;许多人开始探索在抖音平台上开设网店&#xff0c;尤其是卖玩具类商品&#xff0c;那么抖音开网店卖玩具究竟怎么样呢? 首先…

Vue2(状态管理Vuex)

目录 一&#xff0c;状态管理Vuex二&#xff0c;state状态三&#xff0c;mutations四&#xff0c;actions五&#xff0c;modules最后 一&#xff0c;状态管理Vuex Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态&#xff0c;并…

Qt应用开发(基础篇)——对话框窗口 QDialog

一、前言 QDialog类继承于QWidget&#xff0c;是Qt基于对话框窗口(消息窗口QMessageBox、颜色选择窗口QColorDialog、文件选择窗口QFileDialog等)的基类。 QDialog窗口是顶级的窗口&#xff0c;一般情况下&#xff0c;用来当做用户短期任务(确认、输入、选择)或者和用户交流(提…

Royal TSX for Mac:苹果电脑远程桌面,轻松管理,完美兼容版

Royal TSX for Mac是一款功能强大、易于使用且安全稳定的远程连接管理软件。无论您是IT专业人士还是普通用户&#xff0c;它都能满足您对远程工作的需求。在一个直观友好的界面下&#xff0c;Royal TSX提供了广泛的远程连接支持&#xff0c;并具备灵活性和扩展性。无论是作为个…

xxl-job学习(一遍文章解决)

前言&#xff1a;学习xxl-job需要有git&#xff0c;springboot的基础&#xff0c;学起来就很简单 xxl-job是一个分布式的任务调度平台&#xff0c;其核心设计目标是&#xff1a;学习简单、开发迅速、轻量级、易扩展&#xff0c;现在已经开放源代码并接入多家公司的线上产品线&a…

【历史上的今天】8 月 28 日:微软创始人控诉苹果谷歌;人工智能医学领域先驱出生

整理 | 王启隆 透过「历史上的今天」&#xff0c;从过去看未来&#xff0c;从现在亦可以改变未来。 今天是 2023 年 8 月 28 日&#xff0c;在 123 年前的今天&#xff0c;百事可乐公司成立&#xff0c;影响了无数人的闲暇生活&#xff0c;其中的一届 CEO 更是在跳槽之后改变了…

计算机视觉与人工智能在医美人脸皮肤诊断方面的应用

一、人脸皮肤诊断方法 近年来&#xff0c;随着计算机技术和人工智能的不断发展&#xff0c;中医领域开始逐渐探索利用这些先进技术来辅助面诊和诊断。在皮肤望诊方面&#xff0c;也出现了一些现代研究&#xff0c;尝试通过图像分析技术和人工智能算法来客观化地获取皮肤相关的…

数据库相关知识2

数据库知识2 关系完整性 数据完整性 指的是数据库中的数据的准确性和可靠性 实体完整性约束&#xff1a; 目的&#xff1a; 在表中至少有一个唯一的 标识&#xff0c;主属性字段中&#xff0c;不为空&#xff0c;不重复 主键约束&#xff1a;唯一 不重复 不为空 primary k…

Java:HashMap、LinkedHashMap、TreeMap集合的底层原理和集合的嵌套

HashMap的底层原理 LinkedHashMap的底层原理 TreeMap集合的底层原理 集合的嵌套