Rustでエイト・クイーン
本業が一段落したので、またRustの勉強。本読むだけではつまらないし、経験値上がらないので、プログラム書いてみる。テーマは「エイト・クイーン」。いきなりRustで書き下ろす自信は無いので、Javaで書いてそれをRustへ書き直してみた。よって、Rustの良さを生かし切れていないのは十分承知。それでもいろいろ知見が得られた。
use std::time::Instant; // Queenの数 = チェス盤の大きさ const NUM_OF_QUEEN: i32 = 8; // 見つけた解の数 static mut NUM_OF_ANSWER: i32 = 0; fn main() { let mut buff = [0; NUM_OF_QUEEN as usize]; let st = Instant::now(); place(0, &mut buff); let et = Instant::now(); println!("elapsed: {:?}", et - st); } /// 指定された列でQueenが置ける行を探す fn place(col: i32, buff: &mut [i32]) { // N-Queen完成 if col == NUM_OF_QUEEN { unsafe { NUM_OF_ANSWER += 1; println!("{}:{:?}", NUM_OF_ANSWER, buff); } return; } // Queenを置ける場所を探す for row in 0..NUM_OF_QUEEN { if check_can_place_queen(col, row, buff) { // おける buff[col as usize] = row; // 次の列へ place(col + 1, buff) } } } /// col, rowで指定された盤面の場所にQueenが置けるかチェックする fn check_can_place_queen(col: i32, row: i32, buff: &[i32]) -> bool { // 左端列は自由における if col == 0 { return true; } // この列の左側の配置状況から判断する for i in 0..col { // 同じ行に配置済 if buff[i as usize] == row { return false; } // 左上斜め方向に配置済 if buff[i as usize] == (row - col + i) { return false; } // 左下斜め方向に配置済 if buff[i as usize] == (row + col - i) { return false; } } // ここに配置できる return true; }
外部変数(グローバル変数)が使いにくい。シングルスレッドで実行するから競合する恐れは無いんだけど、コンパイラはそれを知らないからunsafeを要求する。
配列の添字はusize。i32ではダメ。as usizeが必要。
再帰呼び出しでbuffをcloneして渡すのかと思ったけど、&mutでいけた。所有権とか、参照の規則から見て良いんだろうか。
ちなみに最初に作ったJavaプログラムはこんな感じ。
package com.example; public class NQueen { // Queenの数 = チェス盤の大きさ private static final int NUM_OF_QUEEN = 8; // 見つけた解の数 private static int numOfAnswer = 0; public static void main(String[] args) { int[] buff = new int[NUM_OF_QUEEN]; long st = System.currentTimeMillis(); place(0, buff); long et = System.currentTimeMillis(); System.out.printf("time=%,d(ms)\n", et - st); } /** * 指定された列でQueenが置ける行を探す * @param col */ private static void place(int col, int[] buff) { if (col == NUM_OF_QUEEN) { // N-Queen完成 ++numOfAnswer; System.out.printf("%,d:[", numOfAnswer); for (int i = 0; i < NUM_OF_QUEEN; i++) { if (i > 0) { System.out.print(", "); } System.out.print(buff[i]); } System.out.println("]"); return; } // Queenを置ける場所を探す for (int row = 0; row < NUM_OF_QUEEN; row++) { if (checkCanPlaceQueen(col, row, buff)) { // 配置できる buff[col] = row; // 次の列へ place(col + 1, buff); } } } /** * col, rowで指定された盤面の場所にQueenが置けるかチェックする * @param col * @param row * @param buff * @return */ private static boolean checkCanPlaceQueen(int col, int row, int[] buff) { // 左端列は自由における if (col == 0) { return true; } // この列の左側の配置状況から判断する for (int i = 0; i < col; i++) { // 同じ行に配置済 if (buff[i] == row) { return false; } // 左上斜め方向に配置済 if (buff[i] == (row - col + i)) { return false; } // 左下斜め方向に配置済 if (buff[i] == (row + col - i)) { return false; } } // ここに配置できる return true; } }