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是,表示八皇后不能互相攻击,为最优解。

  • 相关阅读:
    【PAT甲级】1043 Is It a Binary Search Tree (25 分)(判断是否为BST的先序遍历并输出后序遍历)
    Educational Codeforces Round 73 (Rated for Div. 2)F(线段树,扫描线)
    【PAT甲级】1042 Shuffling Machine (20 分)
    【PAT甲级】1041 Be Unique (20 分)(多重集)
    【PAT甲级】1040 Longest Symmetric String (25 分)(cin.getline(s,1007))
    【PAT甲级】1039 Course List for Student (25 分)(vector嵌套于map,段错误原因未知)
    Codeforces Round #588 (Div. 2)E(DFS,思维,__gcd,树)
    2017-3-9 SQL server 数据库
    2017-3-8 学生信息展示习题
    2017-3-5 C#基础 函数--递归
  • 原文地址:https://www.cnblogs.com/idella/p/13410014.html
Copyright © 2011-2022 走看看