优化N皇后拼图

时间:2011-11-05 18:24:18

标签: java optimization

我正在尝试解决在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(); 
        }
}

}

3 个答案:

答案 0 :(得分:1)

评论会有所帮助:)

而不是重新创建你的冲突集和你最严重的冲突&#34;女王的一切,你能创建一次,然后只更新已更改的行/列吗?

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

我伪造了一下,看看哪些语句被执行得最多,结果发现chooseConflictQueen中的内部循环语句被执行得最多。我试图插入一个休息来拉动第一个冲突女王如果找到,但它似乎没有多大帮助。

Stats

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

More Stats

我意识到我只有10次跑步,这在统计上并不足以在统计上有效,但是嘿。

所以添加休息似乎没有帮助。我认为建设性的解决方案可能会更快,但随机性将再次使其更难检查。

答案 1 :(得分:0)

您的方法很好:具有最小冲突约束的本地搜索算法。我建议尝试改善你的初始状态。而不是随机放置所有皇后,每列1个,尝试放置它们,以便最大限度地减少冲突的数量。一个例子是尝试根据前一个的位置...或者前两个的位置来给你下一个女王...然后你本地搜索将有较少的问题列来处理。

答案 2 :(得分:0)

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

我认为你最好通过各州进行正常迭代。

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