Click here to Skip to main content
15,884,298 members
Articles / Artificial Intelligence
Article

8 Queens Solution with Genetic Algorithm

Rate me:
Please Sign up or sign in to vote.
4.96/5 (53 votes)
19 Oct 20055 min read 339.3K   14.6K   86   70
Using Genetic Algorithm to solve the 8 Queens problem.

Image 1

Genetic Algorithm, Theory

There are so many books and so many resources on the Web about Genetic Algorithms. The best that I can do is quote some nice descriptions from my preferred sites.

"Genetic algorithms are a part of evolutionary computing, which is a rapidly growing area of artificial intelligence. As you can guess, genetic algorithms are inspired by Darwin's theory about evolution. Simply said, solution to a problem solved by genetic algorithms is evolved."

The GA begins, like any other optimization algorithm, by defining the optimization variables. It ends like other optimization algorithms too, by testing for convergence. A path through the components of the GA is shown as a flowchart in Figure 1.

Image 2

Fig. 1 - A path through the components of the GA

Define cost function and cost

For each problem there is a cost function. For example, maximum of a 3D surface with peaks and valleys when displayed in variable space. Cost, a value for fitness, is assigned to each solution.

Chromosomes and genes

A gene is a number between 0 to n-1. A chromosome is an array of these genes. It could be an answer. Population in each generation has determined the number of chromosomes.

Create a random initial population

An initial population is created from a random selection of chromosomes. The number of generations needed for convergence depends on the random initial population.

Decode the chromosome and find the cost

To find the assigned cost for each chromosome a cost function is defined. The result of the cost function called is called cost value. Finally, the average of cost values of each generation converges to the desired answer.

Mating and next generation

Those chromosomes with a higher fitness (lesser cost) value are used to produce the next generation. The offsprings are a product of the father and the mother, whose composition consists of a combination of genes from them (this process is known as "crossing over"). If the new generation contains a chromosome that produces an output that is close enough or equal to the desired answer then the problem has been solved. If this is not the case, then the new generation will go through the same process as their parents did. This will continue until a solution is reached.

About the 8 queens problem

In chess, a queen can move as far as she pleases, horizontally, vertically, or diagonally. A chess board has 8 rows and 8 columns. The standard 8 by 8 queen's problem asks how to place 8 queens on an ordinary chess board so that none of them can hit any other in one move. Here we solve this problem with a genetic algorithm for a n (n is between 8 and 30) queen problem.

Using the code

We first describe the variables and the functions:

Variables:

  • int ChromosomeMatrix[30][1000]: This variable is a matrix of numbers between 0 to ChessBoardLenght-1. For example, if ChessBoardLength=8, ChromosomeMatrix could be {4,6,0,2,7,5,3,1} so that the first number defines the place of the queen in the first row, the second number defines the place of the queen in the second row and so on.
  • int CostMatrix[1000]: For each chromosome matrix, a cost value is defined. The best value is 0 and when no queen can take the other one.
  • int CrossOverMatrix[30][1000]: For generating children from parents, a crossover matrix is needed that decides which gene must be selected from the two parents.
  • int Population, int Iteration, float Mutationrate: These variables are genetic algorithm parameters.
  • int ChessBoardLength: This determines how many rows and columns a chessboard has.
  • int area[30][30]: This variable is a chess board matrix.

Functions:

void CGAQueen::Clear()
{
    // to Reset All cells 
    for (int i=0;i<ChessBorad;i++)
        for (int j=0;j<ChessBorad;j++)
            area[i][j]=0;
}

This function is used to clear the chess board (area[][]) with 0s.

void CGAQueen::InitialPopulation() 
{
    int random;
    bool bCheck;
    for (int index=0;index<=Population-1;index++)
        for (int a=0;a<ChessBorad;a++)
        {
            random=rand();
            bCheck=1;
            for (int b=0;b<a;b++)
            if (random%ChessBorad==ChromosomeMatrix[b][index])
                bCheck=0;
            if (bCheck)
                ChromosomeMatrix[a][index]=random%ChessBorad;
            else
                a--;
        }
}

This function is used to generate the initial population. This generation is a purely random generation. With this function, ChromosomeMatrix[][] is valued with random numbers between 0 and ChessBoardLength-1. The number of ChromosomeMatrix is equal to Population.

void CGAQueen::FillArea(int index)
{
    Clear();
    for (int i=0;i<ChessBoradLenght;i++)
        area[i][ChromosomeMatrix[i][index]]=1;
}

This function is used to fill the chess board with a chromosome. For example, if ChromosomeMatrix = {3,6,8,5,1,4,0,7,9,2}, the first number defines the column of the queen in the first row, the second number defines the column of the queen in the second row and so on. The area is shown in fig. 2 – here ChessBoardLength =10.

Image 3

Fig. 2 - The chess board with ChromosomeMatrix={ 3,6,8,5,1,4,0,7,9,2}

int CGAQueen::CostFunc(int index)
{
    int costValue=0;
    int m,n;
    int i,j;
    for(i=0;i<ChessBoradLenght;i++)
    {    
        j=ChromosomeMatrix[i][index];
        m=i+1;
        n=j-1;
        while(m<ChessBoradLenght    && n>=0)
        {
            if(area[m][n]==1)
                costValue++; 
            m++;
            n--;
        }

        m=i+1;
        n=j+1;
        while(m<ChessBoradLenght && n<ChessBoradLenght )
        {        
            if(area[m][n]==1)
                costValue++;            
            m++;
            n++;
        }
        m=i-1;
        n=j-1;
        while(m>=0 && n>=0)
        {        
            if(area[m][n]==1)
                costValue++; 
            m--;
            n--;
        }
        m=i-1;
        n=j+1;
        while(m>=0 && n<ChessBoradLenght)
        {        
            if(area[m][n]==1)
                costValue++;            
            m--;
            n++;
        }
    }
    return costValue;
}

This function is used to determines the cost value for each ChromosomeMatrix[][] and keeps it in CostMatrix[]. For example, for chromosome = { 2,6,9,3,5,0,4,1,7,8 }, the cost value will be 2. (See fig. 3.)

Image 4

Fig. 3 - Because of these two queens that hit each other, the cost value is 2.

void CGAQueen::PopulationSort()
{
    bool k=1;
    int Temp;
    while (k)
    {
        k=0;
        for (int i=0;i<=Population-2;i++)
        {
            if (CostMatrix[i]>CostMatrix[i+1])    
            {
                Temp=CostMatrix[i];
                CostMatrix[i]=CostMatrix[i+1];
                CostMatrix[i+1]=Temp;
                
            for (int j=0;j<ChessBoradLenght;j++)
            {
                Temp=ChromosomeMatrix[j][i];
                ChromosomeMatrix[j][i]=ChromosomeMatrix[j][i+1];
                ChromosomeMatrix[j][i+1]=Temp;
            }            
            k=1;
            }
        }
    }
}

This function is used to sort the new generation according to their cost value. The best (minimum) is placed on top and the worst (maximum) is placed in the bottom.

void CGAQueen::GenerateCrossOverMatrix()
{
    int randomCrossOver;
    for (int index=0;index<=Population-1;index++)
        for (int a=0;a<ChessBoradLenght;a++)
        {
            randomCrossOver=rand();
            CrossOverMatrix[a][index]=randomCrossOver%2;
        }
}

This function is used to generate the cross over matrix. This matrix contains 0s and 1s.

void CGAQueen::Mating()
{
    int TempMatrix[30][2];
    int TempMatrix0[30],TempMatrix1[30];
    int Temp,j,k;

    …
}

This function is used to generate children from parents using CrossOverMatrix. It is a way to do this job. Genes are drawn from p0 and p1. A gene is drawn from one parent and it is appended to the offspring (child) chromosome. The corresponding gene is deleted in the other parent (see figure 4). This step is repeated until both parent chromosomes are empty and the offspring contains all genes involved.

Image 5

Fig. 4 The Cross Over method

void CGAQueen::ApplyMutation()
{
    int randomChromosome;
    int randomGen0,randomGen1;
    int Temp;
    int NumberOfMutation=int(MutationRate*(Population-1)*ChessBoradLenght);
    for(int k=0;k<=NumberOfMutation;k++)
    {
        randomChromosome=0;
        while((randomChromosome=rand()%Population)==00;
        randomGen0=rand()%ChessBoradLenght;
        while((randomGen1=rand()%ChessBoradLenght)==randomGen0);
        Temp=ChromosomeMatrix[randomGen0][randomChromosome];
        ChromosomeMatrix[randomGen0][randomChromosome]=
          ChromosomeMatrix[randomGen1][randomChromosome];
        ChromosomeMatrix[randomGen0][randomChromosome]=Temp;
    }
}

This function is used to apply mutation to the current generation. First of all, a random chromosome is selected but the first (best) one in the list. Then, two random genes of this chromosome are selected and replaced with each other. Increasing the number of mutations increases the algorithm’s freedom to search outside the current region of chromosome space.

There is a demo for you to enjoy the genetic algorithm.

Have fun.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Iran (Islamic Republic of) Iran (Islamic Republic of)
I live in Iran . I started hardware programming when I was young. I designed and built some ISA cards for attaching to PC.like ocsiloscope and signal generator. Now I am working for a engineering company and Manager of some project.

Comments and Discussions

 
GeneralNice work Pin
Hatem Mostafa24-Mar-06 1:12
Hatem Mostafa24-Mar-06 1:12 
GeneralPoor, Misleading, Invalid PinPopular
Medus20-Nov-05 14:56
Medus20-Nov-05 14:56 
GeneralRe: Poor, Misleading, Invalid Pin
Medus20-Nov-05 15:11
Medus20-Nov-05 15:11 
GeneralRe: Poor, Misleading, Invalid Pin
YDLU10-Apr-06 12:02
YDLU10-Apr-06 12:02 
GeneralRe: Poor, Misleading, Invalid Pin
Medus12-Apr-06 8:56
Medus12-Apr-06 8:56 
GeneralRe: Poor, Misleading, Invalid Pin
David Roh20-Aug-06 4:03
David Roh20-Aug-06 4:03 
GeneralRe: Poor, Misleading, Invalid Pin
cristitomi7-Mar-07 4:33
cristitomi7-Mar-07 4:33 
GeneralRe: Poor, Misleading, Invalid Pin
garychapman15-Aug-07 10:12
garychapman15-Aug-07 10:12 
cristitomi wrote:
At first, I must disagree with you when you say that back-tracking is faster than GAs for this problem.

sence medus didnt catch this one up i will

christitomi, that depends on how you implement recursion and how you implement your genetic system.

if you do a pure bruteforce you're looking at 178,462,987,637,760 permutations in the search space. if you use the set {1, 2, 4, 8, 16, 32, 64, 128} and instead of a 2D board you simply reorder that set in an 8 element array of BYTE then the horizontal and vertical collisions are already negated and all you need to take care of is diagonal collisions. this is considerably faster at 40,360 permutations but slower than chance approaches which dont have the same overhead.

then optimise that the first row only needs the first 4 squares (And any result therefore is two results. itself obviously and also its reflection in the Y) and now you are down to 20,180 permutations for the entire search domain of which there are over 2000 discrete results dropping the search time to first hit dramatically. in reality most recursive solutions can expect to hit one in less than 1000 cycles

so why is a recursive approach slow

probably cus the most common recursive solutions go "queen hunting" some are so naive as to treat the board as a 2D entity and then performing an X,Y iteration to hunt out collisions on diagonals. this slows the approach to a crawl given the messy bounds checking involved and gives the genetics and the chaos crowd an advantage.

alternatively if you maintain a VMask, DMask1 and DMask2 as part of the state object that are each of type BYTE and then do the following (where x is the proposed candidate from the iteration for(unsigned byte x=128;byte>0;byte/=0) you get very fast results. observe...

for(unsigned byte x=128;x>0;x/=0){
if ((x & VMask)==0){
if (((x & HMask1)==0)&&((x & HMask1)==0))){
board[row++]=x;
if(STATE.row==8){
// DisplaySolution
}else{
VMask|=x;
DMask1|=x;DMask<<=1;
DMask2|=x;DMask>>=2;
// Recurse
}
}
}
}

see ? no iteration in the diagonals constraint checking and instead of many thousands of machine cycles per recursive iteration you are now down to less than 50 cycles. Even less if you allow use of register variables.

but dont take my word for it. look at the code and imagine how it optimises. each recursive cycle uses the following native instructions

One 8bit AND solves the vertical constraint in 1 machine instruction
Two 8bit ANDs solve the diagonal constraint in 2 machine instructions
Three 8bit ORs and two 8bit Shifts prepare the state machine for the next row

it doesn't get much faster than that unless you write it in ASM. besides this the C code is mostly bit operations which translate directly to machine instructions anyway. you dont even need the recursion overhead, except where it is posed as recursion homework Poke tongue | ;-P

so, as a byproduct of efficient enforcement of the diagonal constraint the compilers optimiser produces pretty much register level code due to the 8bit bitwise ops and the whole engine can fit in <20 machine code instructions with default optimisation. hardly worth going into ASM to tweak the conditionals just to gain a few extra bytes.

so be clear in knowing how fast the recursive algorithm is when the diagonal constraints are coded correctly. this is essential if one is to compare the two approaches.


by contrast a genetic algo is essentially chance based with a bias towards success. the problem seems to be that everyone seems to think they know how to write a genetic algo. to do this properly you need to know whether the obects you wish to mutate have any properties suitable to the approach. in the queens problem this means greater emphasis on evaluating positional efficiency (strong indicator of potential) rather than collisions themselves which are demonstrably a very poor indicator of offspring strength. a gene in this sense isnt strong just because it doesnt collide, it is strong because it wastes the least diagonals.

if you dont believe me consider the improvement statistics for the above scheme and then rewrite the algorithm to use collisions for fatality and make based on positional efficiency (potential strength) instead. the difference is staggering.

therefore i must agree with modus. the approach as presented is no faster than randomly reordering the set {1, 2, 4, 8, 16, 32, 64, 128} until a solution falls out. i disagree strongly with your statement that a genetic algorithm is faster than a recursive one. it can be in a any given run because the answer can fall out by chance in the first generation indeed this is highly likely if you have a population set of 1000 based upon the row-swapping approach. but that is hardly a success for the genetic approach.

cristitomi wrote:
everybody knows that cross-over points are chosen at random for most problems in genetic algorithms especially when you intend to use a beginner like algorithm, to explain some principles.


strictly speaking thats not a genetic algorithm. the key is to understand that there must be some legitimate notion of potency rather than state strength. if not then you are randomly mashing around till you hit on a solution and that is a chaos approach (albeit a heavily gilded one) rather than a genetic approach.

in the 8queens this a potency indication would be those genes which waste the least diagonals stand a better chance of being mated without collision and thus of producing a solution. therefore genes with such attributes are weighted heavier in the mating cycle. just saying genes that didnt collide much or at all should mate doesnt work and doesnt demonstrate the primary the principle behind genetics that one should impress upon a beginner.


IMHO medus christian and myself are wrong for debating whether genetics are more or less efficient for this problem because that is not the point. the author is just using the problem to show off roughly how a genetic algo works.

unfortunately i agree with medus that the code presented fails badly at showing the core principles of genetics and is probably a bad place for a beginner to start. if you want to understand the genetic approach you have to analyse the problem and identify any state attributes that are suitable for a genetic algo. failure to do so results in a program that abuses the spirit of the genetic method.

cristitomi wrote:
Of course there are better versions of cross-over for this problem, but those are advanced studies and are useless mentioned here (the author just wants to show HOW WE CAN USE GENETIC ALGORITHMS FOR THE 8 QUEENS PROBLEM).
So, keeping this in mind, be more careful next time when you write those kind of messages.

i think the problem is not that it is oversimplified because that would not be a fault at all in a beginners guide to genetics. but there is a very thin line to be drawn here between genetic approach and the chaos approach.

just because the algorithm presented defines 'genes' and swaps them around according to some sane sounding suitability criteria doesnt mean that it is a valid genetic approach. if it were it would favour potentiality. the distinction here is subtle but significant and it is one of the greatest barriers for a beginner in understanding genetics and applying the approach correctly.

similarly you cannot call randomly swapping values in a list until they fall into sequence "sorting" although YES it sorts. you could abstract the approach to include multiple list instances, some crossover mating and a bias toward numerical order and that may be sorting but it wouldnt neccesarily be genetics. see the problem?

the real problem is that we are balancing the definition of a genetic algorithm on a knife edge and arguing about which way it will fall. for this reason and this reason alone this is not a good beginners introduction to genetics.

i think modus makes a good point but rather badly
Generalcross over rate Pin
mbedward1-Nov-05 15:48
mbedward1-Nov-05 15:48 
GeneralRe: cross over rate Pin
asef1-Nov-05 18:22
asef1-Nov-05 18:22 
GeneralRe: cross over rate Pin
mbedward2-Nov-05 15:06
mbedward2-Nov-05 15:06 
Generalcontact with u Pin
amin chegeni28-Oct-05 9:00
amin chegeni28-Oct-05 9:00 
GeneralRe: contact with u Pin
asef29-Oct-05 1:06
asef29-Oct-05 1:06 
Generalgood one Pin
zxaar27-Oct-05 23:20
zxaar27-Oct-05 23:20 
GeneralCool Article Pin
Serge R24-Oct-05 6:18
Serge R24-Oct-05 6:18 
GeneralNice one Pin
hairy_hats24-Oct-05 1:28
hairy_hats24-Oct-05 1:28 
GeneralRe: Nice one Pin
asef26-Oct-05 21:10
asef26-Oct-05 21:10 
GeneralRe: Nice one Pin
hairy_hats27-Oct-05 2:44
hairy_hats27-Oct-05 2:44 
GeneralHi Asef great article! Pete in Australia Pin
Petey Boy23-Oct-05 23:05
Petey Boy23-Oct-05 23:05 
GeneralGot my 5 Pin
Mehdi Bonvari22-Oct-05 2:07
Mehdi Bonvari22-Oct-05 2:07 
GeneralRe: Got my 5 Pin
asef22-Oct-05 2:16
asef22-Oct-05 2:16 
GeneralExcellent! Pin
Abbas_Riazi19-Oct-05 23:50
professionalAbbas_Riazi19-Oct-05 23:50 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.