2024年3月

1.根目录概念:

1.1 项目根目录(Project Root)

  • 项目根目录是你在文件系统中为整个项目选择的顶层目录。
  • 它通常包含了项目的所有内容,包括源代码、构建配置文件、文档、测试文件等。
  • 在版本控制系统中(如 Git),项目根目录通常是仓库的根目录。

1.2 内容根目录(Content Root)

  • 在 IntelliJ IDEA 或其他 JetBrains IDE 中,内容根目录指的是一个模块(Module)的文件系统根目录。
  • 它通常包含该模块的源代码、资源文件、测试代码等。
  • 内容根目录是相对于 IntelliJ IDEA 项目而言的,并不等同于文件系统上的项目根目录。
  • 一个项目可以包含多个模块,每个模块可以有自己的内容根目录。

1.3
两者的关系

  • 项目根目录是一个更大的概念,它包含了整个项目的所有内容。
  • 内容根目录是 IntelliJ IDEA 中模块级别的概念,它位于项目根目录之下,并包含模块特定的文件和目录结构。

1.4 示例

假设你有一个名为
MyProject
的项目,该项目包含两个模块:
CoreModule

WebModule
。在本地硬盘上,
MyProject
的根目录可能看起来像这样:

MyProject/ //项目根目录
|-- .idea/
|-- CoreModule/ //模块内容根目录(Content Root)
|   |-- src/
|   |   |-- main/
|   |   |   |-- java/     (CoreModule 的源代码)
|   |   |   |-- resources/ (CoreModule 的资源文件)
|   |-- test/
|   |-- CoreModule.iml
|-- WebModule/  //模块内容根目录(Content Root)
|   |-- src/
|   |   |-- main/
|   |   |   |-- java/     (WebModule 的源代码)
|   |   |   |-- resources/ (WebModule 的资源文件)
|   |-- test/
|   |-- WebModule.iml
|-- MyProject.iml

在这个例子中,
MyProject
是项目根目录,而
CoreModule

WebModule
则分别有自己的内容根目录。每个模块的内容根目录下进一步细分了源代码目录、测试代码目录和资源文件目录,在 Maven 构建工具的项目结构中,这些目录通常会被自动标记为
源根目录

1.5 总结:

通过 IntelliJ IDEA 的项目结构设置,你可以轻松地管理这些目录和它们的依赖关系,从而确保项目的构建和运行符合预期。

2. 路径概念:

2.1 首先,我们回顾一下
目录
概念:

  • 项目根目录(Project Root)
    项目根目录是整个项目在本地硬盘上的顶层目录。它包含了项目所需的所有文件和子目录,如源代码、构建文件、配置文件等。在 IntelliJ IDEA 中,当你打开一个项目时,这个项目根目录就是你在文件系统中选择的目录。
  • 内容根目录(Content Root)
    内容根目录是存储模块文件(如源代码、资源等)的目录。在 IntelliJ IDEA 中,每个模块(Module)可以有自己的 Content Root。Content Root 是模块层次结构中的顶层目录,它包含了该模块的所有相关文件和子目录。
  • 源根目录(Source Root)
    源根目录(Source Root):Source Root
    是 Content Root 下的一个特殊类型
    的目录。在 IntelliJ IDEA 中,一个 Content Root 下可以有多个 Source Root,它们可以有不同的类型,如
    src

    test

    resources
    等,用于区分不同类型的源代码或资源文件。
    在实际的 MyBatis 项目中,源代码(如 Java 类)通常放在
    src/main/java
    目录下,而 MyBatis 的映射文件(如
    .xml
    文件)通常放在
    src/main/resources
    目录下。这两个目录都是源根目录,但它们存放的文件类型不同。在 IntelliJ IDEA 中,你可以通过模块设置来配置这些源根目录。

2.2 接下来,我们讨论
路径
概念:

  • 内容根路径(path from content root)
    :这是从内容根目录到特定文件的路径。例如,如果
    src
    是 Content Root,那么
    src/main/resources/com/itheima/mapper/UserMapper.xml
    就是相对于 Content Root 的路径。
  • 源根路径(path from source root)
    :这是从源根目录到特定文件的路径。对于 MyBatis 的映射文件,它们通常存放在资源目录(如
    src/main/resources
    )下,该目录被标记为 Source Root。因此,
    com/itheima/mapper/UserMapper.xml
    就是相对于该 Source Root 的路径。

2.3 MyBatis 示例

假设你有一个基于 Maven 的 MyBatis 项目,其结构如下:

MyBatisProject/ (项目根目录)
|-- .idea/
|-- pom.xml
|-- src/ (Content Root)
|   |-- main/
|   |   |-- java/ (Source Root for Java sources)
|   |   |   |-- com/
|   |   |       |-- itheima/
|   |   |           |-- mapper/
|   |   |               |-- UserMapper.java
|   |   |-- resources/ (Source Root for resources)
|   |       |-- com/
|   |           |-- itheima/
|   |               |-- mapper/
|   |                   |-- UserMapper.xml
|-- MyBatisProject.iml (项目模块文件)

在这个例子中:

  • 项目根目录

    MyBatisProject/
  • 内容根目录

    src/
    ,它包含了项目的所有源代码和资源文件。
  • 源根目录
    有两个:
    src/main/java/
    用于 Java 源代码,
    src/main/resources/
    用于资源文件(如 MyBatis 映射文件)。
  • 内容根路径
    (对于
    UserMapper.xml
    )是
    src/main/resources/com/itheima/mapper/UserMapper.xml
  • 源根路径
    (对于
    UserMapper.xml
    )是
    com/itheima/mapper/UserMapper.xml
    ,这是从
    src/main/resources/
    这个源根目录开始的相对路径。

在 MyBatis 的配置中,你通常会使用源根路径来引用映射文件。例如,在
mybatis-config.xml
文件中:

<configuration>
    <mappers>
        <mapper resource="com/itheima/mapper/UserMapper.xml"/>
    </mappers>
</configuration>

这里的
resource
属性期望的是相对于类路径(classpath)的资源路径,这通常与源根路径相对应。当 IntelliJ IDEA 构建项目时,它会确保这些源根目录下的文件被包含在类路径中,从而 MyBatis 能够正确地加载映射文件。

2.4 总结:

了解这些路径概念对于在 IntelliJ IDEA 中正确配置和管理 MyBatis 项目是非常重要的。它们帮助开发者清晰地组织项目结构,并确保文件能够被正确地引用和加载。

最近在做蓝牙的宽带语音通话。相对于蓝牙窄带语音,主要变化是把采样率从8k变到16k,以及编解码器从CVSD变成mSBC(modified SBC,改进的SBC)等。蓝牙语音通话相关的HFP(Hand Free Profile)强烈建议在宽带语音通话时要用上语音丢包补偿(packet lost concealment,PLC)算法来保证丢包时的语音质量,也给出了推荐的PLC算法的浮点实现。由于是在ADSP上做,算力有限,浮点实现是不能直接用的,因此要把浮点实现定点化。下面就讲讲我是怎么来做定点化的。

1,  理解算法的浮点实现

算法实现是基于paper “Waveform Substitution Techniques for Recovering Missing Speech Segments in Packet Voice Communications”的。 函数不多,API主要有三个:InitPLC()是初始化,PLC_good_frame()是正常帧处理,PLC_bad_frame()是丢包帧处理。内部函数也有三个:CrossCorrelation()算互相关,PatternMatch()算最匹配的起始位置,AmplitudeMatch()根据最匹配的位置算比例因子(scale factor),这些函数均在处理丢包帧时调用。

2, 结合mSBC实现做一个丢包补偿的demo

先前只有用于蓝牙音乐播放的SBC编解码demo(基于blueZ)。 为了支持mSBC, 对SBC代码做了修改(核心算法不变,对外围代码做了修改以支持mSBC)。现在又要结合PLC做丢包补偿,需要把PLC代码嵌到mSBC解码程序里。做丢包补偿demo的大致步骤如下:

1) 把一段16k采样的语音PCM用mSBC编码程序编码成sbc码流

2) 用mSBC解码程序把sbc码流解码成PCM

3) 在mSBC的解码程序里嵌入PLC代码。假设每20帧丢一帧(即丢包率为5%),用PLC算法去补偿丢的一帧,看补偿效果。补偿后的语音听起来无异样才算OK。这个PLC算法以及实现是HFP里推荐的,理论上不会有问题。如果补偿后声音有异样,一般是API没用好。

3, PLC算法的定点化

做定点化前需要找到做的点。我通过梳理代码发现有三处需要做定点化,下面看看这三处是怎么做定点化的。

1) 余弦表的定点化

代码中有一个浮点的余弦表,如下:

需要变成定点的表。看了表中值的范围,绝对值都是小于1的,因此在定点表示时适合用Q0.15。这样浮点的表就变成了定点的表,如下:

代码中用到余弦表时都是与PCM值相乘。两个short值相乘,要注意溢出,因此乘前要把一个值强制转换成int型。相乘后要做移位操作(右移15位),在移位前使值更接近浮点值,要做四舍五入(加上 1<<14,即加上0.5)处理。示例代码如下:

2) 算互相关时的定点化

代码中算互相关的代码如下:

从代码看出,用的算式如下:

既涉及到求平方根,又涉及到除法。以前做代码优化时知道求平方根可以用牛顿迭代法,于是我就在AMR-WB的参考代码(主要是basic operation的相关代码)里找有没有现成的可以用。不仅找到了,而且还是算平方根的倒数的(Word32 Isqrt(Word32 L_x),输入是一个32位的值,输出是Q0.31的值),这样除法就变成了乘法。我用几个值(比如100)带进函数Isqrt()试试,Q0.31的结果跟浮点值是对得上的。算式也就变成了如下的:

代码中可以看出x2,y2均是32位的,相乘后很可能超出32位,而Isqrt()的输入是32位的,不能直接使用,需要做些变形,变成 m * 2
2n
(m为16位以内的整数,n为正整数)的形式。

这时m
1
* m
2
是一个32位以内的数,就可以用函数Isqrt()了。


用Isqrt()求,

就是就是右移(n
1
+ n
2
)位。结果再乘以num就可求出定点的Cn了。

3) 做除法时的定点化

浮点实现中在算比例因子时用到了除法,代码如下:

看了AMR-WB参考代码,也有除法实现,即Word16 div_s (Word16 var1, Word16 var2)。不过是16位的除法,输出也是一个Q0.15的16位值。使用时还有其他限制条件:被除数和除数要都是正数,且被除数小于等于除数。在浮点代码中被除数和除数均可能是32位的正值,因此不能直接用,需要做些改进,变成 m * 2
n
(m为16位以内的正整数,n为正整数)的形式。

用div_s()求出m
1
/m
2
,2
(n
1
-n
2

就是移位(左移、右移或者不移)。从浮点代码看出比例因子范围在0.75~1.2,要用Q1.14表示,因此要把用div_s()算出来的Q0.15值转换成Q1.14形式。最后再加一个限幅,范围是0.75~1.2,用Q1.14表示就是12288~19661。经过以上处理后就可求出定点的比例因子了。

在定点化的过程中,每一步定点化时都要比较定点的结果和浮点的结果,确保误差在很小的范围内,通常不超过1。做完后还要比较最终的误差,即最后的PCM值的误差。下图给出了浮点实现和定点实现最终的部分PCM值比较:

从图上可以看出,在做丢包补偿时,定点的和浮点的实现部分PCM值有误差,但误差都是在1范围内,这是可以接受的。

mydocker-run-d.png

本文为从零开始写 Docker 系列第八篇,实现类似 docker run -d 的功能,使得容器能够后台运行。


完整代码见:
https://github.com/lixd/mydocker
欢迎 Star

推荐阅读以下文章对 docker 基本实现有一个大致认识:


开发环境如下:

root@mydocker:~# lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 20.04.2 LTS
Release:	20.04
Codename:	focal
root@mydocker:~# uname -r
5.4.0-74-generic


注意:需要使用 root 用户

1. 概述

经过前面的 7 篇文章,我们已经基本实现了一个简单的 docker 了。

不过与 Docker 创建的容器相比,我们还缺少以下功能

  • 1)指定后台运行容器,也就是 detach 功能
  • 2)通过 docker ps 查看目前处于运行中的容器
  • 3)通过docker logs 查看容器的输出
  • 4)通过 docker exec 进入到一个已经创建好了的容器中

后续几篇文章主要就是一一实现这些功能,本文首先实现
mydocker run -d
让容器后台运行。

2. 原理分析

在 Docker 早期版本,所有的容器 init 进程都是从 docker daemon 这个进程 fork 出来的,这也就会导致一个众所周知的问题,如果 docker daemon 挂掉,那么所有的容器都会宕掉,这给升级 docker daemon 带来很大的风险。

子进程的结束和父进程的运行是一个异步的过程,即父进程永远不知道子进程到底什么时候结束。如果创建子进程的父进程退出,那么这个子进程就成了没人管的孩子,俗称孤儿进程。为了避免孤儿进程退出时无法释放所占用的资源而僵死,进程号为 1 的 init 进程就会接受这些孤儿进程。

即:Docker 早期架构中,docker daemon挂掉后,所有容器作为子进程都会被 init 进程托管,实际上还是可以运行的,但是 docker daemon 挂了会导致他维护的一些资源也没了,所以容器实际上是不能正常运行的。

为了解决该问题后来,Docker 使用了 containerd, 负责管理容器的生命周期,包括创建、运行、停止等。同时 containerd 为每个进程都启动了一个 init 进程(图中的
containerd-shim
),
containerd-shim
进程负责接收来自
containerd
的命令,启动容器中的进程,并监控它们的生命周期。

便可以实现即使 daemon 挂掉,容器依然健在的功能了,其结构如下图所示。

docker-engine-arch.png

为了简单起见,我们就按照 Docker 早期架构实现吧。在我们的实现中:

  • 当前运行命令的 mydocker 是主进程
  • 容器是被当前 mydocker 进程 fork 出来的子进程。

这样看来,mydocker 可以看做是图中的 containerd,mydocker 中具体实现 Namespace 隔离,cgroups 资源限制的部分代码则可以看做是 runC或者 libcontainer。

具体实现就是,fork 出子进程后,mydocker 进程直接退出掉。是当 mydocker 进程退出后,容器进程就会被 init 进程接管,这时容器进程还是运行着的。

也算是实现了一个简易版本的后台运行。

3. 实现

首先,需要在 main-command.go 里面添加 -d flag,表示这个容器启动的时候后台在运行:

var runCommand = cli.Command{
    Name: "run",
    Usage: `Create a container with namespace and cgroups limit
          mydocker run -it [command]`,
    Flags: []cli.Flag{
       cli.BoolFlag{
          Name:  "it", // 简单起见,这里把 -i 和 -t 参数合并成一个
          Usage: "enable tty",
       },
       cli.BoolFlag{
          Name:  "d",
          Usage: "detach container",
       },
        // 省略其他代码
    },
    /*
       这里是run命令执行的真正函数。
       1.判断参数是否包含command
       2.获取用户指定的command
       3.调用Run function去准备启动容器:
    */
    Action: func(context *cli.Context) error {
       if len(context.Args()) < 1 {
          return fmt.Errorf("missing container command")
       }

       var cmdArray []string
       for _, arg := range context.Args() {
          cmdArray = append(cmdArray, arg)
       }
       // tty和detach只能同时生效一个
       tty := context.Bool("it")
       detach := context.Bool("d")

       if tty && detach {
          return fmt.Errorf("it and d paramter can not both provided")
       }
       resConf := &subsystems.ResourceConfig{
          MemoryLimit: context.String("mem"),
          CpuSet:      context.String("cpuset"),
          CpuCfsQuota: context.Int("cpu"),
       }
       volume := context.String("v")
       Run(tty, cmdArray, resConf, volume)
       return nil
    },
}

然后调整 Run 方法,只有指定 tty 的时候才执行 parent.Wait。

parent.Wait() 主要是用于父进程等待子进程结束,这在交互式创建容器的步骤里面是没问题的,但是指定了
-d
要后台运行就不能再去等待,创建容器之后,父进程直接退出即可。

func Run(tty bool, comArray []string, res *subsystems.ResourceConfig, volume string) {
	parent, writePipe := container.NewParentProcess(tty, volume)
	if parent == nil {
		log.Errorf("New parent process error")
		return
	}
	if err := parent.Start(); err != nil {
		log.Errorf("Run parent.Start err:%v", err)
		return
	}
	// 创建cgroup manager, 并通过调用set和apply设置资源限制并使限制在容器上生效
	cgroupManager := cgroups.NewCgroupManager("mydocker-cgroup")
	defer cgroupManager.Destroy()
	_ = cgroupManager.Set(res)
	_ = cgroupManager.Apply(parent.Process.Pid, res)

	// 在子进程创建后才能通过pipe来发送参数
	sendInitCommand(comArray, writePipe)
	if tty { // 如果是tty,那么父进程等待,就是前台运行,否则就是跳过,实现后台运行
		_ = parent.Wait()
		container.DeleteWorkSpace("/root/", volume)
	}
}

4. 测试

运行一个 top 命令:

root@mydocker:~/feat-run-d/mydocker# go build .
root@mydocker:~/feat-run-d/mydocker# ./mydocker run -d top
{"level":"info","msg":"createTty false","time":"2024-01-24T16:58:16+08:00"}
{"level":"info","msg":"resConf:\u0026{ 0  }","time":"2024-01-24T16:58:16+08:00"}
{"level":"info","msg":"busybox:/root/busybox busybox.tar:/root/busybox.tar","time":"2024-01-24T16:58:16+08:00"}
{"level":"info","msg":"mount overlayfs: [/usr/bin/mount -t overlay overlay -o lowerdir=/root/busybox,upperdir=/root/upper,workdir=/root/work /root/merged]","time":"2024-01-24T16:58:16+08:00"}
{"level":"info","msg":"command all is top","time":"2024-01-24T16:58:16+08:00"}

可以看到,mydocker 命令直接退出了。

使用 top 作为容器内前台进程。然后在宿主机上执行 ps -ef 看一下 建的容器进程是否存在:

root@mydocker:~/feat-run-d/mydocker# ps -ef|grep -e PPID -e top
UID          PID    PPID  C STIME TTY          TIME CMD
root      166637       1  0 16:5 pts/8    00:00:00 top

可以看到,top 命令的进程正在运行着,它的父进程是 1。

这说因为
mydocker 主进程
退出了,但是 fork 出来的
容器子进程
依然存在,由于父进程消失,它就被 PID为 1 的 init 进程给托管了,由此就实现了 mydocker run -d 命令,即容器的后台运行。

4. 总结

本篇实现的 mydocker run -d 比较简单,就是启动完子进程(容器)后,直接退出父进程,让 init 进程去接管子进程。

不过现在比较大的问题是,虽然容器在后台运行了,但是已经找不到了,因此下一篇需要实现
mydocker ps
命令来查看运行中的容器。


【从零开始写 Docker 系列】
持续更新中,搜索公众号【
探索云原生
】订阅,阅读更多文章。




完整代码见:
https://github.com/lixd/mydocker
欢迎 Star

相关代码见
feat-volume
分支,测试脚本如下:

需要提前在 /root 目录准备好 busybox.tar 文件,具体见第四篇第二节。

# 克隆代码
git clone -b feat-run-d https://github.com/lixd/mydocker.git
cd mydocker
# 拉取依赖并编译
go mod tidy
go build .
# 测试 
./mydocker run top -d

灰狼优化算法(MOGWO)

摘要

  1. 固定大小的外部档案用来保存帕累托优化解
  2. 在多目标搜索空间中,这个档案被用来定义狼群社会等级和捕猎行为
  3. 这个算法在10个多目标测试集进行测试,并与MOEA/D和MOPSO进行对比

引言

  1. 将多个目标集成一个单一的目标

两个缺点:一个均匀分布的权重不能保证生成一组均匀分布的帕累托优化解集;由于不能使用负权重,且所有权重之和必须为常数,该方法无法找帕累托最优前沿的非凸区域。

  1. MOPSO的收敛速度非常快,在多目标优化中容易出现假帕累托最优前沿过早终止的问题

GWO

该算法是模拟灰狼的社会领导关系和捕猎技术,为了模拟灰狼在狩猎过程中的包围行为,除了社会领导外,提出了一下方程:

t表示当前代数,A和C是向量系数,Xp表示猎物位置,X表示一只灰狼的位置,A和C的计算公式:

a是在迭代过程中从2线性减到0,r1和r2是[0,1]中的随机数,alpha、beta、gamma是前三个最优解。

A的随机值大于1或小于-1,保证了狼群和猎物的偏离,C有助于GWO在优化过程中表现出更随机的行为,有利于规避局部最优。当A的绝对值大于1时,狼群偏离猎物,当A的绝对值小于1时,狼群向猎物收敛。

MOGWO

MOGWO比起GWO多了两个新增部分:

  1. 一个档案用于存储所得帕累托最优解集和进行非支配排序
  2. 选择策略,用于选择alpha、beta、gamma作为领导。

MOGWO的伪代码如下:

MOGWO算法的收敛性是有保证的,因为它利用了相同的数学模型来搜索最优解。事实证明,GWO要求搜索智能体在优化的初期突然改变位置,在优化的后期逐渐改变位置。MOGWO算法继承了GWO的所有特征,这意味着搜索智能体以相同的方式探索和开发搜索空间。主要的区别是,MOGWO围绕一组存档个体进行搜索(即使存档没有变化,也可能不同),而GWO只保存和改进三个最好的解。

MOGWO部分源代码如下,需要完整代码请联系我(免费)。

%% 清理空间

clear all

clc

close all

%% MOGWO算法参数

drawing_flag = 1;

% 测试函数及其细节确定

TestProblem='UF1';

nVar=10;

fobj = cec09(TestProblem);

xrange = xboundary(TestProblem, nVar);

lb=xrange(:,1)';

ub=xrange(:,2)';

VarSize=[1 nVar];

% 迭代次数、种群数量、存档数量

GreyWolves_num=100;

MaxIt=200; % Maximum Number of Iterations

Archive_size=100; % Repository Size

% 网格机制的参数

alpha=0.1; % Grid Inflation Parameter

nGrid=10; % Number of Grids per each Dimension

beta=4; % Leader Selection Pressure Parameter

gamma=2;

%% 种群初始化

GreyWolves=CreateEmptyParticle(GreyWolves_num);

for i=1:GreyWolves_num

​ GreyWolves(i).Velocity=0;%灰狼的初始速度为0

​ GreyWolves(i).Position=zeros(1,nVar);%灰狼的初始位置也为0

​ for j=1:nVar

​ GreyWolves(i).Position(1,j)=unifrnd(lb(j),ub(j),1);%灰狼的位置

​ end

​ GreyWolves(i).Cost=fobj(GreyWolves(i).Position')';

​ GreyWolves(i).Best.Position=GreyWolves(i).Position;

​ GreyWolves(i).Best.Cost=GreyWolves(i).Cost;

end

% 确定支配关系

GreyWolves=DetermineDomination(GreyWolves);

% 非支配解存档

Archive=GetNonDominatedParticles(GreyWolves);

% 网格机制

Archive_costs=GetCosts(Archive); % 存档种群的适应度

G=CreateHypercubes(Archive_costs,nGrid,alpha);

for i=1:numel(Archive)

​ [Archive(i).GridIndex Archive(i).GridSubIndex]=GetGridIndex(Archive(i),G);

end

%% 迭代

for it=1:MaxIt

​ a=2-it*((2)/MaxIt);

​ for i=1:GreyWolves_num

​ clear rep2

​ clear rep3

​ % 选头狼

​ % Choose the alpha, beta, and delta grey wolves

​ Delta=SelectLeader(Archive,beta);

​ Beta=SelectLeader(Archive,beta);

​ Alpha=SelectLeader(Archive,beta);

​ % If there are less than three solutions in the least crowded

​ % hypercube, the second least crowded hypercube is also found

​ % to choose other leaders from.

​ if size(Archive,1)>1

​ counter=0;

​ for newi=1:size(Archive,1)

​ if sum(Delta.Position
=Archive(newi).Position)
=0

​ counter=counter+1;

​ rep2(counter,1)=Archive(newi);

​ end

​ end

​ Beta=SelectLeader(rep2,beta);

​ end

​ % This scenario is the same if the second least crowded hypercube

​ % has one solution, so the delta leader should be chosen from the

​ % third least crowded hypercube.

​ if size(Archive,1)>2

​ counter=0;

​ for newi=1:size(rep2,1)

​ if sum(Beta.Position
=rep2(newi).Position)
=0

​ counter=counter+1;

​ rep3(counter,1)=rep2(newi);

​ end

​ end

​ Alpha=SelectLeader(rep3,beta);

​ end

​ % 同GWO一样

​ % Eq.(3.4) in the paper

​ c=2.*rand(1, nVar);

​ % Eq.(3.1) in the paper

​ D=abs(c.*Delta.Position-GreyWolves(i).Position);

​ % Eq.(3.3) in the paper

​ A=2.
a.
rand(1, nVar)-a;

​ % Eq.(3.8) in the paper

​ X1=Delta.Position-A.*abs(D);

​ % Eq.(3.4) in the paper

​ c=2.*rand(1, nVar);

​ % Eq.(3.1) in the paper

​ D=abs(c.*Beta.Position-GreyWolves(i).Position);

​ % Eq.(3.3) in the paper

​ A=2.
a.
rand()-a;

​ % Eq.(3.9) in the paper

​ X2=Beta.Position-A.*abs(D);

​ % Eq.(3.4) in the paper

​ c=2.*rand(1, nVar);

​ % Eq.(3.1) in the paper

​ D=abs(c.*Alpha.Position-GreyWolves(i).Position);

​ % Eq.(3.3) in the paper

​ A=2.
a.
rand()-a;

​ % Eq.(3.10) in the paper

​ X3=Alpha.Position-A.*abs(D);

​ % Eq.(3.11) in the paper

​ GreyWolves(i).Position=(X1+X2+X3)./3;

​ % Boundary checking

​ GreyWolves(i).Position=min(max(GreyWolves(i).Position,lb),ub);

​ GreyWolves(i).Cost=fobj(GreyWolves(i).Position')';

​ end

​ % 支配关系、存档、网格更新

​ GreyWolves=DetermineDomination(GreyWolves);

​ non_dominated_wolves=GetNonDominatedParticles(GreyWolves);

​ Archive=[Archive

​ non_dominated_wolves];

​ Archive=DetermineDomination(Archive);

​ Archive=GetNonDominatedParticles(Archive);

​ for i=1:numel(Archive)

​ [Archive(i).GridIndex Archive(i).GridSubIndex]=GetGridIndex(Archive(i),G);

​ end

​ if numel(Archive)>Archive_size

​ EXTRA=numel(Archive)-Archive_size;

​ Archive=DeleteFromRep(Archive,EXTRA,gamma);

​ Archive_costs=GetCosts(Archive);

​ G=CreateHypercubes(Archive_costs,nGrid,alpha);

​ end

​ disp(['In iteration ' num2str(it) ': Number of solutions in the archive = ' num2str(numel(Archive))]);

​ save results

​ % Results

​ costs=GetCosts(GreyWolves);

​ Archive_costs=GetCosts(Archive);

​ hold off

​ plot(costs(1,:),costs(2,:),'k.');

​ hold on

​ plot(Archive_costs(1,:),Archive_costs(2,:),'rd');

​ legend('Grey wolves','Non-dominated solutions');

​ drawnow

end

之前有关Git,写过一片文章:
Git五个常见问题及解决方法

一、reset命令使用场景

有时候我们提交了一些错误的或者不完善的代码,需要回退到之前的某个稳定的版本,面对这种情况有两种解决方法:

解决方法1
:修改错误内容,再次commit一次

解决方法2
:使用git reset 命令撤销这一次错误的commit

第一种方法比较直接,但会多一次commit记录,同时并不是所有场景都可以这么做。

所以建议使用第二种方法,因为错误的commit没必要保留下来。


二、reset命令

git reset 命令用于
回退版本
,可以指定退回某一次提交的版本。

reset 命令的原理是根据 commitId 来恢复版本。

因为每次提交都会生成一个 commitId,所以说 reset 可以帮你恢复到历史的任何一个版本。

这里的版本和提交是一个意思,一个 commitId 就是一个版本

命令格式如下:

git reset [--soft | --mixed | --hard] [HEAD]

常用命令

# 回退所有内容到上一个版本
git reset HEAD^

# 回退test.txt这个文件的版本到上一个版本
git reset HEAD^ test.txt

# 向前回退到第3个版本
git reset  HEAD~3

# 回退到某个版本51363e6
git reset 51363e6

注意:对于已经 push 的 commit,也可以使用reset命令,不过再次 push 时,由于远程分支和本地分支有差异,需要强制推送 git push -f 来覆盖被 reset 的 commit。

这里非常需要注意,因为
git push -f
是一个非常危险的命令。

如果在 git push -f 之前有其他同事 push 新代码。那么同事提交代码会丢失。

所以这里一定要注意 git push -f 之前要先 git pull 拉下是否有新提交代码。


三、reset参数有哪些?

option 参数的选项有以下几种

git reset --mixed
:此为默认方式,将撤回的代码,存放到工作区。同时会保留本地未提交的内容。

git reset --soft
:回退到某个版本 。将撤回的代码,存放到暂存区。同时会保留本地未提交的内容。

git reset --hard
:彻底回退到某个版本,丢弃将撤回的代码,本地没有commit的修改会被全部擦掉。(
慎用
)

下面对这三种参数分别一一通过示例演示


四、reset --mixed

默认方式,将撤回的代码,存放到
工作区
。同时会保留本地未提交的内容。

在代码提交后,我发现这次提交也就是 commitId 为 7812249,提交错了,需要撤回。

同时这个时候呢,本地又新增修改了一些代码

执行命令

git reset d0eda46

注意
这里的commitId不是
提交代码2
的,而是
提交代码1
的,因为我们想的是回到是1这个版本,别搞错了

可以看出本地的代码和撤回的代码都在,且都在
工作区

我们再来看下提交日志

git log --oneline

可以看到
提交代码2
的提交记录已经没有了。


五、reset --soft

soft和mixed差别并不大,无非就是一个回退到暂存区一个回退到工作区。

我们来演示下,把上面三个文件重新提交一次

$ git commit -a -m  '提交代码2'
[feature ebb89a1] 提交代码2
3 files changed, 5 insertions(+), 2 deletions(-)

然后再来执行reset命令

git reset --soft d0eda46

可以看出撤回的都返回在暂存区。


六、reset --hardt

这个命令和前面差别就非常大了,它不仅会丢弃错误commit提交的代码,而且本地没有commit的修改会被全部擦掉,关键是这种情况本地代码一旦擦掉是没有任何办法在找回来了。

同样我们把上面的代码再次提交

$ git commit -m  '提交代码3'
[feature de970f2] 提交代码3
3 files changed, 5 insertions(+), 2 deletions(-)

然后再来执行reset命令

git reset --hard d0eda46

在看本地

$ git status
On branch feature
nothing to commit, working tree clean

在工作区和暂存区已经没有任何代码了,也就是如果这个时候你本地开发的代码没有提交,那就永远也找不回来了。

那之前提交过的但又撤回的
提交代码3
的代码可以找回吗?

这个是可以的。

我们通过
git log --oneline
是找不回的。

找不到
提交代码3
的记录了。 那怎么办呢?

可以通过 git reflog 命令实现找回

执行命令

git reflog

这里能找到 commit
提交代码3
的记录。那现在要做的就是撤回到当前提交的位置。

执行命令

git reset de970f2

全部回来啦。

注意
切记!!!工作区有未提交的代码时不要用这个命令,因为工作区会回滚,你没有提交的代码就再也找不回了。



声明
: 公众号如需转载该篇文章,发表文章的头部一定要 告知是转至公众号: 后端元宇宙。同时也可以问本人要markdown原稿和原图片。其它情况一律禁止转载!