auto merge of #14063 : TeXitoi/rust/shootout-meteor-improvement, r=pcwalton

- 5-10% of raw speedup
- parallelization of the search
This commit is contained in:
bors 2014-05-09 18:36:29 -07:00
commit fb8773463a

View File

@ -1,4 +1,4 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
@ -8,11 +8,18 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(phase)]
#[phase(syntax)] extern crate green;
extern crate sync;
use sync::Arc;
green_start!(main)
//
// Utilities.
//
// returns an infinite iterator of repeated applications of f to x,
// i.e. [x, f(x), f(f(x)), ...], as haskell iterate function.
fn iterate<'a, T>(x: T, f: |&T|: 'a -> T) -> Iterate<'a, T> {
@ -93,7 +100,7 @@ fn transform(piece: Vec<(int, int)> , all: bool) -> Vec<Vec<(int, int)>> {
// Takes a piece with minimum coordinate (0, 0) (as generated by
// transform). Returns the corresponding mask if p translated by (dy,
// dx) is on the board.
fn mask(dy: int, dx: int, id: uint, p: &[(int, int)]) -> Option<u64> {
fn mask(dy: int, dx: int, id: uint, p: &Vec<(int, int)>) -> Option<u64> {
let mut m = 1 << (50 + id);
for &(y, x) in p.iter() {
let x = x + dx + (y + (dy % 2)) / 2;
@ -105,7 +112,7 @@ fn mask(dy: int, dx: int, id: uint, p: &[(int, int)]) -> Option<u64> {
Some(m)
}
// Makes every possible masks. masks[id][i] correspond to every
// Makes every possible masks. masks[i][id] correspond to every
// possible masks for piece with identifier id with minimum coordinate
// (i/5, i%5).
fn make_masks() -> Vec<Vec<Vec<u64> > > {
@ -120,168 +127,182 @@ fn make_masks() -> Vec<Vec<Vec<u64> > > {
vec!((0,0),(0,1),(0,2),(1,0),(1,2)),
vec!((0,0),(0,1),(0,2),(1,2),(1,3)),
vec!((0,0),(0,1),(0,2),(0,3),(1,2)));
let mut res = Vec::new();
for (id, p) in pieces.move_iter().enumerate() {
// To break the central symetry of the problem, every
// transformation must be taken except for one piece (piece 3
// here).
let trans = transform(p, id != 3);
let mut cur_piece = Vec::new();
for dy in range(0, 10) {
for dx in range(0, 5) {
let masks =
trans.iter()
.filter_map(|t| mask(dy, dx, id, t.as_slice()))
.collect();
cur_piece.push(masks);
}
}
res.push(cur_piece);
}
res
// To break the central symetry of the problem, every
// transformation must be taken except for one piece (piece 3
// here).
let transforms: Vec<Vec<Vec<(int, int)>>> =
pieces.move_iter().enumerate()
.map(|(id, p)| transform(p, id != 3))
.collect();
range(0, 50).map(|yx| {
transforms.iter().enumerate().map(|(id, t)| {
t.iter().filter_map(|p| mask(yx / 5, yx % 5, id, p)).collect()
}).collect()
}).collect()
}
// Check if all coordinates can be covered by an unused piece and that
// all unused piece can be placed on the board.
fn is_board_unfeasible(board: u64, masks: &[Vec<Vec<u64> > ]) -> bool {
fn is_board_unfeasible(board: u64, masks: &Vec<Vec<Vec<u64>>>) -> bool {
let mut coverable = board;
for i in range(0, 50).filter(|&i| board & 1 << i == 0) {
for (cur_id, pos_masks) in masks.iter().enumerate() {
if board & 1 << (50 + cur_id) != 0 {continue;}
for &cur_m in pos_masks.get(i as uint).iter() {
if cur_m & board == 0 {coverable |= cur_m;}
for (i, masks_at) in masks.iter().enumerate() {
if board & 1 << i != 0 { continue; }
for (cur_id, pos_masks) in masks_at.iter().enumerate() {
if board & 1 << (50 + cur_id) != 0 { continue; }
for &cur_m in pos_masks.iter() {
if cur_m & board != 0 { continue; }
coverable |= cur_m;
// if every coordinates can be covered and every
// piece can be used.
if coverable == (1 << 60) - 1 { return false; }
}
}
if coverable & (1 << i) == 0 {return true;}
if coverable & 1 << i == 0 { return true; }
}
// check if every coordinates can be covered and every piece can
// be used.
coverable != (1 << 60) - 1
true
}
// Filter the masks that we can prove to result to unfeasible board.
fn filter_masks(masks: &[Vec<Vec<u64> > ]) -> Vec<Vec<Vec<u64> > > {
masks.iter().map(
|p| p.iter().map(
|p| p.iter()
.map(|&m| m)
fn filter_masks(masks: &mut Vec<Vec<Vec<u64>>>) {
for i in range(0, masks.len()) {
for j in range(0, masks.get(i).len()) {
*masks.get_mut(i).get_mut(j) =
masks.get(i).get(j).iter().map(|&m| m)
.filter(|&m| !is_board_unfeasible(m, masks))
.collect())
.collect())
.collect()
.collect();
}
}
}
// Gets the identifier of a mask.
fn get_id(m: u64) -> u8 {
for id in range(0, 10) {
if m & (1 << (id + 50)) != 0 {return id as u8;}
for id in range(0u8, 10) {
if m & (1 << (id + 50)) != 0 {return id;}
}
fail!("{:016x} does not have a valid identifier", m);
}
// Converts a list of mask to a ~str.
fn to_utf8(raw_sol: &List<u64>) -> ~str {
let mut sol: Vec<u8> = Vec::from_elem(50, '.' as u8);
fn to_vec(raw_sol: &List<u64>) -> Vec<u8> {
let mut sol = Vec::from_elem(50, '.' as u8);
for &m in raw_sol.iter() {
let id = get_id(m);
for i in range(0, 50) {
let id = '0' as u8 + get_id(m);
for i in range(0u, 50) {
if m & 1 << i != 0 {
*sol.get_mut(i as uint) = '0' as u8 + id;
*sol.get_mut(i) = id;
}
}
}
std::str::from_utf8(sol.as_slice()).unwrap().to_owned()
sol
}
// Prints a solution in ~str form.
fn print_sol(sol: &str) {
for (i, c) in sol.chars().enumerate() {
fn print_sol(sol: &Vec<u8>) {
for (i, c) in sol.iter().enumerate() {
if (i) % 5 == 0 { println!(""); }
if (i + 5) % 10 == 0 { print!(" "); }
print!("{} ", c);
print!("{} ", *c as char);
}
println!("");
}
// The data managed during the search
struct Data {
// If more than stop_after is found, stop the search.
stop_after: int,
// Number of solution found.
nb: int,
// Lexicographically minimal solution found.
min: ~str,
min: Vec<u8>,
// Lexicographically maximal solution found.
max: ~str
max: Vec<u8>
}
impl Data {
fn new() -> Data {
Data {nb: 0, min: vec!(), max: vec!()}
}
fn reduce_from(&mut self, other: Data) {
self.nb += other.nb;
let Data { min: min, max: max, ..} = other;
if min < self.min { self.min = min; }
if max > self.max { self.max = max; }
}
}
// Records a new found solution. Returns false if the search must be
// stopped.
fn handle_sol(raw_sol: &List<u64>, data: &mut Data) -> bool {
fn handle_sol(raw_sol: &List<u64>, data: &mut Data) {
// because we break the symetry, 2 solutions correspond to a call
// to this method: the normal solution, and the same solution in
// reverse order, i.e. the board rotated by half a turn.
data.nb += 2;
let sol1 = to_utf8(raw_sol);
let sol2: ~str = sol1.chars().rev().collect();
let sol1 = to_vec(raw_sol);
let sol2: Vec<u8> = sol1.iter().rev().map(|x| *x).collect();
if data.nb == 2 {
data.min = sol1.clone();
data.max = sol1.clone();
}
if sol1 < data.min {data.min = sol1.clone();}
if sol2 < data.min {data.min = sol2.clone();}
if sol1 > data.max {data.max = sol1;}
if sol2 > data.max {data.max = sol2;}
data.nb < data.stop_after
if sol1 < data.min {data.min = sol1;}
else if sol1 > data.max {data.max = sol1;}
if sol2 < data.min {data.min = sol2;}
else if sol2 > data.max {data.max = sol2;}
}
// Search for every solutions. Returns false if the search was
// stopped before the end.
fn search(
masks: &[Vec<Vec<u64> > ],
masks: &Vec<Vec<Vec<u64>>>,
board: u64,
mut i: int,
mut i: uint,
cur: List<u64>,
data: &mut Data)
-> bool
{
// Search for the lesser empty coordinate.
while board & (1 << i) != 0 && i < 50 {i += 1;}
// the board is full: a solution is found.
if i >= 50 {return handle_sol(&cur, data);}
let masks_at = masks.get(i);
// for every unused piece
for id in range(0, 10).filter(|id| board & (1 << (id + 50)) == 0) {
for id in range(0u, 10).filter(|id| board & (1 << (id + 50)) == 0) {
// for each mask that fits on the board
for &m in masks[id as uint].get(i as uint)
.iter()
.filter(|&m| board & *m == 0) {
for &m in masks_at.get(id).iter().filter(|&m| board & *m == 0) {
// This check is too costy.
//if is_board_unfeasible(board | m, masks) {continue;}
if !search(masks, board | m, i + 1, Cons(m, &cur), data) {
return false;
}
search(masks, board | m, i + 1, Cons(m, &cur), data);
}
}
return true;
}
fn par_search(masks: Vec<Vec<Vec<u64>>>) -> Data {
let masks = Arc::new(masks);
let (tx, rx) = channel();
// launching the search in parallel on every masks at minimum
// coordinate (0,0)
for &m in masks.get(0).iter().flat_map(|masks_pos| masks_pos.iter()) {
let masks = masks.clone();
let tx = tx.clone();
spawn(proc() {
let mut data = Data::new();
search(&*masks, m, 1, Cons(m, &Nil), &mut data);
tx.send(data);
});
}
// collecting the results
drop(tx);
let mut data = rx.recv();
for d in rx.iter() { data.reduce_from(d); }
data
}
fn main () {
let args = std::os::args();
let args = args.as_slice();
let stop_after = if args.len() <= 1 {
2098
} else {
from_str(args[1]).unwrap()
};
let masks = make_masks();
let masks = filter_masks(masks.as_slice());
let mut data = Data {stop_after: stop_after, nb: 0, min: "".to_owned(), max: "".to_owned()};
search(masks.as_slice(), 0, 0, Nil, &mut data);
let mut masks = make_masks();
filter_masks(&mut masks);
let data = par_search(masks);
println!("{} solutions found", data.nb);
print_sol(data.min);
print_sol(data.max);
print_sol(&data.min);
print_sol(&data.max);
println!("");
}