數學建模--基於遺傳算法求解數獨

求解數獨網上有很多算法,由於最近在學習遺傳算法,所以嘗試通過遺傳算法來求解數獨。
遺傳算法求解數獨步驟如下
1.初始化化種羣
首先需要產生較優的初始種羣,以減少進化代數,如果沒有較優的初始種羣會加大後面運算壓力。爲了得到較優的初始種羣,設置一下規則(1)、每個方格的數字不重複;
(2)、儘可能使填入的數字與所在行或列的數字不重複。
根據以上規則得到一定數量的初始九宮格,然後將每個方格缺的數字按從上到下、從左到右的順序連在一次作爲染色體。
2、交叉
將染色體隨機兩兩組合,隨機取兩個染色體中間相同的位置進行交換,交叉完後,將未交叉的重複元素用另一個染色體的重複的元素交換(因爲該染色體重複的元素就是另一個染色體缺少的元素,元素守恆)。
3、變異
按變異率在種羣中隨機選擇一定數量的個體,隨機產生一個變異節點(一一個九宮格的方格作爲一個節點),將該節點左右翻轉。
4、選擇
將父代、子代、變異代三部分染色體合在一起,計算每個染色體還原到九宮格中行和列重複數字的個數,初始分爲8*(9+9)=144,每重複一次減去一分。選出分數最高一部分作爲下一輪進化的父代。進化到一定程度,出現分數等於144時,退出進化。
matlab代碼實現如下:

clc,clear
tic;
Su=[
0 2 0 0 0 5 0 0 0;
3 5 4 0 8 0 1 0 0;
0 9 1 6 2 0 0 5 0;
0 3 9 7 6 2 0 8 0;
0 7 0 0 9 8 0 0 0;
0 0 2 0 3 1 7 9 4;
2 6 7 0 1 9 8 4 5;
0 0 3 8 5 6 0 1 0;
5 0 0 0 7 4 9 3 6;
];

index1=[1 4 7 1 4 7 1 4 7];
index2=[1 1 1 4 4 4 7 7 7];

SuNNum=zeros(9,9);
SuNNumP=zeros(9,9);
SuNNumL=zeros(9,1);
l=0;
for i=1:9
    SuOne=Su(index1(i):index1(i)+2,index2(i):index2(i)+2);
    v=find(SuOne==0);
    SuNNumP(i,1:length(v))=v';
    t=0;
    for j=1:9
        v=find(SuOne==j);
        if isempty(v)
            t=t+1;
            SuNNum(i,t)=j;
            SuNNumL(i)=t;
        end
    end
end
SuNNum;%按從上到下,從左往右的順序保存每個方格所缺的數字
SuNNumP;%按從上到下,從左往右的順序保存每個方格所缺的數字的位置
SuNNumL;%按從上到下,從左往右的順序保存每個方格所缺的數字的長度
%初始化種羣
xtab=[1 2 3 1 2 3 1 2 3];
ytab=[1 1 1 2 2 2 3 3 3];
xtab1=[0 1 2 0 1 2 0 1 2];
ytab1=[0 0 0 1 1 1 2 2 2];
w=sum(SuNNumL)*6;%種羣大小
SuP=zeros(9,9,w);%w個九宮格
for s=1:w
    SuP(:,:,s)=Su;
    for i=1:9
        pf=1;
        pl=SuNNumL(i);
        rand1=randperm(SuNNumL(i));
        for j=1:SuNNumL(i)
            x1=xtab(SuNNumP(i,j))+3*xtab1(i);
            y1=ytab(SuNNumP(i,j))+3*ytab1(i);
            v1=find(SuP(x1,:,s)==SuNNum(i,rand1(pf)));
            v2=find(SuP(:,y1,s)==SuNNum(i,rand1(pf)));
            %如果在行或列沒有重複的數字則將該數字填入九宮格
            %否則將從後面往前取一個數字填入九宮格
            if isempty(v1) && isempty(v2)
                SuP(x1,y1,s)=SuNNum(i,rand1(pf));
                pf=pf+1;
            else
                SuP(x1,y1,s)=SuNNum(i,rand1(pl));
                pl=pl-1;
            end
        end
    end
end

Ge=zeros(9,9,w);
for s=1:w
    for i=1:9
        for j=1:SuNNumL(i)
            x1=xtab(SuNNumP(i,j))+3*xtab1(i);
            y1=ytab(SuNNumP(i,j))+3*ytab1(i);
            Ge(i,j,s)=SuP(x1,y1,s);
            %將SuP中的空格位置的數字取出
        end
    end
    Ge(:,:,s)=Ge(:,:,s)';
end
Ge(Ge==0)=[];%去除Ge中的零元素
Ge2=zeros(w,sum(SuNNumL));
for s=1:w
    Ge2(s,:)=Ge((s-1)*sum(SuNNumL)+1:s*sum(SuNNumL));
    %將Ge切分爲w個染色體
end
%種羣初始化結束,Ge2保存種羣中的所有染色體
while true
    %交叉
    Ge2Son=Ge2;
    rand2=zeros(2,w);
    rand3=randperm(w);
    rand2(1,:)=1+floor(sum(SuNNumL)*rand(1,w));
    rand2(2,:)=1+floor(sum(SuNNumL)*rand(1,w));
    %調整rand2的大小順序,rand(1,)<=rand(2,)
    for s=1:w
        if rand2(1,s)>rand2(2,s)
            tempc=rand2(1,s);
            rand2(1,s)=rand2(2,s);
            rand2(2,s)=tempc;
        end
    end
    %根據rand3隨機序列、以rand(1,)作爲低位交叉點、以rand(2,)作爲高位交叉點進行兩個個體之間基因交叉
    for s=1:2:w-1
        tempc1=Ge2Son(rand3(s),rand2(1,s):rand2(2,s));
        Ge2Son(rand3(s),rand2(1,s):rand2(2,s))=Ge2Son(rand3(s+1),rand2(1,s):rand2(2,s));
        Ge2Son(rand3(s+1),rand2(1,s):rand2(2,s))=tempc1;
    end

    %去除染色體各節點的重複元素
    SuNNumLS=cumsum(SuNNumL); 
    Lowid=zeros(1,w-1);
    Highid=zeros(1,w-1);
    for s=1:2:w-1
        %找出低位交叉點所在的九宮格
        for i=1:9
            if rand2(1,s)<SuNNumLS(i)
                Lowid(s)=i;
                break;
            elseif i==1
                Lowid(s)=9;
            end
        end
        %找出高位交叉點所在的九宮格
        for i=9:-1:1
            if rand2(2,s)>SuNNumLS(i)
                Highid(s)=i+1;
                break;
            elseif i==1
                Highid(s)=1;
            end
        end
        Lowid(s+1)=Lowid(s);
        Highid(s+1)=Highid(s);
    %獲取交叉點所在節點組別   
    end
    %Lowid(Lowid==0)=[];
    %Highid(Highid==0)=[];
    %tabulate
    Getemp1=zeros(9,9);
    Getemp2=zeros(9,9);
    %%%%%%%%%%%%%%%%%%%%%%%%%%%
    for s=1:2:w
        for i=1:9
            Getemp1(i,1:SuNNumL(i))=Ge2Son(rand3(s),SuNNumLS(i)-SuNNumL(i)+1:SuNNumLS(i));
            Getemp2(i,1:SuNNumL(i))=Ge2Son(rand3(s+1),SuNNumLS(i)-SuNNumL(i)+1:SuNNumLS(i));
            %將染色體以節點爲單位切分
        end
        SL1=tabulate(Getemp1(Lowid(s),:));
        temp= SL1(:,1)~=0;%去除數字爲零結果
        SL1=SL1(temp,:);
        SL1num=SL1(SL1(:,2)==2,1);%找出出現兩次的數字

        SL2=tabulate(Getemp2(Lowid(s),:));
        temp= SL2(:,1)~=0;
        SL2=SL2(temp,:);
        SL2num=SL2(SL2(:,2)==2,1);
        %如果有出現兩次的數字,則將父代與母代的出現的數字交換
        if (~isempty(SL1num)) && (length(SL1num)==length(SL2num))
            for i=1:length(SL1num)
                Getemp1(Lowid(s),find(Getemp1(Lowid(s),:)==SL1num(i),1))=SL2num(i);
                Getemp2(Lowid(s),find(Getemp2(Lowid(s),:)==SL2num(i),1))=SL1num(i);
            end
        end

        SH1=tabulate(Getemp1(Highid(s),:));
        temp= SH1(:,1)~=0;%去除數字爲零結果
        SH1=SH1(temp,:);
        SH1num=SH1(SH1(:,2)==2,1);%找出出現兩次的數字

        SH2=tabulate(Getemp2(Highid(s),:));
        temp=SH2(:,1)~=0;
        SH2=SH2(temp,:);
        SH2num=SH2(SH2(:,2)==2,1);    
        %如果有出現兩次的數字,則將父代與母代的出現的數字交換
        if ~isempty(SH1num) && (length(SH1num)==length(SH2num))
            for i=1:length(SH1num)
                Getemp1(Highid(s),find(Getemp1(Highid(s),:)==SH1num(i),1))=SH2num(i);
                Getemp2(Highid(s),find(Getemp2(Highid(s),:)==SH2num(i),1))=SH1num(i);
            end
        end
        for i=1:9
            Ge2Son(rand3(s),SuNNumLS(i)-SuNNumL(i)+1:SuNNumLS(i))=Getemp1(i,1:SuNNumL(i));
            Ge2Son(rand3(s+1),SuNNumLS(i)-SuNNumL(i)+1:SuNNumLS(i))=Getemp2(i,1:SuNNumL(i));
            %將去除重複元素後的節點恢復到染色體
        end
    end
    %交叉結束,子代爲Ge2Son

    %變異
    p=0.1;%變異率 
    Munum=floor(p*w);%變異個體數
    rand4=randperm(w);%變異個體的隨機序列
    Ge2Mu=zeros(Munum,sum(SuNNumL));
    Getemp=zeros(9,9);
    for i=1:Munum
        Ge2Mu(i,:)=Ge2(rand4(i),:);
        for j=1:9
            Getemp(j,1:SuNNumL(j))=Ge2Mu(i,SuNNumLS(j)-SuNNumL(j)+1:SuNNumLS(j));
        end     
        rand5=1+floor(9*rand(1,1));%變異節點隨機序號
        Getemp(rand5,1:SuNNumL(rand5))=Getemp(rand5,SuNNumL(rand5):-1:1);%將節點翻轉
        for j=1:9
            Ge2Mu(i,SuNNumLS(j)-SuNNumL(j)+1:SuNNumLS(j))=Getemp(j,1:SuNNumL(j));
        end 
        %將切分的染色體還原
    end
    %變異結束,GeMu爲變異得到的個體
    G(1:w,1:SuNNumLS(9))=Ge2;
    G(w+1:2*w,1:SuNNumLS(9))=Ge2Son;
    G(2*w+1:2*w+Munum,1:SuNNumLS(9))=Ge2Mu;
    GeScore=144*ones(1,2*w+Munum);
    SuOne=zeros(3,3);
    for i=1:2*w+Munum
        n=0;
        for j=1:9
            SuOne=Su(index1(j):index1(j)+2,index2(j):index2(j)+2);
            for m=1:SuNNumL(j)
                n=n+1;
                SuOne(SuNNumP(j,m))=G(i,n);             
            end
            Su(index1(j):index1(j)+2,index2(j):index2(j)+2)=SuOne;
        end
        for j=1:9
            SH=tabulate(Su(j,:));
            SHSame=SH(SH(:,2)>1,2)-1;
            GeScore(i)=GeScore(i)-sum(SHSame);                      
        end
        for j=1:9
            SL=tabulate(Su(:,j));
            SLSame=SL(SL(:,2)>1,2)-1;
            GeScore(i)=GeScore(i)-sum(SLSame);  
        end
    end
    score=sort(GeScore);
    score(1:length(GeScore))=score(length(GeScore):-1:1);%降序排列
    t1=1;
    f=1;
    while f==1
        MaxNum=find(GeScore==score(t1));
        for j=1:length(MaxNum)
            Ge2(t1,:)=G(MaxNum(j),:);
            if t1==w
                f=0;
                break;
            end
            t1=t1+1;
        end 
    end

    BestGe=G(1,:);
    if score(1)==144
        break;
    end
    l=l+1
end
BestGe
score(1)
toc;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章