【问题标题】:Evolution Algorithm to solve Nonograms求解非图的进化算法
【发布时间】:2014-05-22 21:00:21
【问题描述】:

我想尝试使用进化算法解决非图。 我将适应度表示为满足董事会的约束量。 例如板 10X10 有 20 个约束(左 10,上 10)

所以我的最大体能是 20。

我的主要问题是算法在大多数情况下都停留在局部最大值(适应度为 15-18) 而且我不知道如何防止它或朝着正确的方向前进。

有时算法还是能解决这个难题。

我使用简单的交叉(第一个个体的 x 行 + 第二个个体的 y 行) 以及改变 1 个随机细胞的突变。

有什么更好的突变或其他技术可以帮助我解决局部最大值问题的想法吗?

非常感谢!

【问题讨论】:

  • 更多的多样性和/或更多的时间,但与替代方法相比,GA 不能很好地解决这种约束满足问题。

标签: algorithm genetic-algorithm evolutionary-algorithm


【解决方案1】:

这里的问题是 GA 对约束满足问题不是很好,例如,我给出了一个仅满足一种配置的板,而众所周知,GA 将尝试不同的排列来获得解决方案。对于 10X10 非图,您在排列中获得解决方案的概率是 (1/2^100),因此即使您跳过局部最小值,您获得解决方案的可能性也非常低。 GA 对于诸如背包或 TSP 之类的优化问题非常有用,在这些问题中您有解决方案但需要改进它。

解决方案:-

使用混合整数和约束规划

【讨论】:

    【解决方案2】:

    我不同意之前的回答,GA 的约束满足性非常好,虽然在 2^100 选项中只有 1 个答案是正确的,但您没有进行随机搜索,因此 1/2^100 的概率不是完全正确。

    在谈论 GA 时要牢记的重要一点是,他们通过探索搜索空间并了解不同选项的优劣来“构建”解决方案。我认为你的主要问题是你的健康,只有 20 个健康值使得算法很难区分“好”解决方案和“好++”解决方案,这就是为什么你最终在 15-18 范围内。

    创建 GOOD 适应度函数是 GA 设计中最困难的问题之一,通常需要多次尝试才能正确完成,让我们看一个例子来解释我的意思。

    获取 2x2 Nonogram。

      2 1
    2|_|_|
    1|_|_|
    

    解决办法:

      2 1
    2|X|_|
    1|_|_|
    

    将从您的适应度函数中获得 0 分,因为它满足 0 个约束。

    解决办法:

      2 1
    2|_|_|
    1|_|X|
    

    会从您的适应度函数中获得 2 分,但正如您可能已经意识到的那样,第一个是朝着解决方案迈出的“一步”,第二个会卡在那里。显然,您的适应度函数需要进行一些重新审视和调整,因此您可以采用“朝着正确方向”的解决方案并在它们的基础上进行构建(通过突变或交叉,我认为您描述它们的方式很好)。

    【讨论】:

    • 我做了类似你说的事情。最后,我的适应度函数返回的值在 (0,100) 范围内。有了这个新的适应度,我设法解决了 10x10 有时是 15x15 的非图,但更大的仍然是个问题。
    • 那么你可能需要一些额外的调整,不仅实际的健身范围很重要,你用来计算它的“规则”也很重要。随着您不断迭代,您会发现一些规则可以帮助您更快地找到答案,并且您可以使用它们。此外,GA 的一个很酷的地方在于它们被认为是“任何时间”的算法,所以如果你的算法卡住了,你可以抓住当前的最佳解决方案,并使用确定性的方法来完成基于 GA 已经发现的探索. (这是 GA 的一种相当常见的用法,可大幅减少搜索空间)
    【解决方案3】:

    (添加到 Sisnett 的 good answer

    有什么更好的突变或其他技术可以帮助我解决局部最大值问题的想法吗?

    可以采用标准的变异/交叉算子,可能问题表示和适应度函数有待提高。

    这里我只是举个例子。更多 details 和可能的 C++ implementation(基于 Vita 框架)可以在 Github 上找到。

    个人代表

    一个候选解(即一个个体的GAs群体)可以被编码为一个整数序列。

    考虑以下问题:

    . # # # . . . .  3
    # # . # . . . .  2 1
    . # # # . . # #  3 2
    . . # # . . # #  2 2
    . . # # # # # #  6
    # . # # # # # .  1 5
    # # # # # # . .  6
    . . . . # . . .  1
    . . . # # . . .  2
    1 3 1 7 5 3 4 3
    2 1 5 1
    

    模式可以被编码(列顺序)为:

    COLUMN (zero-based indexing)
    | 0  |  1  |  2  |  3  | 4| 5| 6| 7|
    
    {1, 2, 0, 2, 0, 0, 0, 0, 4, 4, 2, 2}
    
     ^  ^                          ^  ^
     |  |                          |  +-- last block (size 3)
     |  |                          +----- second to last block (size 4)
     .  .                          .  .
     .  .                          .  .
     .  .                          .  .
     |  +-------------------------------- second block (size 2)
     +----------------------------------- first block (size 1)
    

    其中每个数字代表一个方块与第一个允许放置位置的距离。总共有 8 列 12 个块。

    按列编码 是任意选择。相同的模式可以表示(行顺序)为:

    {1, 0, 0, 1, 1, 2, 1, 2, 0, 0, 0, 4, 3}
    

    这次是 9 行 13 块。

    根据问题结构(即线索的数量和类型),一种方法可能比另一种方法更好(从性能的角度来看)。

    无论如何,总有可能:

    1. 交换行列线索
    2. 解决由此产生的问题
    3. 转置解矩阵得到原问题的解

    请注意,该表示允许无效的个人。考虑以下列:

    .
    .
    .
    .
    1
    2
    

    唯一的解决方案是{0,0}:

    #
    .
    #
    #
    1
    2
    

    但表示也允许{0,1}{0,2}{0,3}、...{3,2}{3,3}

    为了“保持简单”,可以将无效整数映射到有效范围(例如通过 模运算符),从而在基因型(整数列表)和表型(块在板上的实际位置)。

    这与语法进化方案非常相似,其中搜索算法操作的对象和适应度评估函数解释的对象是不同的。

    健身功能

    首先,您需要一个解码函数,在给定基因组的情况下,它会返回一个true/false 矩阵。

    使用建议的表示列约束(即列线索)已经得到满足。适应度函数仅考虑行线索来衡量个人的表现。

    例如

    Solution:                Candidate solution:
                             {1,2,0,2,0,0,0,0,4,4,2,0}
    
    . # # # . . . .  3       . # # # . . . #  3 1    <-- ERROR
    # # . # . . . .  2 1     # # . # . . . #  2 1 1  <-- ERROR
    . # # # . . # #  3 2     . # # # . . # #  3 2
    . . # # . . # #  2 2     . . # # . . # .  2 1    <-- ERROR
    . . # # # # # #  6       . . # # # # # .  5      <-- ERROR
    # . # # # # # .  1 5     # . # # # # # .  1 5
    # # # # # # . .  6       # # # # # # . .  6
    . . . . # . . .  1       . . . . # . . .  1
    . . . # # . . .  2       . . . # # . . .  2
    1 3 1 7 5 3 4 3          1 3 1 7 5 3 4 3
    2 1 5 1                  2 1 5 1
    

    最后一列的块放错了:

    ROW  CORRECT  WRONG     DELTA
    0     [3]     [3,1]     |3-3| + |0-1| = 1
    1     [2,1]   [2,1,1]   |2-2| + |1-1| + |0-1| = 1
    3     [2,2]   [2,1]     |2-2| + |2-1| = 1
    4     [6]     [5]       |6-5| = 1
    
    FITNESS = -sum(delta_i) = -4
    

    适应度函数计算正确和错误序列之间的距离,按顺序匹配元素并获取它们的绝对差。

    注意事项

    GA 往往会产生良好但次优的解决方案,这种行为仅适​​用于某些组合优化问题。

    所提出的表示足以检测低阶模式中的偏差,并结合信息最终收敛(我使用过 30x30 非图)。

    还有进一步的改进,但这应该是一个很好的起点。

    【讨论】:

      【解决方案4】:

      这是一个解决非图形表的 Python 代码。就是这样:

      import numpy as np
      from itertools import product
      from datetime import datetime
      
      row_constraints = ([7,2], [5,1], [3], [15], [2], [15], [1], [1], [13], [3,1,1,1], [1,3,1,3], [1,3,3], [1,1,1], [1,1,1],\
                     [1,6], [1,8,1], [1,2,2,1], [2,2,2], [4,3], [14])
      column_constraints = ([8], [6,1,1], [4,1,2,4,2], [2,1,1,3,5], [2,1,1,1,1,3], [1,1,1,1,2,2,2], [1,1,1,1,3,3,1], \
                         [1,1,3,3,1], [1,1,1,2,1,1], [1,1,1,2,1,1], [1,1,3,3,1], [1,1,1,2,3,1], [1,1,1,2,2,2], [1,1,1,1,2,3], [2,1,1,3,5])
      
      
      
      def is_valid_solution(new_solution, list_of_cols_to_check, list_of_rows_to_check, col_or_row):
      
          if col_or_row == 'row':
              for col in list_of_cols_to_check:
                  arr = new_solution[:, col]
                  if get_list_of_strip_sizes(arr, 20) != column_constraints[col]:
                      return False
          else:
              for row in list_of_rows_to_check:
                  arr = new_solution[row, :]
                  if get_list_of_strip_sizes(arr, 15) != row_constraints[row]:
                      return False
      
          return True
      
      
      def get_list_of_strip_sizes(arr, length):
          list_of_strips = []
          i = 0
          while i < length:
              while i < length and arr[i] == 0:
                  i += 1
      
              if i == length:
                  return list_of_strips
      
              strip_length = 0
              while i < length and arr[i] == 1:
                  i += 1
                  strip_length += 1
      
              list_of_strips.append(strip_length)
      
              if i == length:
                  return list_of_strips
      
      
      def is_legal_product_setting(potentially_legal_line, constraints):
          for index in range(1, len(potentially_legal_line)):
              if(potentially_legal_line[index] <= potentially_legal_line[index-1] + constraints[index-1]):
                  return False
          return True
      
      
      
      def create_lines_with_constraints(length=15, constraints=[]):
          list_of_ranges = []
          min_value = 0
          max_value = length - sum(constraints) - len(constraints) + 1
          for constraint in constraints:
              list_of_ranges.append(range(min_value, max_value + 1))
              min_value += constraint + 1
              max_value += constraint + 1
      
          list_of_legal_lines = []
          for potentially_legal_line in product(*list_of_ranges):
      
              # skip illegal products
              if is_legal_product_setting(potentially_legal_line, constraints) == False:
                  continue
      
              # get blanks
              line = np.zeros(length)
              for index, start_position in enumerate(potentially_legal_line):
                  for cell in range(start_position, start_position + constraints[index]):
                      line[cell] = 1
      
              list_of_legal_lines.append(line)
      
          return list_of_legal_lines
      
      
      def add_new_line(col_or_row, index, line_length, constraint, list_of_cols_to_check, list_of_rows_to_check, list_of_potential_solutions):
          new_list_of_valid_solutions = []
      
          potential_new_lines = create_lines_with_constraints(length=line_length, constraints=constraint)
      
          for solution in list_of_potential_solutions:
      
              for potential_new_line in potential_new_lines:
      
                  if col_or_row == 'col':
                      before = np.reshape(solution[:, 0:index], (20, index))
                      inject = np.reshape(potential_new_line, (20, 1))
                      after_dim = 15 - index - 1
                      after = np.reshape(solution[:, index + 1:], (20, after_dim))
                      new_solution = np.concatenate([before, inject, after], axis=1)
                  else:
                      before = np.reshape(solution[0:index, :], (index, 15))
                      inject = np.reshape(potential_new_line, (1, 15))
                      after_dim = 20 - index - 1
                      after = np.reshape(solution[index + 1:, :], (after_dim, 15))
                      new_solution = np.concatenate([before, inject, after], axis=0)
      
                  if is_valid_solution(new_solution, list_of_cols_to_check, list_of_rows_to_check, col_or_row):
                      new_list_of_valid_solutions.append(new_solution)
      
          return new_list_of_valid_solutions
      def main():
          # initial solution a solution is a matrix
      
          list_of_potential_solutions = [np.zeros((20, 15), dtype=int)]
      
          list_of_cols_to_check = []
          list_of_rows_to_check = []
          list_of_lines_to_check = [('row', 3), ('col', 0), ('row', 5), ('col', 1), 
                                    ('row', 8), ('col', 2), ('row', 19), ('col', 3), 
                                    ('row', 15), ('col', 10), ('row', 0), ('col', 14), 
                                    ('row', 10), ('col', 6), ('row', 4), ('col', 5), 
                                    ('row', 1), ('col', 13), ('row', 9), ('col', 4), 
                                    ('row', 7), ('col', 7), ('row', 11), ('col', 12), 
                                    ('row', 18), ('col', 11), ('row', 2), ('col', 8), 
                                    ('row', 17), ('col', 9), 
                                    ('row', 16), ('row', 13), ('row', 12), ('row', 14), ('row', 6)]
          for cell in list_of_lines_to_check:
      
              if (cell[0]=='col'):
                  col_index = cell[1]
                  list_of_cols_to_check.append(col_index)
                  list_of_potential_solutions = add_new_line('col', col_index, 20, column_constraints[col_index], list_of_cols_to_check, list_of_rows_to_check, list_of_potential_solutions)
              else:
                  row_index = cell[1]
                  list_of_rows_to_check.append(row_index)
                  list_of_potential_solutions = add_new_line('row', row_index, 15, row_constraints[row_index], list_of_cols_to_check, list_of_rows_to_check, list_of_potential_solutions)
      
              print([cell, len(list_of_potential_solutions), datetime.now().hour, datetime.now().minute, datetime.now().second])
      
          print(list_of_potential_solutions)
      
      
      
      if __name__ == "__main__":
          main()
      

      就是这样!

      【讨论】:

        猜你喜欢
        • 2016-04-16
        • 2016-08-13
        • 1970-01-01
        • 1970-01-01
        • 2010-12-03
        • 2015-02-07
        • 1970-01-01
        • 2016-01-26
        • 1970-01-01
        相关资源
        最近更新 更多