zoukankan      html  css  js  c++  java
  • Genetic Algorithms with python 学习笔记ch4

    八皇后问题

    在一个棋盘上放置八个皇后,要求各个皇后不能互相攻击。即各个皇后不能在同一行、同一列、同一对角线。

    利用GA求解该问题,其 8queensTests.py 完整代码如下:

    import datetime
    import unittest
    
    import genetic
    
    
    def get_fitness(genes, size):
        board = Board(genes, size)
        rowsWithQueens = set()
        colsWithQueens = set()
        northEastDiagonalsWithQueens = set()
        southEastDiagonalsWithQueens = set()
        for row in range(size):
            for col in range(size):
                if board.get(row, col) == 'Q':
                    rowsWithQueens.add(row)
                    colsWithQueens.add(col)
                    northEastDiagonalsWithQueens.add(row + col)
                    southEastDiagonalsWithQueens.add(size - 1 - row + col)
        total = size - len(rowsWithQueens) 
                + size - len(colsWithQueens) 
                + size - len(northEastDiagonalsWithQueens) 
                + size - len(southEastDiagonalsWithQueens)
        return Fitness(total)
    
    
    def display(candidate, startTime, size):
        timeDiff = datetime.datetime.now() - startTime
        board = Board(candidate.Genes, size)
        board.print()
        print("{}	- {}	{}".format(
            ' '.join(map(str, candidate.Genes)),
            candidate.Fitness,
            timeDiff))
    
    
    class EightQueensTests(unittest.TestCase):
        def test(self, size=8):
            geneset = [i for i in range(size)]
            startTime = datetime.datetime.now()
    
            def fnDisplay(candidate):
                display(candidate, startTime, size)
    
            def fnGetFitness(genes):
                return get_fitness(genes, size)
    
            optimalFitness = Fitness(0)
            best = genetic.get_best(fnGetFitness, 2 * size, optimalFitness,
                                    geneset, fnDisplay)
            self.assertTrue(not optimalFitness > best.Fitness)
    
        def test_benchmark(self):
            genetic.Benchmark.run(lambda: self.test(20))
    
    
    class Board:
        def __init__(self, genes, size):
            board = [['.'] * size for _ in range(size)]
            for index in range(0, len(genes), 2):
                row = genes[index]
                column = genes[index + 1]
                board[column][row] = 'Q'
            self._board = board
    
        def get(self, row, column):
            return self._board[column][row]
    
        def print(self):
            # 0,0 prints in bottom left corner
            for i in reversed(range(len(self._board))):
                print(' '.join(self._board[i]))
    
    
    class Fitness:
        def __init__(self, total):
            self.Total = total
    
        def __gt__(self, other):
            return self.Total < other.Total
    
        def __str__(self):
            return "{}".format(self.Total)
    
    
    if __name__ == '__main__':
        unittest.main()
    

    下面对上述代码逐一解释:首先对于八皇后这样一个问题,如果需要用遗传算法来求解的话,并不需要修改之前一直使用的 engine ,因此 genetic.py 和上一章中使用的文件内容相同。
    那么如何利用 engine 来进行计算呢?下面是两个关键的步骤:

    首先需要设置基因的类型
    其次需要修改适应值

    1.制定基因形式表示皇后的位置

    class Board:
        def __init__(self, genes, size):
            board = [['.'] * size for _ in range(size)]
            for index in range(0, len(genes), 2):
                row = genes[index]
                column = genes[index + 1]
                board[column][row] = 'Q'
            self._board = board
    
        def get(self, row, column):
            return self._board[column][row]
    
        def print(self):
            # 0,0 prints in bottom left corner
            for i in reversed(range(len(self._board))):
                print(' '.join(self._board[i]))
    

    上面表示Board类表示的是整个棋盘,棋盘中 '.' 表示空白,'Q' 表示皇后,初始棋盘如下图所示:

    这里设置基因表示皇后的位置,八个皇后共16位,分别表示八个皇后的行列号(取值范围为0~7)。

    2.设定适应值函数

    class Fitness:
        def __init__(self, total):
            self.Total = total
    
        def __gt__(self, other):
            return self.Total < other.Total
    
        def __str__(self):
            return "{}".format(self.Total)
    

    首先适应值由其元素 Total 值的大小决定,并且该值越小越好,那么 Total 是如何计算的呢?

    def get_fitness(genes, size):
        board = Board(genes, size)
        rowsWithQueens = set()
        colsWithQueens = set()
        northEastDiagonalsWithQueens = set()
        southEastDiagonalsWithQueens = set()
        for row in range(size):
            for col in range(size):
                if board.get(row, col) == 'Q':
                    rowsWithQueens.add(row)
                    colsWithQueens.add(col)
                    northEastDiagonalsWithQueens.add(row + col)
                    southEastDiagonalsWithQueens.add(size - 1 - row + col)
        total = size - len(rowsWithQueens) 
                + size - len(colsWithQueens) 
                + size - len(northEastDiagonalsWithQueens) 
                + size - len(southEastDiagonalsWithQueens)
        return Fitness(total)
    

    上面就是Total的计算过程,有点不好理解,所以先举个简单的例子:

    由于八个皇后不能在同一行,也不能在同一列,因此不同皇后的行号应当不同,列号也应当不同
    所以设置一个关于行号的集合 rowsWithQueens ,和一个关于列号的集合 colsWithQueens 。
    由于集合之中不能有相同的元素,因此两个皇后有相同行号 x的话集合中也只有一个 x 存在。
    这样的话如果集合的长度 len(rowsWithQueens) = size 就表示每个皇后所在的行是不同的

    但是只满足上列得条件不一定会得到问题的解,例如:
    image.png
    因此应当将相同对角线也编上相同的编号,这样是对角线的编号不同就可以了。对角线分为东南对角线(如上图),和东北对角线(如下图):
    image.png
    对他们的编号情况如下:
    image.png
    上面是东南对角线的编号,对角线对应的编号可以由(8-1-行号)+列号计算得出。
    image.png

    上面这个是东北对角线的编号,编号可以由行号+列号计算得出。
    因此total由(size-行号个数)+(size-列号个数)+(size-东南对角线号的个数)+(size-东北对角线号的个数)得到,当total=0是,表示八皇后不能互相攻击,为最优解。

  • 相关阅读:
    如何用cmd命令加密文件夹
    C++异常处理
    STRTOK
    如何生成Detours.lib——Detours的使用准备
    学习C++心得与值得一看的书
    工作两年后的感悟
    MFC十八个简单问题转载
    程序员的五种非技术错误 转载
    用VC写DLL中"error LNK2005: _DllMain@12 already defined"的错误
    CxImage
  • 原文地址:https://www.cnblogs.com/idella/p/13410014.html
Copyright © 2011-2022 走看看