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

如何在多线程求解八皇后问题时,找到首个解后终止所有线程?

八皇后问题多线程优化求助

我正在开发一个解决八皇后问题的程序。程序会提示用户输入棋盘维度n,调用HillClimbingSearch类的runSearch()方法计算解并输出结果。为提升效率,我用100个线程并发执行runSearch()方法,但目前程序会输出所有100个线程各自找到的解。我需要实现仅输出第一个找到解的线程的结果,且只能修改Main类或runSearch()方法,特此求助。

以下是我的代码:

Main类代码

import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        int n = 0;
        try (Scanner s = new Scanner(System.in)) {
            while (true) {
                System.out.println("Enter the number of Queens :");
                n = s.nextInt();
                if (n == 2 || n == 3) {
                    System.out.println("No Solution possible for " + n + " Queens. Please enter another number");
                } else {
                    break;
                }
            }
        }
        long timestamp1 = System.currentTimeMillis();

        System.out.println("Solution to " + n + " queens using hill climbing search:");

        // 创建100个线程用于爬山搜索
        ThreadGroup threadGroup = new ThreadGroup("HillClimbingGroup");

        // 创建100个线程用于爬山搜索
        for (int i = 0; i < 100; i++) {
            HillClimbingSearch h = new HillClimbingSearch(n);
            Thread t = new Thread(threadGroup, h);
            t.start();
            h.runSearch();
        }

        // 等待所有线程完成
        while (threadGroup.activeCount() > 0) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        // 输出执行时间
        long timestamp2 = System.currentTimeMillis();
        long timeDiff = timestamp2 - timestamp1;
        System.out.println("Execution Time: " + timeDiff + " ms");
    }
}

HillClimbingSearch类代码

// 基于随机重启爬山法解决N皇后问题的程序
import java.util.Random;

public class HillClimbingSearch extends Thread {
    private int n;
    private int heuristic = 0;
    private int presentHeuristic;
    private NQueen[] finalSolution;

    public HillClimbingSearch(int size) {
        n = size;
        finalSolution = null;
    }

    public NQueen[] getFinalSolution() {
        return finalSolution;
    }

    // 生成随机初始棋盘
    public NQueen[] generateBoard() {
        NQueen[] startBoard = new NQueen[n];
        Random rndm = new Random();
        for (int i = 0; i < n; i++) {
            startBoard[i] = new NQueen(rndm.nextInt(n), i);
        }
        return startBoard;
    }

    // 打印当前棋盘状态
    public void printState(NQueen[] state) {
        // 根据当前棋盘创建临时二维数组
        int[][] tempBoard = new int[n][n];
        for (int i = 0; i < n; i++) {
            // 将皇后位置标记为1
            tempBoard[state[i].getRow()][state[i].getColumn()] = 1;
        }
        System.out.println();
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                System.out.print(tempBoard[i][j] + " ");
            }
            System.out.println();
        }
    }

    // 计算当前状态的启发值(冲突数)
    public int findHeuristic(NQueen[] state) {
        int heuristic = 0;
        for (int i = 0; i < state.length; i++) {
            for (int j = i + 1; j < state.length; j++) {
                if (state[i].ifConflict(state[j])) {
                    heuristic++;
                }
            }
        }
        return heuristic;
    }

    // 获取启发值更低的下一个棋盘状态
    public NQueen[] nextBoard(NQueen[] presentBoard) {
        NQueen[] nextBoard = new NQueen[n];
        NQueen[] tmpBoard = new NQueen[n];
        int presentHeuristic = findHeuristic(presentBoard);
        int bestHeuristic = presentHeuristic;
        int tempH;

        // 复制当前棋盘作为最优候选和临时棋盘
        for (int i = 0; i < n; i++) {
            nextBoard[i] = new NQueen(presentBoard[i].getRow(), presentBoard[i].getColumn());
            tmpBoard[i] = nextBoard[i];
        }

        // 遍历每一列
        for (int i = 0; i < n; i++) {
            if (i > 0)
                tmpBoard[i - 1] = new NQueen(presentBoard[i - 1].getRow(), presentBoard[i - 1].getColumn());
            tmpBoard[i] = new NQueen(0, tmpBoard[i].getColumn());

            // 遍历每一行
            for (int j = 0; j < n; j++) {
                // 计算临时棋盘的启发值
                tempH = findHeuristic(tmpBoard);
                // 如果临时棋盘更优,更新最优候选
                if (tempH < bestHeuristic) {
                    bestHeuristic = tempH;
                    for (int k = 0; k < n; k++) {
                        nextBoard[k] = new NQueen(tmpBoard[k].getRow(), tmpBoard[k].getColumn());
                    }
                }
                // 移动当前皇后到下一行
                if (tmpBoard[i].getRow() != n - 1)
                    tmpBoard[i].move();
            }
        }

        // 如果没有找到更优状态,随机生成新棋盘
        if (bestHeuristic == presentHeuristic) {
            nextBoard = generateBoard();
            heuristic = findHeuristic(nextBoard);
        } else
            heuristic = bestHeuristic;
        return nextBoard;
    }

    public void runSearch() {
        NQueen[] presentBoard = generateBoard();
        presentHeuristic = findHeuristic(presentBoard);
        // 循环直到找到解或线程被中断
        while (presentHeuristic != 0 && !Thread.currentThread().isInterrupted()) {
            presentBoard = nextBoard(presentBoard);
            presentHeuristic = findHeuristic(presentBoard);
        }
        if (!Thread.currentThread().isInterrupted()) {
            finalSolution = presentBoard;
            printState(finalSolution);
        }
    }
}

NQueen类代码

// N皇后问题的皇后类
public class NQueen {
    private int row;
    private int column;

    public NQueen(int row, int column) {
        this.row = row;
        this.column = column;
    }

    // 将皇后向下移动一行
    public void move() {
        row++;
    }

    // 判断当前皇后与另一皇后是否存在冲突
    public boolean ifConflict(NQueen q) {
        // 检查行或列冲突
        if (row == q.getRow() || column == q.getColumn())
            return true;
        // 检查对角线冲突
        else if (Math.abs(column - q.getColumn()) == Math.abs(row - q.getRow()))
            return true;
        return false;
    }

    public int getRow() {
        return row;
    }

    public int getColumn() {
        return column;
    }
}

解决方案

1. 修复Main类的线程执行错误

原代码中启动线程后直接调用h.runSearch(),会导致主线程同步执行该方法,而非由新线程处理。删除这一行:

for (int i = 0; i < 100; i++) {
    HillClimbingSearch h = new HillClimbingSearch(n);
    Thread t = new Thread(threadGroup, h);
    t.start();
    // 删除此行:h.runSearch();
}

2. 修改HillClimbingSearch类实现全局控制

添加线程安全的全局标记,确保只有第一个找到解的线程输出结果,并中断其他线程:

import java.util.concurrent.atomic.AtomicBoolean;

public class HillClimbingSearch extends Thread {
    // 全局标记:是否已找到解,原子类保证线程安全
    private static final AtomicBoolean solutionFound = new AtomicBoolean(false);
    private int n;
    // 其他原有变量保持不变

    public void runSearch() {
        // 已有解则直接返回
        if (solutionFound.get()) {
            return;
        }
        NQueen[] presentBoard = generateBoard();
        presentHeuristic = findHeuristic(presentBoard);
        
        // 循环条件新增解是否已找到的判断
        while (presentHeuristic != 0 && !Thread.currentThread().isInterrupted() && !solutionFound.get()) {
            presentBoard = nextBoard(presentBoard);
            presentHeuristic = findHeuristic(presentBoard);
        }
        
        // 只有第一个成功设置标记的线程执行输出
        if (!Thread.currentThread().isInterrupted() && solutionFound.compareAndSet(false, true)) {
            finalSolution = presentBoard;
            printState(finalSolution);
            // 中断线程组内所有其他线程,停止无效计算
            Thread.currentThread().getThreadGroup().interrupt();
        }
    }
}

核心逻辑

  • AtomicBoolean的compareAndSet方法确保只有第一个线程能将标记从false改为true,避免多个线程同时输出。
  • 找到解的线程会中断整个线程组,终止其他未完成的计算,提升效率。
  • 修复了原代码中线程未真正并发的错误,让100个线程真正并行执行搜索。

内容的提问来源于stack exchange,提问作者metsys

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 13:52:02