游戏AI的创造思路-技术基础-遗传算法

遗传算法,选对了遗传算子,那就是优秀的继承者,选错了,那就是传说在祸害遗千年~~~~~

目录

1. 定义

2. 发展历史

3. 遗传算法的基本原理和流程

3.1. 基本原理

3.1.1.基本原理

3.1.2. 算法流程

3.1.3. 关键要素

3.2. 函数和方法

3.2.1. 适应度函数(Fitness Function)

3.2.2. 选择函数(Selection Function)

3.2.3. 交叉函数(Crossover Function)

3.2.4. 变异函数(Mutation Function)

3.2.5. 辅助函数

3.2.6. Python实现示例(概念性)

3.3. Python实现代码

4. 遗传算法的优缺点

5. 在游戏中的应用实例

5.1. 概述

5.2. 举个例子

5.2.1. 猜数字游戏

5.2.2. 遗传算法设计

5.2.3. Python 实现

6. 对比退火算法

6.1. 基本原理

6.2. 操作方式

6.3. 应用场景

6.4. 优缺点比较

6.5. 对比结果


1. 定义

遗传算法(Genetic Algorithms, GA)是一种模拟自然选择和遗传学机制的搜索算法。

它通过模拟生物进化过程中的选择、交叉(杂交)和变异等操作来求解优化问题。

2. 发展历史

遗传算法的概念起源于20世纪60年代,由美国密歇根大学的John Holland教授及其学生首次提出。

经过几十年的发展,遗传算法已经被广泛应用于各种优化和搜索问题中。

3. 遗传算法的基本原理和流程

3.1. 基本原理

3.1.1.基本原理

遗传算法是一种基于自然选择和群体遗传机理的搜索算法,它模拟了自然选择和自然遗传过程中的繁殖、杂交和突变现象。在遗传算法中,问题的每一个可能解都被编码成一个“染色体”,即个体,若干个个体构成了群体(所有可能解)。遗传算法通过迭代的方式,逐步优化群体中的个体,最终找到问题的最优解或近似最优解。

3.1.2. 算法流程

遗传算法的基本流程通常包括以下几个步骤:

  1. 初始化:随机生成一定数量的个体作为初始种群。
  2. 个体评价:计算种群中每个个体的适应度值。适应度函数是根据问题的目标函数设计的,用于评估个体的优劣。
  3. 选择运算:根据个体的适应度值,选择一部分优秀个体作为父代,用于产生下一代种群。选择操作体现了“适者生存”的原理。
  4. 交叉运算:将选中的父代个体进行交叉操作,生成新的个体。交叉操作模拟了生物繁殖过程中的基因重组现象,是遗传算法产生新个体的主要手段。
  5. 变异运算:以一定的概率对种群中的个体进行变异操作,改变其某些基因的值。变异操作模拟了生物进化过程中的基因突变现象,增加了种群的多样性,有助于算法跳出局部最优解。
  6. 终止条件判断:判断算法是否满足终止条件(如达到最大进化代数、找到满足精度的解等)。如果满足终止条件,则算法结束;否则,返回步骤2继续迭代。

3.1.3. 关键要素

  1. 编码:将问题的解空间映射到遗传算法的搜索空间,即对问题的解进行编码。常用的编码方式有二进制编码、实数编码等。
  2. 适应度函数:用于评估个体的适应度值,是遗传算法进行个体选择的依据。适应度函数的设计应根据问题的目标函数来确定。
  3. 遗传算子:包括选择算子、交叉算子和变异算子。这些算子模拟了生物进化过程中的选择、交叉和变异现象,是遗传算法实现优化搜索的关键。

3.2. 函数和方法

在遗传算法中,并没有特定的“公式”来直接解决问题,而是依赖于一系列的操作和函数来迭代优化解集。以下是遗传算法中常用的函数和这些函数的基本描述:

3.2.1. 适应度函数(Fitness Function)

  • 定义:适应度函数是遗传算法中最重要的函数之一,用于评估每个个体的适应度表现。它通常基于个体的目标函数值或其他特定的性能指标来计算个体的适应度得分。
  • 作用:适应度函数决定了哪些个体更有可能被选中进行繁殖,从而影响算法的搜索方向和速度。

3.2.2. 选择函数(Selection Function)

  • 定义:选择函数用于根据适应度得分排名,选择一些个体作为繁殖下一代的“父代”。
  • 常用方法:轮盘赌选择、锦标赛选择、最佳选择等。这些方法基于个体的适应度得分,以不同的概率选择个体。

3.2.3. 交叉函数(Crossover Function)

  • 定义:交叉函数用于将父代的染色体段进行随机交叉,产生新的个体组合,从而丰富种群的性状。
  • 常用方法:单点交叉、多点交叉、均匀交叉等。这些方法在父代染色体上随机选择交叉点,并交换交叉点两侧的部分基因。

3.2.4. 变异函数(Mutation Function)

  • 定义:变异函数用于随机改变个体的染色体上的某些位点,以提高搜索空间的探索性和多样性。
  • 常用方法:基本变异、非一致性变异、可逆变异等。这些方法以一定的概率随机改变个体的某些基因值。

3.2.5. 辅助函数

除了上述核心函数外,遗传算法还可能涉及一些辅助函数,如种群初始化函数、终止条件判定函数等。这些函数用于初始化种群、设置算法的运行参数和判断算法的终止条件等。

3.2.6. Python实现示例(概念性)

虽然无法直接给出遗传算法的完整Python实现代码(因为这取决于具体问题的编码和适应度函数的设计),但可以提供一些概念性的代码片段来说明这些函数是如何在遗传算法中工作的。

# 假设fitness_function是已经定义的适应度函数  
# population是种群,个体以某种编码形式表示(如二进制串)  
  
def selection(population, fitnesses):  
    # 根据适应度选择个体,这里以轮盘赌选择为例  
    # ...(实现轮盘赌选择的代码)  
    return selected_individuals  
  
def crossover(parent1, parent2):  
    # 对两个父代个体进行交叉操作,生成子代  
    # ...(实现交叉操作的代码,如单点交叉)  
    return child1, child2  
  
def mutation(individual, mutation_rate):  
    # 对个体进行变异操作  
    # ...(实现变异操作的代码)  
    return mutated_individual  
  
# 遗传算法主循环(概念性)  
for generation in range(num_generations):  
    fitnesses = [fitness_function(ind) for ind in population]  
    selected = selection(population, fitnesses)  
    offspring = []  
    for i in range(0, len(selected), 2):  
        parent1, parent2 = selected[i], selected[i+1]  
        child1, child2 = crossover(parent1, parent2)  
        offspring.append(mutation(child1, mutation_rate))  
        offspring.append(mutation(child2, mutation_rate))  
    # 可能还需要一些额外的逻辑来处理种群大小不变的情况  
    # ...  
    population = offspring  # 或者基于某种策略合并父代和子代

 上述代码是概念性的,并没有直接运行的能力。在实际应用中,您需要根据具体问题来定义适应度函数、编码方案、选择、交叉和变异等操作的具体实现。

3.3. Python实现代码

以下是一个简单的遗传算法实现,用于优化一个简单的函数:

import numpy as np  
  
# 假设的游戏适应度函数  
def game_fitness(individual):  
    # 个体是一个二进制串,适应度是其中1的个数  
    return np.sum(individual),  
  
# 选择操作:轮盘赌选择  
def selection(population, fitnesses, num_parents):  
    fitnesses = np.array(fitnesses)  
    total_fitness = np.sum(fitnesses)  
    probabilities = fitnesses / total_fitness  
    indices = np.random.choice(np.arange(len(population)), size=num_parents, replace=False, p=probabilities)  
    return population[indices]  
  
# 交叉操作:单点交叉  
def crossover(parents, num_children):  
    children = []  
    for _ in range(num_children // 2):  
        parent1, parent2 = np.random.choice(parents, 2, replace=False)  
        cross_point = np.random.randint(len(parent1))  
        child1 = np.concatenate([parent1[:cross_point], parent2[cross_point:]])  
        child2 = np.concatenate([parent2[:cross_point], parent1[cross_point:]])  
        children.extend([child1, child2])  
    return np.array(children)  
  
# 变异操作  
def mutation(individual, mutation_rate=0.01):  
    for i in range(len(individual)):  
        if np.random.rand() < mutation_rate:  
            individual[i] = 1 - individual[i]  # 翻转基因  
    return individual  
  
# 遗传算法主函数  
def genetic_algorithm(num_generations, population_size, num_parents, num_children, mutation_rate):  
    population = np.random.randint(0, 2, (population_size, 10))  # 假设个体长度为10  
    for generation in range(num_generations):  
        fitnesses = np.array([game_fitness(individual)[0] for individual in population])  
        parents = selection(population, fitnesses, num_parents)  
        children = crossover(parents, num_children)  
        for i in range(len(children)):  
            children[i] = mutation(children[i], mutation_rate)  
        population = np.concatenate([parents, children])[:population_size]  
        print(f"Generation {generation}: Best Fitness = {np.max(fitnesses)}")  
    return population[np.argmax(fitnesses)]  
  
# 运行遗传算法  
best_individual = genetic_algorithm(num_generations=100, population_size=100, num_parents=20, num_children=80, mutation_rate=0.01)  
print("Best Individual:", best_individual)

在这个例子中,我们定义了一个简单的游戏适应度函数,它计算个体中1的个数作为适应度。

然后,我们实现了选择、交叉和变异操作,并在遗传算法主函数中迭代地应用这些操作来优化种群。

最后,我们输出了最优个体和其对应的适应度。

4. 遗传算法的优缺点

优点

  • 全局搜索能力强:通过交叉和变异操作,遗传算法能够在整个解空间内进行搜索,避免陷入局部最优解。
  • 并行性好:遗传算法采用种群的方式组织搜索,可以同时处理多个解,提高了搜索效率。
  • 鲁棒性强:遗传算法对问题的依赖性小,只需要根据问题的目标函数设计适应度函数即可,无需对问题进行过多的数学处理。
  • 易于实现:遗传算法的流程简单,易于编程实现,且不需要复杂的数学推导和证明。
  • 通用性强:可以应用于多种类型的问题,可以处理离散和连续变量。

缺点

  • 编程实现比较复杂。
  • 容易过早收敛到局部最优解。
  • 参数(如交叉率、变异率)的选择对算法性能有很大影响。

5. 在游戏中的应用实例

5.1. 概述

遗传算法在游戏AI中常用于生成内容、策略优化和自动游戏测试等。

例如,可以使用遗传算法来优化游戏中的NPC行为策略,使其更加智能和逼真。

通过定义适应度函数来评估不同策略的效果,然后使用遗传算法来搜索最优策略。

5.2. 举个例子

遗传算法在游戏AI中的应用通常涉及优化游戏策略或行为决策。以下是一个简单的例子,我们将使用遗传算法来训练一个AI,使其在玩一个简单的猜数字游戏时能够更有效地猜测数字。

5.2.1. 猜数字游戏

游戏规则如下:

  • 游戏有一个目标数字,范围是0到100。
  • AI每次猜测一个数字,游戏会告诉AI它的猜测是太高、太低还是正确。
  • AI的目标是尽可能少地猜测次数内找到目标数字。

5.2.2. 遗传算法设计

  1. 个体表示:每个个体是一个包含100个基因的二进制串,每个基因代表一个数字(0-99),如果某个基因位为1,则表示AI会猜测这个数字。

  2. 适应度函数:适应度函数计算个体在猜数字游戏中的表现,即猜测次数越少,适应度越高。

  3. 选择:使用轮盘赌选择方法。

  4. 交叉:使用单点交叉。

  5. 变异:随机翻转某个基因位。

5.2.3. Python 实现

 

import numpy as np  
  
# 参数设置  
POP_SIZE = 100        # 种群大小  
GENES = 100           # 基因数量(代表0-99的数字)  
MAX_GUESSES = 20      # 最大猜测次数  
MUTATION_RATE = 0.01  # 变异率  
NUM_GENERATIONS = 50  # 进化代数  
TARGET = 42           # 目标数字  
  
# 适应度函数  
def fitness(individual):  
    guesses = 0  
    for i in range(GENES):  
        if individual[i] == 1:  
            guesses += 1  
            if i == TARGET:  
                return 1 / guesses  # 猜测次数越少,适应度越高  
    return 0  # 没有猜对则适应度为0  
  
# 选择函数  
def select(population, fitnesses):  
    total_fitness = sum(fitnesses)  
    pick = np.random.rand() * total_fitness  
    current = 0  
    for i, fitness in enumerate(fitnesses):  
        current += fitness  
        if current > pick:  
            return population[i]  
  
# 交叉函数  
def crossover(parent1, parent2):  
    crossover_point = np.random.randint(0, GENES)  
    child1 = parent1[:crossover_point] + parent2[crossover_point:]  
    child2 = parent2[:crossover_point] + parent1[crossover_point:]  
    return child1, child2  
  
# 变异函数  
def mutation(individual):  
    for i in range(GENES):  
        if np.random.rand() < MUTATION_RATE:  
            individual[i] = 1 - individual[i]  
    return individual  
  
# 初始化种群  
population = [[np.random.randint(0, 2) for _ in range(GENES)] for _ in range(POP_SIZE)]  
  
# 遗传算法主循环  
for generation in range(NUM_GENERATIONS):  
    fitnesses = [fitness(ind) for ind in population]  
    new_population = []  
    for _ in range(POP_SIZE // 2):  # 生成新种群,因为每次交叉产生两个后代  
        parent1 = select(population, fitnesses)  
        parent2 = select(population, fitnesses)  
        child1, child2 = crossover(parent1, parent2)  
        new_population.append(mutation(child1))  
        new_population.append(mutation(child2))  
    population = new_population  
      
    # 输出当前代的最佳适应度和对应的猜测次数  
    best_fitness = max(fitnesses)  
    best_guesses = int(1 / best_fitness) if best_fitness > 0 else MAX_GUESSES + 1  
    print(f"Generation {generation}: Best Fitness = {best_fitness}, Best Guesses = {best_guesses}")  
  
# 输出最终的最佳个体和猜测次数  
best_individual = max(population, key=fitness)  
best_guesses = sum(best_individual)  
print(f"Best Individual: {best_individual}")  
print(f"Best Guesses: {best_guesses}")

这段代码实现了一个简单的遗传算法来训练AI玩猜数字游戏。

每一代中,算法都会评估种群中每个个体的适应度,选择适应度高的个体进行交叉和变异,生成新的种群。

最终,算法会输出一个最佳个体,它代表了AI在猜数字游戏中的最优策略。


6. 对比退火算法

 遗传算法(Genetic Algorithm, GA)和模拟退火算法(Simulated Annealing, SA)是两种常用的优化算法,它们在原理、操作方式及应用场景上存在一定的区别。以下是两者的详细比较:

6.1. 基本原理

模拟退火算法

  • 模拟退火算法是一种基于物理退火过程的随机搜索和优化算法。它模拟了固体退火过程中温度逐渐降低,系统内部能量趋于最小的过程,通过接受一定概率的劣解来跳出局部最优解,从而找到全局最优解或接近全局最优的解。
  • 模拟退火算法的主要操作包括初始化温度、生成新解、计算能量差、计算接受概率以及接受或拒绝新解,并逐步降低温度直至收敛。

6.2. 操作方式

模拟退火算法

  • 单个体搜索:模拟退火算法通常以一个解作为当前状态进行搜索。
  • 随机性:模拟退火算法通过引入随机性来接受一定概率的劣解,从而跳出局部最优解。
  • 温度控制:模拟退火算法通过逐步降低温度来控制搜索过程,温度越高接受劣解的概率越大,温度越低接受劣解的概率越小。

6.3. 应用场景

遗传算法

  • 遗传算法适用于处理传统搜索方法难以解决的复杂和非线性问题,如函数优化、自动控制、图像识别、机器学习等。
  • 遗传算法具有广泛的应用范围,不受问题领域的限制,只需根据问题的目标函数设计适应度函数即可。

模拟退火算法

  • 模拟退火算法同样适用于解决组合优化问题,如旅行商问题、排课问题等。
  • 模拟退火算法通过引入随机性和温度参数来克服传统优化算法容易陷入局部最小值的问题,从而找到全局最优解或接近全局最优的解。

6.4. 优缺点比较

遗传算法的优点

  • 全局搜索能力强,不易陷入局部最优解。
  • 并行性好,搜索效率高。
  • 对问题领域无限制,适用范围广泛。

遗传算法的缺点

  • 编程实现较复杂,需要对问题进行编码和解码。
  • 算法参数(如交叉率、变异率)的选择对解的品质影响较大,且目前大部分依赖于经验。
  • 在实际应用中容易产生早熟收敛的问题。

模拟退火算法的优点

  • 能够以随机搜索技术从概率的意义上找出目标函数的全局最小点。
  • 具有摆脱局部最优解的能力。

模拟退火算法的缺点

  • 优化过程长,效率不高。
  • 温度参数的控制对算法性能有较大影响。

6.5. 对比结果

综上所述,遗传算法和模拟退火算法在基本原理、操作方式及应用场景上存在一定的区别。

在实际应用中,可以根据问题的具体特点选择合适的算法进行优化求解。

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

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

相关文章

栈和队列---循环队列

1.循环队列的出现 &#xff08;1&#xff09;上面的这个就是一个普通的数据的入队和出队的过程我们正常情况下去实现这个入队和出队的过程&#xff0c;就是这个数据从这个队尾进入&#xff0c;从队头离开&#xff0c;但是这个加入的时候肯定是没有其他的问题的&#xff0c;直接…

为什么固定尺寸 AdSense 广告依旧会出现并非指定的尺寸广告?

经常在网站上投放谷歌 AdSense广告的站长应该都碰到过&#xff0c;明明投放的是固定尺寸的广告位里旧会出现并非指定尺寸的AdSense 广告&#xff0c;很诡异的感觉。其实这都是因为你的 AdSense 账号广告优化造成的&#xff0c;其中里面就包含了广告尺寸优化&#xff0c;只需要在…

盘点当下智能体应用开发的几种形态

现在多智能体系统开发的关注度越来越高了&#xff0c;不光在开发者的圈子热度很高&#xff0c;很多职场人士&#xff0c;甚至是小白也参与其中&#xff0c;因为现在的门槛越来越低了&#xff0c;尤其是&#xff0c;最近特别火的扣子&#xff08;coze&#xff09;和百度的appbui…

Sequelize 操作 MySQL 数据库

安装 npm install --save sequelize安装驱动程序&#xff1a; npm install --save mysql2连接到数据库 要连接到数据库,必须创建一个 Sequelize 实例. 这可以通过将连接参数分别传递到 Sequelize 构造函数或通过传递一个连接 URI 来完成&#xff1a; const {Sequelize} re…

【Java12】封装

封装&#xff08;Encapsulation&#xff09;是面向对象的三大特征之一&#xff08;另两个是继承和多态&#xff09;&#xff0c;指的是将对象的状态信息隐藏在对象内部&#xff0c;不允许外部程序直接访问对象的内部信息&#xff0c;而是通过该类所提供的方法来实现对内部信息的…

[护网训练]原创应急响应靶机整理集合

前言 目前已经出了很多应急响应靶机了&#xff0c;有意愿的时间&#xff0c;或者正在准备国护的师傅&#xff0c;可以尝试着做一做已知的应急响应靶机。 关于后期&#xff1a; 后期的应急响应会偏向拓扑化&#xff0c;不再是单单一台机器&#xff0c;也会慢慢完善整体制度。…

《昇思25天学习打卡营第14天|onereal》

第14天学习内容如下&#xff1a; Diffusion扩散模型 本文基于Hugging Face&#xff1a;The Annotated Diffusion Model一文翻译迁移而来&#xff0c;同时参考了由浅入深了解Diffusion Model一文。 本教程在Jupyter Notebook上成功运行。如您下载本文档为Python文件&#xff0c…

Zabbix 配置grafana对接

zabbix对接grafana简介 Zabbix与Grafana对接可以实现更加丰富和美观的数据可视化&#xff0c;可以利用Grafana强大的可视化功能来展示Zabbix收集的数据。 Grafana 本身是提供了Zabbix的对接插件&#xff0c;开箱即用&#xff0c;安装好了之后点击 enable 一下就能启用。然后就…

深度学习中的Channel,通道数是什么?

参考文章&#xff1a; 直观理解深度学习的卷积操作&#xff0c;超赞&#xff01;-CSDN博客​​​​​​如何理解卷积神经网络中的通道&#xff08;channel&#xff09;_神经网络通道数-CSDN博客 深度学习-卷积神经网络—卷积操作详细介绍_深度卷积的作用-CSDN博客 正文&…

护网在即,助力安服仔漏洞扫描~

整合了个漏扫系统&#xff0c;安服仔必备~ 使用场景 网前布防&#xff0c;漏洞扫描&#xff0c;资产梳理 使用方法&#xff1a; 启动虚拟机后运行命令&#xff1a; ./StartSystemScript.sh 输入密码attack 启动完成后浏览器打开网站&#xff1a; http://IP:5000 相关账户…

VSCode神仙插件——Codeium (AI编程助手)

1、安装&登录插件 安装过程中会让你登录Codeium账户&#xff0c;可以通过Google账户登录&#xff0c;或者可以注册一个Codeium账户&#xff08;如果没有弹出让你登录账户的界面&#xff0c;可以等安装结束后在右下角找到登录的地方&#xff09; 右下角显示如下图所示&#…

异常组成、作用、处理方式(3种)、异常方法、自定义异常

目录 异常的组成&#xff1a;运行异常与编译异常 两者区别&#xff1a;编译异常用来提醒程序员&#xff0c;运行异常大部分是由于参数传递错误导致 异常作用&#xff1a; 作用1&#xff1a;就是平时的报错&#xff0c;方便我们找到报错的来源 作用2&#xff1a;在方法内部…

计算机网络性能指标概述:速率、带宽、时延等

在计算机网络中&#xff0c;性能指标是衡量网络效率和质量的重要参数。本文将综合三篇关于计算机网络性能指标的文章&#xff0c;详细介绍速率、带宽、吞吐量、时延、时延带宽积、往返时延&#xff08;RTT&#xff09; 和利用率的概念及其在网络中的应用。 1. 速率&#xff08;…

windows系统本地端口被占用的问题

第一步&#xff1a;查找所有运行的端口 按住“WindowsR”组合键&#xff0c;打开命令窗口&#xff0c;输入【cmd】命令&#xff0c;回车。在弹出的窗口中输入 命令【netstat -ano】&#xff0c;再按一下回车键 Win系统端口被占用-查找所有运行的端口 第二步&#xff1a;查看…

整洁架构SOLID-单一职责原则(SRP)

文章目录 定义案例分析重复的假象代码合并解决方案 小结 定义 SRP是SOLID五大设计原则中最容易被误解的一个。也许是名字的原因&#xff0c;很多程序员根据SRP这个名字想当然地认为这个原则就是指&#xff1a;每个模块都应该只做一件事。 在历史上&#xff0c;我们曾经这样描…

CSS技巧:纯CSS实现文字渐变动画效果

文字渐变动画&#xff0c;可以实现的有两种&#xff1a;一种是一行文字整体变化颜色&#xff1b;另一种一行文字依次变化颜色。接下来&#xff0c;我就介绍一下这两种文字渐变的实现过程。 布局代码&#xff1a; <div class"con"><div class"animate…

GPIO配置-PIN_Speed的理解

在使用STM32的GPIO 口配置时&#xff0c;经常会疑惑应该选用什么样的配置模式&#xff0c;本文谈谈对pin_speed的理解。 根据数据手册可得&#xff0c;STM32提供10MHz,2MHz和50MHz三种输出速度的配置&#xff0c;三种配置的应用场景是怎么样的&#xff1f;。 1.为什么要配置引…

力扣双指针算法题目:快乐数

目录 1.题目 2.思路解析 3.代码展示 1.题目 . - 力扣&#xff08;LeetCode&#xff09; 2.思路解析 题目意思是将一个正整数上面的每一位拿出来&#xff0c;然后分别求平方&#xff0c;最后将这些数字的平方求和得到一个数字&#xff0c;如此循环&#xff0c;如果在此循环中…

OpenEarthMap:全球高分辨率土地覆盖制图的基准数据集(开源来下载!!!)

OpenEarthMap由220万段5000张航拍和卫星图像组成&#xff0c;覆盖6大洲44个国家97个地区&#xff0c;在0.25-0.5m的地面采样距离上人工标注8类土地覆盖标签。我们提供8类标注:裸地、牧场、已开发空间、道路、树木、水、农业用地和建筑。类选择与现有的具有亚米GSD的产品和基准数…

C#知识|项目的实施过程及通用三级架构的搭建笔记

哈喽,你好啊,我是雷工! 01 项目需求分析 根据与需求方沟通,分析需求,一般都有需求分析师来进行项目需求收集与分析。 根据需求文档进行项目功能设计。 02 框架的选择 ①小项目可以根据需求选择两层或三层结构。 ②中型大型项目,至少需要三层架构和其他架构的组合。 03 框…