You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在OptaPlanner N皇后示例中定义moveFactory?配置后遇异常

解决N皇后示例中自定义MoveFactory的配置问题

你遇到的异常本质是默认MoveSelector和自定义MoveListFactory的冲突——OptaPlanner默认会自动为可规划变量生成一个ChangeMoveSelector,当你直接在配置里加<moveListFactory>却没明确覆盖默认选择器时,系统会同时加载多个MoveSelector,从而触发参数错误。下面是一步步的解决方案:

1. 修正XML配置文件

你需要把<moveListFactory>包裹在<moveSelector>标签内,明确告诉OptaPlanner只使用你的自定义MoveFactory,替换掉默认的选择器。修改后的localSearch配置示例如下:

<solver>
  <!-- 保留原有的solutionClass、entityClass、scoreDirectorFactory等配置 -->
  <localSearch>
    <!-- 明确指定自定义MoveListFactory为唯一的MoveSelector -->
    <moveSelector>
      <moveListFactory>
        <moveListFactoryClass>org.optaplanner.examples.nqueens.solver.move.factory.RowChangeMoveFactory</moveListFactoryClass>
      </moveListFactory>
    </moveSelector>
    <!-- 保留原有的acceptor、forager等优化配置 -->
    <acceptor>
      <entityTabuSize>7</entityTabuSize>
    </acceptor>
    <forager>
      <acceptedCountLimit>1000</acceptedCountLimit>
    </forager>
  </localSearch>
</solver>

2. 正确实现RowChangeMoveFactory

你的RowChangeMoveFactory需要实现OptaPlanner的MoveListFactory接口,针对N皇后场景生成所有合法的行变更移动(即把某一列的皇后移动到其他行,保持列不变)。示例代码如下:

package org.optaplanner.examples.nqueens.solver.move.factory;

import org.optaplanner.core.impl.heuristic.move.Move;
import org.optaplanner.core.impl.heuristic.move.factory.MoveListFactory;
import org.optaplanner.examples.nqueens.domain.NQueen;
import org.optaplanner.examples.nqueens.domain.NQueensSolution;

import java.util.ArrayList;
import java.util.List;

public class RowChangeMoveFactory implements MoveListFactory<NQueensSolution> {

    @Override
    public List<? extends Move> createMoveList(NQueensSolution solution) {
        List<Move> moveList = new ArrayList<>();
        int boardSize = solution.getN();
        
        // 遍历每个皇后,生成所有可行的行变更移动
        for (NQueen queen : solution.getNQueenList()) {
            int currentRow = queen.getRowIndex();
            for (int targetRow = 0; targetRow < boardSize; targetRow++) {
                // 排除当前行,避免无效移动
                if (targetRow != currentRow) {
                    moveList.add(new RowChangeMove(queen, targetRow));
                }
            }
        }
        return moveList;
    }
}

3. 实现对应的RowChangeMove类

MoveFactory生成的每一个Move实例需要支持执行、撤销操作,还要能判断移动是否有效。你需要实现这个Move类:

package org.optaplanner.examples.nqueens.solver.move;

import org.optaplanner.core.impl.heuristic.move.AbstractMove;
import org.optaplanner.examples.nqueens.domain.NQueen;
import org.optaplanner.examples.nqueens.domain.NQueensSolution;

public class RowChangeMove extends AbstractMove<NQueensSolution> {

    private final NQueen queen;
    private final int targetRowIndex;

    public RowChangeMove(NQueen queen, int targetRowIndex) {
        this.queen = queen;
        this.targetRowIndex = targetRowIndex;
    }

    // 执行移动:修改皇后的行号
    @Override
    protected void doMoveOnGenuineVariables(NQueensSolution solution) {
        queen.setRowIndex(targetRowIndex);
    }

    // 创建撤销移动:回到原来的行号
    @Override
    public RowChangeMove createUndoMove(NQueensSolution solution) {
        return new RowChangeMove(queen, queen.getRowIndex());
    }

    // 判断移动是否有效(目标行和当前行不同)
    @Override
    public boolean isMoveDoable(NQueensSolution solution) {
        return targetRowIndex != queen.getRowIndex();
    }

    // 生成移动描述,方便日志调试
    @Override
    public String getSimpleMoveDescription() {
        return queen + " moves from row " + queen.getRowIndex() + " to row " + targetRowIndex;
    }
}

异常原因复盘

OptaPlanner默认会自动为实体的可规划变量生成ChangeMoveSelector,当你直接添加<moveListFactory>却没通过<moveSelector>包裹时,系统会同时加载默认选择器和自定义工厂,导致moveSelector列表存在多个元素,最终触发IllegalArgumentException。通过将<moveListFactory>放入<moveSelector>内,你就明确指定了唯一的Move选择器,解决了冲突。

内容的提问来源于stack exchange,提问作者lambda-pumpkin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 04:21:01