目录

一、遗传算法的概念

1.1、基本概念

1.2、遗传算法的基本过程

1.3、遗传算法的具体步骤

二、遗传算法经典案例

2.1、遗传算法求解函数极大值问题

2.2、遗传算法求解函数极小值问题

2.3、遗传算法求解旅行商问题(TSP)

2.4、遗传算法求解背包问题


一、遗传算法的概念

1.1、基本概念

遗传算法(Genetic Algorithm,GA):模仿生物的遗传进化原理,通过选择、交叉、变异等操作,使得种群个体的适应性不断提高,物竞天择,适者生存。智能算法,全局搜索寻优。

遗传算法的特点如下:

遗传算法的应用领域比较广泛,具体涉及的应用领域如下:

我们看三个概念,即个体,种群,适应度。初始解组成的是个体,多个个体组成的称为种群,适应度是指衡量个体适应高低的,适应度函数一般就是指的目标函数。

下面看一下编码和解码的概念,本文中的编码和解码如下,编码是指将原来的个体改变成能进行选择,交叉,和变异等操作的染色体的过程。而解码就是将染色体转换成为原始的个体,即最优解。编码是遗传算法解决问题的先决条件和关键步骤,常用的编码方式有:二进制编码,浮点数编码等。一般来说,二进制编码比浮点型编码的搜索能力更强,浮点型编码在种群多样性的操作更具多样性。

下面看一下遗传操作的选择,交叉与变异。选择操作就是选择好的进行遗传,交叉就是随机配对,变异就是改变基因到其对应的基因。

1.2、遗传算法的基本过程

遗传算法的基本过程如下:主要包括:收集问题参数并编码成染色体,初始化种族群体,通过选择交叉,变异等遗传操作更新群体,并计算群体的适应度,直到达到迭代次数或者适应度值不发生改变,则终止循环,并对染色体进行解码,即可得到问题的最优解。

下面看一下遗传算法的具体步骤:

首先,我们需要确定决策变量和约束条件,这个通常根据题意获得。然后建立模型,需要确定目标函数,然后需要找出个体,并对个体进行编码,编码是非常重要的,常用的编码方式是二进制编码。

标准的遗传算法基本是采用二进制编码,就是将个体(决策变量)用二进制串连在一起,构成染色体。编码完成,最后留下满足适应度函数的染色体后,还需要进行解码成个体,所以需要确定解码方法。其实就是还原成原来的决策变量。

然后,我们需要确定个体适应度的量化方法,即确定适应度函数,通过适应度函数去求解最优的决策变量,一般比较常用的就是直接将目标函数作为适应度函数,当然也有其它方法去确定适应度函数。

有了适应度函数后,我们就可以确定选择,交叉和变异的方式,个体选择常用的方法是按比例适应度分配,个体的适应度在总适应度的比例越大,即被选取的概率越大,遗产因子就会在种群中逐渐扩大。

计算出每个个体的选择概率后,我们就使用轮盘赌法进行选择,其实就是概率越大,轮盘所占比例越大,被选中的可能越大。

遗传的相关操作除了选择之外,还有交叉和变异,我们下面看一下交叉操作,交叉其实就是序列的交换,交叉率不宜过大,也不宜过小,一般取0.4-0.9。

下面看一下变异操作,对于二进制编码的变异,就是将1变成0,或者将0变成1,也应该选取合适的变异率,变异率过大和过小都不好。

1.3、遗传算法的具体步骤

遗传算法的具体步骤如下:首先对个体编码成染色体,初始化群体并计算解码后的适应值,然后进行遗传操作形成下一代群体, 判断是否满足要求,不满足则继续迭代,直到满足要求或者达到迭代次数。

二、遗传算法经典案例

2.1、遗传算法求解函数极大值问题

用标准遗传算法求函数f(x)=x+10sin(5x)+7cos(4x) 的最大值,其中 x 的取值范围为[0,10]。这是一个有多个局部极值的函数。

下面是使用遗传算法求解该函数模型最大值的具体步骤:

(1)初始化种群数目为 NP=50,染色体的编码方式选择二级制编码,二进制编码长度为 L=20,遗传算法的最大进化迭代数为 G=100,交叉概率 取Pc=0.8,变异概率取Pm=0.1。

(2)产生初始种群,将二进制编码转换成十进制,计算个体适应度值,并进行归一化;采用基于轮盘赌的选择操作、基于概率的交叉和变异操作,产生新的种群,并把历代的最优个体保留在新种群中,进行下一步遗传操作。

(3)判断是否满足终止条件:若满足,则结束搜索过程,输出优化值;若不满足,则继续进行迭代优化。

优化结束后,其适应度进化曲线如下图所示,最优个体 x= 7.8569,函数 f(x)的最大值为 24.8554。这个值不是固定的,每次执行都会有些许波动。

遗传算法的具体流程图如下:

完整的matlab代码如下,注释很全:

%遗传算法求函数极值
clear
clc
NP = 50; %种群数量
L = 20; %二进制位串长度
Pc = 0.8; %交叉率
Pm = 0.1; %变异率
G = 100; %最大遗传迭代次数
Xs = 10; %个体最小值
Xx = 0; %个体最大值
f = randi([0,1],NP,L); %随机获得初始种群
%遗传算法
for k = 1:Gfor i = 1:NPU = f(i,:); %遍历每个种群m = 0;for j = 1:L %遍历种群的每个二进制位m = U(j)*2^(j-1)+m ; %将二进制转换成十进制endx(i) = Xx+m*(Xs-Xx)/(2^L-1); %对染色体进行解码得到个体Fit(i) = func1(x(i)); %通过目标函数计算个体的适应度endmaxFit = max(Fit); %适应度最大值minFit = min(Fit); %适应度最小值rr = find(Fit==maxFit); %找到适应度最大值的的位置fBest = f(rr(1,1),:); %最适应的种群xBest = x(rr(1,1)); %最适应的染色体,即个体Fit = (Fit-minFit)/(maxFit-minFit); %归一化适应度值%基于轮盘赌法的选择操作sum_Fit = sum(Fit) ; %对适应度进行求和fitvalue = Fit./sum_Fit; %求每个适应度被选中的概率fitvalue = cumsum(fitvalue) ; %得到累加后的概率ms = sort(rand(NP,1)) ; %随机生成概率值,并由小到大排序fiti = 1;newi = 1;while newi <= NPif ms(newi) < fitvalue(fiti) %如果随机生成的概率值小于被选中的概率nf(newi,:) = f(fiti,:); %找到选择出来的染色体newi = newi+1;else %不满足条件,继寻找合适的染色体fiti = fiti+1;endend%交叉操作,就是二进制对应0和1的互换for i = 1:2:NP  %遍历种群p = rand ; %随机产生的一个概率if p > Pc %要保证随机产生的概率大于交叉率q = rand(1,L);for j = 1:Lif q(j)==1 %只要随机产生的位置等于1,就进行交叉%将被选择的二级制进行交换,即交叉temp = nf(i+1,j);nf(i+1,j) = nf(i,j);nf(i,j) = temp;endendendend%变异操作,就是二进制取反操作i = 1;while i <= round(NP*Pm)h = randi([1,NP],1,1); %随机选取一个需要变异的染色体for j = 1:round(L*Pm)g = randi([1,L],1,1); %随机选取需要变异的基因数nf(h,g) =~ nf(h,g); %取反变异endi = i+1;endf = nf; %更新种群f(1,:) = fBest; %保留最优个体在新种群中trace(k) = maxFit; %保存最优的适应度
end
disp('最优个体如下:') ;
disp(xBest) ;
disp('函数最大值:') ;
disp(func1(xBest)) ;
figure
plot(trace)
xlabel('迭代次数')
ylabel('目标函数值')
title('适应度进化曲线')

适应度函数即目标函数如下:

function result = func1(x)
fit = x+10*sin(5*x)+7*cos(4*x);
result = fit;
end

迭代过程的适应度进行如下,最终的值在24.85左右,即函数的最值为24.85左右。

2.2、遗传算法求解函数极小值问题

(1)初始化种群数目为 NP=100,染色体的编码方式选择二级制编码,二进制编码长度为 L=10,遗传算法的最大进化迭代数为 G=1000,交叉概率 取Pc=0.8,变异概率取Pm=0.1。

(2)产生初始种群,将二进制编码转换成十进制,计算个体适应度值;采用基于轮盘赌的选择操作、基于概率的交叉和变异操作,产生新的种群,并把历代的最优个体保留在新种群中,进行下一步遗传操作。

(3)判断是否满足终止条件:若满足,则结束搜索过程,输出优化值;若不满足,则继续进行迭代优化。

%遗传算法求函数极值
clear
clc
NP = 100; %种群数量
L = 10; %二进制位串长度
Pc = 0.8; %交叉率
Pm = 0.1; %变异率
G = 1000; %最大遗传迭代次数
Xs = 20; %个体上限
Xx = -20; %个体下线
f = randi([0,1],NP,L); %随机获得初始种群
%遗传算法
for k = 1:Gfor i = 1:NPU = f(i,:); %遍历每个种群m = 0;for j = 1:L %遍历种群的每个二进制位m = U(j)*2^(j-1)+m ; %将二进制转换成十进制endx(i) = Xx+m*(Xs-Xx)/(2^L-1); %对染色体进行解码得到个体Fit(i) = -func2(x(i)); %通过目标函数计算个体的适应度endmaxFit = max(Fit); %适应度最大值%minFit = min(Fit); %适应度最小值rr = find(Fit==maxFit); %找到适应度最小值的的位置fBest = f(rr(1,1),:); %最适应的种群xBest = x(rr(1,1)); %最适应的染色体,即个体% Fit = (Fit-minFit)/(maxFit-minFit); %归一化适应度值%基于轮盘赌法的选择操作sum_Fit = sum(Fit) ; %对适应度进行求和fitvalue = Fit./sum_Fit; %求每个适应度被选中的概率fitvalue = cumsum(fitvalue);  %得到累加后的概率ms = sort(rand(NP,1)) ; %随机生成概率值,并由小到大排序fiti = 1;newi = 1;while newi <= NPif ms(newi) < fitvalue(fiti) %如果随机生成的概率值小于被选中的概率nf(newi,:) = f(fiti,:); %找到选择出来的染色体newi = newi+1;else %不满足条件,继寻找合适的染色体fiti = fiti+1;endend%交叉操作,就是二进制对应0和1的互换for i = 1:2:NP  %遍历种群p = rand ; %随机产生的一个概率if p > Pc %要保证随机产生的概率大于交叉率q = rand(1,L);for j = 1:Lif q(j)==1 %只要随机产生的位置等于1,就进行交叉%将被选择的二级制进行交换,即交叉temp = nf(i+1,j);nf(i+1,j) = nf(i,j);nf(i,j) = temp;endendendend%变异操作,就是二进制取反操作i = 1;while i <= round(NP*Pm)h = randi([1,NP],1,1); %随机选取一个需要变异的染色体for j = 1:round(L*Pm)g = randi([1,L],1,1); %随机选取需要变异的基因数nf(h,g) =~ nf(h,g); %取反变异endi = i+1;endf = nf; %更新种群f(1,:) = fBest; %保留最优个体在新种群中trace(k) = -maxFit; %保存最大的适应度
end
disp('最优个体如下:') ;
disp(xBest) ;
disp('函数最小值:') ;
disp(func2(xBest)) ;
figure
plot(trace)
xlabel('迭代次数')
ylabel('目标函数值')
title('适应度进化曲线')

适应度函数如下:

function result=func2(x)
summ=sum(x.^2);
result=summ;
end

运行结果如下:

2.3、遗传算法求解旅行商问题(TSP)

[旅行商问题](TSP)。假设有一个旅行商人要拜访全国31个省会城市,他需要选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市。对路径选择的要求是:所选路径的路程为所有路径之中的最小值。全国31个省会城市的坐标为[1304 2312;3639 1315;4177 2244;37121399;3488 1535;3326 1556;3238 1229;4196 1004;4312 790;4386 570;3007 1970;2562 1756;2788 1491;2381 1676;1332695;3715 1678;3918 2179;4061 2370;3780 2212;3676 2578;4029 2838;4263 2931;3429 1908;3507 2367;3394 2643;34393201;2935 3240;3140 3550;2545 2357;2778 2826;2370 2975]。

(1)初始化种群数目为NP=200,最大遗传迭代数目为G=1000,染色体基因个数为N=31,即城市数目。

(2)产生初始种群,计算个体适应度值,即路径长度;采用基于概率的方式选择进行操作的个体;对选中的成对个体,随机交叉所选中的成对城市坐标,以确保交叉后路径每个城市只到访一次;对选中的单个个体,随机交换其一对城市坐标作为变异操作,产生新的种群,进行下一次遗传操作。

(3)判断是否满足终止条件:若满足,则结束搜索过程,输出优化值;若不满足,则继续进行迭代优化。

Matlab代码如下:

%遗传算法解决TSP问题
clear
close all;
clc;
C=[1304 2312;3639 1315;4177 2244;3712 1399;3488 1535;3326 1556;...3238 1229;4196 1044;4312  790;4386  570;3007 1970;2562 1756;...2788 1491;2381 1676;1332  695;3715 1678;3918 2179;4061 2370;...3780 2212;3676 2578;4029 2838;4263 2931;3429 1908;3507 2376;...3394 2643;3439 3201;2935 3240;3140 3550;2545 2357;2778 2826;...2370 2975];%省会城市坐标
N=size(C,1);%C的行数,即城市数目
D=zeros(N);%N*N的零矩阵,初始化任意两个城市距离间隔矩阵
%求任意两个城市距离间隔矩阵
for i=1:Nfor j=1:ND(i,j)=((C(i,1)-C(j,1))^2+(C(i,2)-C(j,2))^2)^0.5;end
end
NP=200;%种群数目
G=1000;%最大遗传迭代数
f=zeros(NP,N);%NP*N的零矩阵,用于存储种群
F=[];%种群更新中间存储
for i=1:NPf(i,:)=randperm(N);%1~N之间的随机数,随机生成初始种群
end
R=f(1,:);%用于存储最优种群
len=zeros(NP,1);%用于存储路径长度
fitness=zeros(NP,1);%用于存储归一化适应值
gen=0;
%遗传算法循环,小于最大遗传迭代次数,则继续更新下一代
while gen<G%计算路径长度for i=1:NPlen(i,1)=D(f(i,N),f(i,1));for j=1:(N-1)len(i,1)=len(i,1)+D(f(i,j),f(i,j+1));endendmaxlen=max(len);%最长路径minlen=min(len);%最短路径%更新最短路径rr=find(len==minlen);R=f(rr(1,1),:);%计算归一化适应值for i=1:length(len)fitness(i,1)=(1-((len(i,1)-minlen)/(maxlen-minlen+0.001)));end%选择操作nn=0;for i=1:NPif fitness(i,1)>=randnn=nn+1;F(nn,:)=f(i,:);endend[aa,bb]=size(F);while aa<NPnnper=randperm(nn);A=F(nnper(1),:);B=F(nnper(2),:);%交叉操作W=ceil(N/10);%交叉点个数p=unidrnd(N-W+1);%随机选择交叉范围,从p到p+Wfor i=1:Wx=find(A==B(p+i-1));y=find(B==A(p+i-1));temp=A(p+i-1);A(p+i-1)=B(p+i-1); B(p+i-1)=temp;temp=A(x); A(x)=B(y); B(y)=temp;end%变异操作p1=floor(1+N*rand());p2=floor(1+N*rand());while p1==p2p1=floor(1+N*rand());p2=floor(1+N*rand());endtmp=A(p1); A(p1)=A(p2); A(p2)=tmp;tmp=B(p1); B(p1)=B(p2); B(p2)=tmp;F=[F;A;B];[aa,bb]=size(F);endif aa>NPF=F(1:NP,:);%保持种群规模为nendf=F;%更新种群f(1,:)=R;%保留每代最优个体clear F;gen=gen+1 ;Rlength(gen)=minlen;
end
figure
for i=1:N-1plot([C(R(i),1),C(R(i+1),1)],[C(R(i),2),C(R(i+1),2)],'bo-');hold on;
end
title(['优化最短距离:',num2str(minlen)]);
figure
plot(Rlength)
xlabel('迭代次数')
ylabel('目标函数值')
title('适应度进化曲线')

运行结果如下:

2.4、遗传算法求解背包问题

[0-1背包问题]。有N件物品和一个容量为V的背包。第i件物品的体积是c(i),价值是w(i)。求解将哪些物品放入背包可使物品的体积总和不超过背包的容量,且价值总和最大。假设物品数量为10,背包的容量为300。每件物品的体积为[95,75,23,73,50,22,6,57,89,98],价值为[89,59,19,43,100,72,44,16,7,64]。

(1)初始化种群数目为Np =50,染色体基因维数为L=10,最大进化代数为G=100。

(2)产生二进制初始种群,其中1表示选择该物品,0表示不选择该物品。取适应度值为选择物品的价值总和,计算个体适应度值,当物品体积总和大于背包容量时,对适应度值进行惩罚计算。

(3)对适应度进行归一化,采用基于轮盘赌的选择操作、基于概率的交叉和变异操作,产生新的种群,并把历代的最优个体保留在新种群中,进行下一步遗传操作。

(4)判断是否满足终止条件:若满足,则结束搜索过程,输出优化值;若不满足,则继续进行迭代优化。

matlab代码如下:

%遗传算法解决0-1背包问题
clear
close all;
clc;
NP = 50;%种群规模
L = 10;%物品件数
Pc = 0.8;%交叉率
Pm = 0.05;%变异率
G = 100;%最大遗传代数
V = 300;%背包容量
C = [95,75,23,73,50,22,6,57,89,98]; %物品体积
W = [89,59,19,43,100,72,44,16,7,64];%物品价值
afa = 2; %惩罚函数系数
f = randi([0,1],NP,L);%随机获得初始种群
maxFit = 0 ;
%遗传算法循环部分
for k = 1:G%适应度计算,即计算总价值for i = 1:NPFit(i) = func4(f(i,:),C,W,V,afa);endmaxFit = max(Fit);%最大价值minFit = min(Fit);%最小价值rr = find(Fit==maxFit);fBest = f(rr(1,1),:);%历代最优个体Fit = (Fit - minFit)/(maxFit - minFit);%归一化适应度值%基于轮盘赌的选择sum_Fit = sum(Fit); %求适应度和fitvalue = Fit./sum_Fit;%求被选中的概率fitvalue = cumsum(fitvalue);%累加ms = sort(rand(NP,1)); %随机生成的概率,并按照由小到大排序fiti = 1;newi = 1;while newi <= NPif (ms(newi)) < fitvalue(fiti) %被选中的概率大于随机生成的nf(newi,:) = f(fiti,:); %选择复制newi = newi + 1;elsefiti = fiti + 1;endend%基于概率的交叉操作for i = 1:2:NPp = rand;if p > Pcq = randi([0,1],1,L);          for j = 1:Lif q(j)==1;temp = nf(i + 1,j);nf(i + 1,j) = nf(i,j);nf(i,j) = temp;endendendend%基于概率的变异操作for m = 1:NPfor n = 1:Lr = rand(1,1);if r < Pmnf(m,n) = ~nf(m,n);endendendf = nf;f(1,:) = fBest; %保留最优个体在新种群中trace(k) = maxFit; %历代最优适应度
end
disp('最优个体如下:') ;
disp(fBest) ;
disp('最大价值如下:') ;
disp(maxFit) ;
figure
plot(trace)
xlabel('迭代次数')
ylabel('目标函数值')
title('适应度进化曲线')

适应度函数的代码如下:

%适应度函数,求物品价值总和
function result = func4(f,C,W,V,afa)
fit = sum(f.*W); %物品价值总和
TotalSize = sum(f.*C); %物品总体积
if TotalSize <= V %物品总体积小于背包容量fit = fit;
else %物品总体积大于背包容量fit = fit - afa * (TotalSize - V);
end
result = fit;
end

运行结果如下:其实10个物品,1表示选择该物品,0表示不选择该物品,最后的最大总价值为388

备战数学建模37-遗传算法GA(攻坚战1)相关推荐

  1. 备战数学建模(Python)

    备战数学建模(Python) Python之建模规划篇 Python之建模数值逼近篇 Python之建模微分方程篇 由于美国大学生数学建模大赛很快就要开赛了,所以我就打算在这几天内,好好的看看< ...

  2. 数学建模专栏 | 开篇:如何备战数学建模竞赛之 MATLAB 编程

    作 者 简 介 卓金武,MathWorks中国高级工程师,教育业务经理,在数据分析.数据挖掘.机器学习.数学建模.量化投资和优化等科学计算方面有多年工作经验,现主要负责MATLAB校园版业务.曾2次获 ...

  3. 数学建模之遗传算法(含matlab代码)

    目录 前言 遗传算法 简介 算法主体 概括 编码格式 适应度函数 交叉操作 变异操作 各个变量设置 算法总结 问题和代码 问题 数据 matlab程序 结果展示 结语 前言 本文以面向数学建模的同学为 ...

  4. 备战数学建模国赛,快速搞定算法模型!

    全世界只有3.14 % 的人关注了 青少年数学之旅 说到数学建模,大家的第一反应就是国赛.美赛等数学建模比赛,但这只是冰山一角,不过这个反应却也很正常,因为很多小伙伴接触数学建模的契机,大部分还是因为 ...

  5. 备战数学建模9-层次分析法模型

    层次分析法,简称AHP,是建模比赛中最基础的模型之一,其主要用于解决评价类问题,例如:哪中方案更好?哪位运动员或者员工表现得更优秀? 一.层次分析模型建立部分 下面我们看一道引出层次分析得例题,如下所 ...

  6. 备战数学建模40-遗传算法优化bp神经网络(攻坚站4)

    BP神经网络主要用于预测和分类,对于大样本的数据,BP神经网络的预测效果较佳,BP神经网络包括输入层.输出层和隐含层三层,通过划分训练集和测试集可以完成模型的训练和预测,由于其简单的结构,可调整的参数 ...

  7. 备战数学建模19-数学规划问题

    目录 一.线性规划 1-线性规划的概念 2-线性规划的实例与定义 3-线性规划MATLAB与lingo实现 4-可转化为线性规划的问题 5-线性规划问题实战案例 二.整数规划 1-整数规划相关概念 2 ...

  8. 备战数学建模35-时间序列预测模型

    目录 一.时间序列概念与分解模型 1-时间序列数据与基本概念 2-时间序列分解 二.SPSS中七种指数平滑模型 1-七种指数平滑模型简介 2-七种指数平滑模型具体分析 三.ARIMA模型相关的知识点 ...

  9. 备战数学建模1-MATLAB矩阵相关

    目录 一.数值数据 二.常用函数 三.变量及其操作 四.矩阵的基础应用 五.MATLAB基本运算 六.字符串处理 七.特殊矩阵 八.矩阵变换 九.矩阵求值 十.矩阵的特征值与特征向量 十一.稀疏矩阵 ...

最新文章

  1. AI智商评测标准专家研讨会邀请,2018年12月20日北京
  2. mysqlimport
  3. php发表图片文章代码,最新PHP图片上传的一个例子的文章【延伸阅读】
  4. 疯子的算法总结(一) 位运算(快速幂、快速乘)
  5. VS生成Cordova for Android应用之Gradle
  6. java swagger ui 教程_java集成Swagger的步骤详解
  7. 外贸常用术语_外贸常用贸易术语之间,要这么换算……
  8. html5播放倍速,[html5]html5倍速播放功能源代码实例
  9. qudp socket信号不触发_QT下udpsocket一段时间接收不到数据的问题
  10. Java正则表达式语法与示例
  11. 如何把avi转换为mp4?视频转换用嗨格式视频转换器
  12. ROS做端口映射DDNS的N个做法详细教程
  13. java 如何调用类库_Java中怎么调用类库?
  14. mysql3819错误,微软 Office 3819.20006 预览版发布:修复 Excel 导出 PDF 错误等问题
  15. php程序员未来前景,PHP程序员有前景吗?3个角度为你详解!
  16. WinRAR下载官方免费版
  17. GeoServer中使用SLD样式
  18. jieba分词关键词抽取
  19. 笔记本电脑启动无法打开计算机,笔记本电脑开机没反应(详细教程教您怎么解决)...
  20. 【大数据开发必看】可视化BI神器---FineBI

热门文章

  1. 【网络学习笔记】- 什么是IP地址?
  2. 当 TiDB 遇上 Flink:TiDB 高效入湖“新玩法” | TiLaker 团队访谈
  3. 3种常用的文件上传方式
  4. 在数据库中将中文转换为拼音或者汉字首字母 转
  5. AtCoder Beginner Contest 217 D - Cutting Woods(set + 二分查找)
  6. 关于neo4j导入Protégé导出的owl文件遇到的问题
  7. IoU计算与读取Xml
  8. OpenStack主要功能和作用
  9. 计算机毕业设计选题参考【系统、论文】
  10. mac电脑如何从远程连接的服务器上下载文件