rust-計算象棋馬在棋盤上任意一點步數的兩種方法

簡述

路徑計算又兩種方法,一種經過迭代,另外一種經過隊列實現。算法

迭代方案

從起始點開始遍歷,一個點周圍又八個next_point,去除超出棋盤的點後,選擇遍歷的點知足,該點未被遍歷或該點以前遍歷路徑所需的步數大於當前路徑對於的步數。性能

隊列方案

將起始點push到隊列中。當隊列不爲空時,每次循環從隊列中pop一個點。若是那八個next_point知足條件:1. 點不超出棋盤;2. 該點未被遍歷或該點以前遍歷路徑所需的步數大於當前路徑對於的步數。測試

實現

lib.rs

pub mod checkerboard;
pub mod checkerboardq;

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn for_horse() {
        let mut board = super::checkerboard::CheckerBoard::new();

        board.calculate((2,3));
        println!("{}",board.get_count());
        assert_eq!(board.get(&(2,3)),0);
    }

    #[test]
    fn for_horse_query() {
        let mut board = super::checkerboardq::CheckerBoardQ::new();
        board.calculate((2,3));
        board.print_board();
        println!("{}",board.get_count());
        assert_eq!(board.get(&(2,3)),0);
    }
}

checkerboard.rs

//迭代方案
#[derive(Debug)]
pub struct CheckerBoard {
    board: Vec<Vec<isize>>,//存放路徑對應步數
    count:usize,//記錄遍歷次數
}

impl CheckerBoard {
    // add code here
    pub fn new() -> CheckerBoard {
        let mut board:Vec<Vec<isize>> = Vec::with_capacity(10);
        let count:usize=0;
        for _ in 0..10 {
            board.push(vec![-1;9]);
        }
        CheckerBoard{
            board,
            count,
        }
    }

    pub fn get(&self,point:&(usize,usize)) -> isize{
        self.board[point.0][point.1]
    }

    pub fn set(&mut self,point:&(usize,usize),length:isize){
        self.board[point.0][point.1] = length;
    }

    pub fn print_board(&self){
        for i in 0..10{
            println!("{:?}",self.board[i]);
        }
    }
    pub fn calculate(&mut self,point:(usize,usize)){
        self.travel(point,-1);
    }
    fn travel(&mut self,point:(usize,usize),length:isize){
        if point.0>9 || point.1>8{
            return;
        }
        self.count = self.count + 1;
        let _length = self.get(&point);
        if _length>=0 && _length<=length+1{
            return;
        }
        self.count = self.count+1;
        let _length = length+1;
        self.set(&point,length+1);
        //如下代碼這麼醜是爲了減小bool計算次數
        if point.0>1{
            if point.1>1{
                self.travel((point.0-1,point.1-2),_length);
                self.travel((point.0-1,point.1+2),_length);
                self.travel((point.0-2,point.1-1),_length);
                self.travel((point.0-2,point.1+1),_length);
                self.travel((point.0+1,point.1-2),_length);
                self.travel((point.0+2,point.1-1),_length);
            }else if point.1>0 {                
                self.travel((point.0-1,point.1+2),_length);
                self.travel((point.0-2,point.1-1),_length);
                self.travel((point.0-2,point.1+1),_length);
                self.travel((point.0+2,point.1-1),_length);
            }
        }else if point.0>0{
            if point.1>1{
                self.travel((point.0-1,point.1-2),_length);
                self.travel((point.0-1,point.1+2),_length);
                self.travel((point.0+1,point.1-2),_length);
                self.travel((point.0+2,point.1-1),_length);
            }else if point.1>0{
                self.travel((point.0-1,point.1+2),_length);
                self.travel((point.0+2,point.1-1),_length);
            }
        }
        self.travel((point.0+1,point.1+2),_length);
        self.travel((point.0+2,point.1+1),_length);
        
    }

    pub fn get_count(&self) -> usize{
        self.count
    }
    
}

checkerboardq.rs

#[derive(Debug)]
struct Queue<T> {
    qdata: Vec<T>,
}

impl <T> Queue<T> {
    pub fn new() -> Self {
        Queue{qdata: Vec::new()}
    }

    pub fn len(&self)->usize{
        self.qdata.len()
    }

    pub fn push(&mut self, item:T) {
        self.qdata.push(item);
    }

    pub fn pop(&mut self) -> T{
        self.qdata.remove(0)
    }
}

#[derive(Debug)]
pub struct CheckerBoardQ {
    board: Vec<Vec<isize>>,
    count:usize,
    point_query:Queue<(isize,isize)>,
}
impl CheckerBoardQ {
    // add code here
    pub fn new() -> CheckerBoardQ {
        let mut board:Vec<Vec<isize>> = Vec::with_capacity(10);
        let count:usize=0;
        let point_query:Queue<(isize,isize)> = Queue::new();
        for _ in 0..10 {
            board.push(vec![-1;9]);
        }
        CheckerBoardQ{
            board,
            count,
            point_query,
        }
    }
    fn set(&mut self,point:&(isize,isize),length:isize){
        self.board[point.0 as usize ][point.1 as usize] = length;
    }
    pub fn get(&self,point:&(usize,usize)) -> isize{
        self.board[point.0][point.1]
    }
    fn _get(&self,point:&(isize,isize)) -> isize{
        self.board[point.0 as usize][point.1 as usize]
    }
    pub fn print_board(&self){
        for i in 0..10{
            println!("{:?}",self.board[i]);
        }
    }
    pub fn calculate(&mut self,point:(usize,usize)){
        assert!(point.0<10 && point.1<9);
        let i_point = (point.0 as isize,point.1 as isize);
        self.set(&i_point,0);
        self.point_query.push(i_point);
        while self.point_query.len()>0 {
            self.count = self.count + 1;
            let _point = self.point_query.pop();
            self.travel(_point);
        }

        
    }
    fn update_query(&mut self,length:isize,next_point:(isize,isize)){
        let length = length+1;
        let _length = self._get(&next_point);
        if _length<0 || _length>length {
            self.set(&next_point,length);
            self.point_query.push(next_point);
        }
    }
    fn travel(&mut self,point:(isize,isize))  {
        //如下代碼看上去簡潔許多,但實際bool計算次數較上面方案多
        let mut _next_point_query = vec![
            (point.0-1,point.1-2),(point.0-1,point.1+2),
            (point.0+1,point.1-2),(point.0+1,point.1+2),
            (point.0-2,point.1-1),(point.0-2,point.1+1),
            (point.0+2,point.1-1),(point.0+2,point.1+1)].into_iter();
        let length = self._get(&point);
        while let Some(_point) = _next_point_query.next() {
            if _point.0<0||_point.0>9||_point.1<0||_point.1>8{
                continue;
            }
            self.update_query(length,_point);
        }
    }
    pub fn get_count(&self) -> usize {
        self.count
    }
    
}

測試結果

cargo test -- --nocapture --test-threads=1spa

測試結果很明顯,迭代方案性能遠遠低於隊列方案。這一點其實很是好理解,雖然,兩個方案的核心判斷條件相同,但迭代方案是從一個點出發,將其知足核心判斷條件的路徑所有遍歷一遍。而隊列方案每次只前進一步,就能夠提早結束一大部分確定會被淘汰掉的遍歷路徑。設計

小結

上述代碼中設計到的rust語法很少。比較有意思的是八個next_point是否在棋盤上的判斷算法,要兼顧優雅和高效。我所用的兩個算法基本算是這種狀況的極端。第一種代碼一大坨,可是遍歷一個點的平均bool計算次數爲3.7,第二種明顯是每一個next_point點都判斷一次,爲8 。code

相關文章
相關標籤/搜索