繁体   English   中英

优化N皇后拼图

[英]Optimizing N queens puzzle

我正在尝试解决在NxN板上放置N个皇后号而没有行,列和对角线冲突的问题。 我使用最小化冲突的算法。 首先,在每列上随机放置一个女王。 之后,从所有冲突女王中随机选择一个,并为她的列计算每个可能位置的冲突。 然后,女王以最少的冲突次数移动到最佳位置。 它可以工作,但运行速度非常慢。 我的目标是使其快速运行10000个皇后。 您能否请我建议一些改进,或者发现我的逻辑中有一些错误?

这是我的代码:

public class Queen {

int column;
int row;
int d1;
int d2;

public Queen(int column, int row, int d1, int d2) {
    super();
    this.column = column;
    this.row = row;
    this.d1 = d1;
    this.d2 = d2;
}

@Override
public String toString() {
    return "Queen [column=" + column + ", row=" + row + ", d1=" + d1
            + ", d2=" + d2 + "]";
}

@Override
public boolean equals(Object obj) {
    return ((Queen)obj).column == this.column && ((Queen)obj).row == this.row;
}   

}

和:

import java.util.HashSet;
import java.util.Random;


public class SolveQueens {
public static boolean printBoard = false;
public static int N = 100;
public static int maxSteps = 2000000;
public static int[] queens = new int[N];
public static Random random = new Random();

public static HashSet<Queen> q = new HashSet<Queen>();

public static HashSet rowConfl[] = new HashSet[N];
public static HashSet d1Confl[] = new HashSet[2*N - 1];
public static HashSet d2Confl[] = new HashSet[2*N -  1];

public static void init () {
    int r;
    rowConfl = new HashSet[N];
    d1Confl = new HashSet[2*N - 1];
    d2Confl = new HashSet[2*N - 1];
    for (int i = 0; i < N; i++) {
        r = random.nextInt(N);
        queens[i] = r;
        Queen k = new Queen(i, r, i + r, N - 1 +  i - r);
        q.add(k);
        if (rowConfl[k.row] == null) {
            rowConfl[k.row] = new HashSet<Queen>();
        }
        if (d1Confl[k.d1] == null) {
            d1Confl[k.d1] = new HashSet<Queen>();
        }
        if (d2Confl[k.d2] == null) {
            d2Confl[k.d2] = new HashSet<Queen>();
        }
        ((HashSet<Queen>)rowConfl[k.row]).add(k);
        ((HashSet<Queen>)d1Confl[k.d1]).add(k);
        ((HashSet<Queen>)d2Confl[k.d2]).add(k);
    }
}

public static void print () {
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            System.out.print(queens[i] == j ? "♕ " : "◻◻◻ ");
        }
        System.out.println();
    }  
    System.out.println();
}


public static boolean checkItLinear() {

    Queen r = choseConflictQueen();

    if (r == null) {
        return true;
    }

    Queen newQ = findNewBestPosition(r);

    q.remove(r);
    q.add(newQ);
    rowConfl[r.row].remove(r);
    d1Confl[r.d1].remove(r);
    d2Confl[r.d2].remove(r);
    if (rowConfl[newQ.row] == null) {
        rowConfl[newQ.row] = new HashSet<Queen>();
    }
    if (d1Confl[newQ.d1] == null) {
        d1Confl[newQ.d1] = new HashSet<Queen>();
    }
    if (d2Confl[newQ.d2] == null) {
        d2Confl[newQ.d2] = new HashSet<Queen>();
    }
    ((HashSet<Queen>)rowConfl[newQ.row]).add(newQ);
    ((HashSet<Queen>)d1Confl[newQ.d1]).add(newQ);
    ((HashSet<Queen>)d2Confl[newQ.d2]).add(newQ);
    queens[r.column] = newQ.row;
    return false;
}

public static Queen choseConflictQueen () {

    HashSet<Queen> conflictSet = new HashSet<Queen>();
    boolean hasConflicts = false;

    for (int i = 0; i < 2*N - 1; i++) {
        if (i < N && rowConfl[i] != null) {
            hasConflicts = hasConflicts || rowConfl[i].size() > 1;
            conflictSet.addAll(rowConfl[i]);
        }
        if (d1Confl[i] != null) {
            hasConflicts = hasConflicts || d1Confl[i].size() > 1;
            conflictSet.addAll(d1Confl[i]);
        }
        if (d2Confl[i] != null) {
            hasConflicts = hasConflicts || d2Confl[i].size() > 1;
            conflictSet.addAll(d2Confl[i]);
        }
    }
    if (hasConflicts) {
        int c = random.nextInt(conflictSet.size());
        return (Queen) conflictSet.toArray()[c];
    }

    return null;
}

public static Queen findNewBestPosition(Queen old) {        
    int[] row = new int[N];
    int min = Integer.MAX_VALUE;
    int minInd = old.row;

    for (int i = 0; i < N; i++) {
        if (rowConfl[i] != null) {
            row[i] = rowConfl[i].size();
        }
        if (d1Confl[old.column + i] != null) {
            row[i] += d1Confl[old.column + i].size();
        }
        if (d2Confl[N - 1 +  old.column - i] != null) {
            row[i] += d2Confl[N - 1 +  old.column - i].size();
        }
        if (i == old.row) {
            row[i] = row[i] - 3;
        }

        if (row[i] <= min && i != minInd) {
            min = row[i];
            minInd = i;
        }
    }

    return new Queen(old.column, minInd, old.column + minInd, N - 1 +  old.column - minInd);
}

public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        init();
        int steps = 0;
        while(!checkItLinear()) {
            if (++steps > maxSteps) {
                init();
                steps = 0;
            }
        }
        long endTime = System.currentTimeMillis();
        System.out.println("Done for " + (endTime - startTime) + "ms\n");

        if(printBoard){
            print(); 
        }


}
}

编辑:

这是我的一点点优化的解决方案,删除了一些未使用的对象,并在初始化时将皇后区放在对角线位置。

import java.util.Random;
import java.util.Vector;


public class SolveQueens {
public static boolean PRINT_BOARD = true;
public static int N = 10;
public static int MAX_STEPS = 5000;

public static int[] queens = new int[N];
public static Random random = new Random();


public static int[] rowConfl = new int[N];
public static int[] d1Confl = new int[2*N - 1];
public static int[] d2Confl = new int[2*N - 1];

public static Vector<Integer> conflicts = new Vector<Integer>();

public static void init () {
    random = new Random();
    for (int i = 0; i < N; i++) {
        queens[i] = i;
    }
}

public static int getD1Pos (int col, int row) {
    return col + row;
}

public static int getD2Pos (int col, int row) {
    return N - 1 + col - row;
}

public static void print () {
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            System.out.print(queens[i] == j ? "Q " : "* ");
        }
        System.out.println();
    }  
    System.out.println();
}


public static boolean hasConflicts() {

    generateConflicts();

    if (conflicts.isEmpty()) {
        return false;
    }

    int r = random.nextInt(conflicts.size());
    int conflQueenCol = conflicts.get(r);
    int currentRow = queens[conflQueenCol];
    int bestRow = currentRow;
    int minConfl = getConflicts(conflQueenCol, queens[conflQueenCol]) - 3;
    int tempConflCount;

    for (int i = 0; i < N ; i++) {
        tempConflCount = getConflicts(conflQueenCol, i);
        if (i != currentRow && tempConflCount <= minConfl) {
            minConfl = tempConflCount;
            bestRow = i;
        }
    }
    queens[conflQueenCol] = bestRow;
    return true;
}

public static void generateConflicts () {

    conflicts = new Vector<Integer>();

    rowConfl = new int[N];
    d1Confl = new int[2*N - 1];
    d2Confl = new int[2*N -  1];

    for (int i = 0; i < N; i++) {
        int r = queens[i];
        rowConfl[r]++;
        d1Confl[getD1Pos(i, r)]++;
        d2Confl[getD2Pos(i, r)]++;
    }

    for (int i = 0; i < N; i++) {
        int conflictsCount = getConflicts(i, queens[i]) - 3;
        if (conflictsCount > 0) {
            conflicts.add(i);
        }
    }
}

public static int getConflicts(int col, int row) {
    return rowConfl[row] + d1Confl[getD1Pos(col, row)] + d2Confl[getD2Pos(col, row)];
}



public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        init();
        int steps = 0;
        while(hasConflicts()) {
            if (++steps > MAX_STEPS) {
                init();
                steps = 0;
            }
        }
        long endTime = System.currentTimeMillis();
        System.out.println("Done for " + (endTime - startTime) + "ms\n");

        if(PRINT_BOARD){
            print(); 
        }
}

}

评论会很有帮助:)

除了重新创建冲突集和“最糟糕的冲突”之后,您还可以创建它一次,然后仅更新已更改的行/列吗?

编辑0:我尝试了一下您的代码。 由于代码是随机的,因此很难确定更改是否正确,因为您可能会以良好的初始状态或糟糕的状态开始。 我尝试用10个皇后进行10次跑步,并得出了截然不同的答案,但结果如下。

我伪装分析了哪些语句执行得最多,结果表明selectConflictQueen中的内部循环语句执行得最多。 我尝试插入一个中断以拉出第一个冲突女王(如果找到),但似乎并没有太大帮助。

统计

仅对耗时超过一秒的运行进行分组:

更多统计

我意识到我只有10次跑步,这在统计上还算是不够的,但是,嘿。

因此,增加休息时间似乎无济于事。 我认为建设性的解决方案可能会更快,但是随机性将再次使检查变得更加困难。

您的方法很好:具有最小冲突约束的本地搜索算法。 我建议尝试改善您的初始状态。 与其随机放置所有皇后(每列1个),不如尝试放置它们,以使冲突的数量最小化。 一个示例是尝试根据前一个位置或下两个位置放置下一个皇后,然后本地搜索将减少问题列的处理。

如果您随机选择,则可能选择先前状态相同的状态。 从理论上讲,即使有解决方案,也可能永远找不到。

我认为您最好在各州之间进行正常的迭代。

另外,您确定8x8以外的其他电路板是否可解决? 通过检查,不是2x2,不是3x3,不是4x4。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM