【问题标题】:Using GA in GUI在 GUI 中使用 GA
【发布时间】:2011-01-18 06:55:48
【问题描述】:

抱歉,如果不清楚,因为我是在移动设备上写的,我正在努力让它快点。

我用二进制编码(基因)编写了一个基本的遗传算法,它建立了一个适应度值,并使用锦标赛选择、变异和交叉通过多次迭代进化。作为一个基本的命令行示例,它似乎可以工作。

我遇到的问题是在 GUI 中应用遗传算法,因为我正在编写一个迷宫解决程序,该程序使用 GA 在迷宫中寻找方法。如何将我的随机二进制编码基因和适应度函数(将所有二进制值相加)转化为控制机器人绕迷宫的方法?我已经用 Java 构建了一个基本的 GUI,它由迷宫般的标签(如网格)组成,可用的路线是蓝色的,墙壁是黑色的。

重申我的 GA 表现良好,并且包含任何典型 GA 的功能(适应度方法、获取和设置人口、选择、交叉等),但现在我需要将其插入 GUI 以让我的迷宫运行。为了让机器人可以根据 GA 所说的内容向不同方向移动,需要去哪里?如果可能的话,粗略的伪代码会很棒

根据要求,Individual 是使用单独的类 (Indiv) 构建的,所有主要工作都在 Pop 类中完成。当一个新个体被实例化时,一个整数数组表示该个体的基因,基因从 0 到 1 之间的数字中随机选取。适应度函数只是将这些基因的值加在一起,在 Pop 类中处理选择,两个选定个体的突变和交叉。没什么其他的了,命令行程序只是显示了 n 代的进化,每次迭代的总适应度都在提高。

编辑:现在开始变得更有意义了,尽管有一些事情困扰着我......

正如 Adamski 建议的那样,我想使用下面显示的选项创建一个“代理”。我遇到的问题是随机位串在这里发挥作用。代理知道墙在哪里,并将其布置在 4 位字符串(即 0111)中,但这对随机 32 位字符串有何影响? (即10001011011001001010011011010101)如果我有以下迷宫(x是起点,2是目标,1是墙):

x 1 1 1 1
0 0 1 0 0
1 0 0 0 2

如果我向左转,我会走错路,如果它向前移动,代理将完全离开迷宫。我假设第一代绳子是完全随机的,它会随着适应度的增长而发展,但我不知道绳子在迷宫中是如何工作的。

所以,要直截了当地说...

适应度是代理能够移动并且靠墙时的结果。

基因是一串 32 位的字符串,分为 16 组 2 位以显示可用的动作,机器人移动这两个位需要从代理中传递四个位,以显示其在墙壁附近的位置。如果移动是要越过墙,则该移动不被执行并且被认为是无效的,如果移动被移动并且如果找到了新墙,那么适应度就会上升。

是吗?

【问题讨论】:

  • 也许您可以提供更多关于每个进化个体中编码了哪些信息的详细信息?
  • 我已经编辑了主要问题,虽然目前没有真正重要的编码,目前每个人有 500 个基因,随机数方法在其中添加 1 或 0。

标签: java genetic-algorithm


【解决方案1】:

如果我理解正确,您需要确定您的机器人在 GUI 中的操作如何由遗传算法的结果表示?我认为确定您要使用的表示应该是您的起点。因此,您需要为您个人中的每个(组)“基因”创建一个映射到您的机器人的特定动作或移动算法的特定变化。

一旦您选择了可行的表示形式,实施就会更加合乎逻辑。

运动的一个非常简单的表示是让基因硬编码一条特定的路线。你可以用四个基因块来代表不同的方向,然后一个 0 代表“不要朝这个方向移动”,一个 1 代表移动。

那么表示01001101可以翻译成如下运动模式:

stand still
go one step east
stand still
stand still
go one step north
go one step east
stand still
go one step west

【讨论】:

    【解决方案2】:

    如果您想解决特定的迷宫,BadHorse 的答案很好;您只需将您的位串解释为精确的指令的序列,以引导代理通过迷宫。在这种情况下,您的适应度不是位串的总和(正如您在问题中所述),而是一些衡量代理在解决问题方面的成功程度的指标。例如,您的适应度可能被定义为“处理 20 条指令后到迷宫尽头的直线距离”。

    因此,在评估每个个体时,您允许它处理来自您的位串的前 20 条指令,然后计算其适应度,执行任何交叉/突变,然后创建下一代个体。

    如果您希望开发您的代理来解决任何迷宫,您需要在位串中编码规则而不是一系列指令。您可以根据墙是紧靠机器人的后面、前面、左侧还是右侧来定义规则;例如

    FBLR Action
    0000 Move Forward
    0001 Move Forward
    0010 Turn Right
    etc
    

    这为您提供了一个由 16 个动作组成的位串,每个动作编码为 2 位(00 = 向前移动,01 = 右转,10 = 左转,11 = 向后移动)。在评估您的代理时,它只是确定其当前状态并使用位串作为查找表来确定它应该如何响应。然后它会重复此操作一定次数,然后您评估它的适应度。

    鉴于这种编码,代理可以评估人类通常使用的规则,即“持续跟随左侧墙”。显然,如果迷宫没有完全连接,这种方法将失败,在这种情况下,您需要将更多状态编码到基于规则的方法中(例如,如果越过“旧地”,代理可能会做出不同的反应)。

    希望对您有所帮助。

    编辑

    回应您的最新编辑:

    我已经对代理“传感器”进行编码以检测它是否靠近墙壁这一事实与位串(您的基因)无关,也许我与我的示例有些混淆。该基因仅编码动作(向前移动等)不传感器状态。

    因此,您应该编写代码来查找给定传感器读数的特定组合的位串的相关部分;例如

    /**
     * Enumeration describing the four available actions to the agent
     * and methods for decoding a given action from the "bit" string
     * (actually represented using booleans).
     */
    public enum Action {
      MOVE_FORWARD, REVERSE, TURN_LEFT, TURN_RIGHT
    
      Action decodeAction(boolean b1, boolean b2) {
        Action ret;
    
        if (b1) {
          ret = b2 ? Action.MOVE_FORWARD : Action.TURN_LEFT;
        } else {
          ret = b2 ? Action.TURN_RIGHT : Action.REVERSE;
        }
    
        return ret;
      }
    }
    
    /**
     * Class encapsulating the 32-bit "bit string" represented using booleans.
     * Given the state of the four agent inputs the gene will provide a specific
     * action for the agent to perform.
     */
    public class Gene {
      private final boolean[] values = new boolean[32];
    
      public Action getActionForSensorInputs(boolean wallInFront,
        boolean wallBehind, boolean wallToLeft, boolean wallToRight) {
    
        int i=0;
    
        // Encode the four sensor inputs as a single integer value by
        // bitwise-ORing each sensor value with a power of 2.
        // The encoded value will be in the range [0, 15].
        if (wallToRight) {
          i |= 0x01;
        }
    
        if (wallToLeft) {
          i |= 0x02;
        }
    
        if (wallBehind) {
          i |= 0x04;
        }
    
        if (wallInFront) {
          i |= 0x08;
        }
    
        // The look-up index is i * 2 because each action is encoded as 2
        // booleans.
        int index = i * 2;
    
        // Retrieve the two action bits from the bit string.
        boolean b1 = this.values[index];
        boolean b2 = this.values[index + 1];
    
        // Finally decode the action to perform.
        return Action.decodeAction(b1, b2);
      }
    
      // TODO: Add method to support crossover and mutation with other Genes.
    }
    

    鉴于Gene 的这个简单定义,您可以将此类嵌入Agent 实现中,并记录代理在“安装”当前基因的情况下如何执行;例如

    private enum Direction { NORTH, SOUTH, EAST, WEST };
    
    public class Agent {
      private final Geneva gene;
      private final int x; // x position in maze;
      private final int y; // y position in maze;
      private Direction currentDirection;
    
      public double evaluate() {
        double fitness;
    
        // Perform up to 20 actions and then evaluate fitness.
        for (int i=0; i<20; ++i) {
          // TODO Determine sensor inputs.
    
          Action action = gene.getActionForSensorInputs(...);
    
          // TODO: Now apply action to update agent's state.
          // If agent has reached goal exit loop and return fitness 1.0 (max fitness).
          // If agent has exited the maze then exit loop and return 0.0 (min fitness).
        }
    
        // Calculate fitness after 100 steps taken.  For example could be
        // calculated as sqrt((goal.x - x) ^ 2 + (goal.y - y) ^ 2).
    
        return fitness;
      }
    }
    

    【讨论】:

    • 后半部分是我想做的,所以它将使用规则解决任何给定的迷宫。我遇到麻烦的部分是分配规则。据我了解,代理将使用您在顶部给出的值进行自己的“查找”,然后从基因中获得一串 32 位的字符串,分成 16 组 2 位(动作),其中有四个可采取的行动。我已经编辑了问题以显示我遇到问题的地方,这样我就不会在这里用完空间..
    • @AlexT:抱歉耽搁了;我已经编辑了我的答案并添加了一些示例代码来帮助您入门。
    • 目前看起来很棒!我将玩弄一下代码并尝试构建一个 Maze 类,然后如果一切按计划进行,我会回来接受这个答案。
    • 好的,不用担心。请注意,我很快就将代码组合在一起并且尚未对其进行测试。另外,请记住,由于每个状态(例如前 3 个方格)中没有编码“记忆”,因此解决方案的解决能力将相当有限,但我认为这是一个很好的起点。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-27
    • 1970-01-01
    • 1970-01-01
    • 2017-12-22
    • 2014-04-22
    相关资源
    最近更新 更多