Skip to content

Commit d1733ff

Browse files
Peter554samueltardieu
authored andcommitted
feat: add example for bidirectional BFS
On my machine: Corner to Corner ================ BFS took 5.821209ms Bidirectional BFS took 7.437667ms Center to Corner ================ BFS took 6.005333ms Bidirectional BFS took 4.5025ms
1 parent 29c382a commit d1733ff

2 files changed

Lines changed: 158 additions & 0 deletions

File tree

examples/bfs_bidirectional.rs

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
//! This example demonstrates the BFS bidirectional algorithm,
2+
//! and compares it with the regular BFS algorithm.
3+
4+
use pathfinding::prelude::{bfs, bfs_bidirectional};
5+
use std::ops::Add;
6+
use std::time::Instant;
7+
8+
const SIZE: isize = 64;
9+
const BOTTOM_LEFT: P = P(0, 0);
10+
const TOP_RIGHT: P = P(SIZE, SIZE);
11+
const CENTER: P = P(SIZE / 2, SIZE / 2);
12+
13+
#[derive(Debug, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
14+
struct P(isize, isize);
15+
16+
impl Add for P {
17+
type Output = Self;
18+
19+
fn add(self, other: Self) -> Self {
20+
P(self.0 + other.0, self.1 + other.1)
21+
}
22+
}
23+
24+
fn successors(p: &P) -> Vec<P> {
25+
[P(0, 1), P(0, -1), P(1, 0), P(-1, 0)]
26+
.into_iter()
27+
.map(|delta| p.clone() + delta)
28+
.filter(|p| p.0 >= 0 && p.0 <= SIZE && p.1 >= 0 && p.1 <= SIZE)
29+
.collect()
30+
}
31+
32+
fn main() {
33+
run_corner_to_corner();
34+
run_center_to_corner();
35+
}
36+
37+
/// Corner to corner:
38+
/// =================
39+
///
40+
/// In this case both algorithms will perform similarly.
41+
/// In fact, regular BFS will perform slightly better, since the algorithm is slightly simpler.
42+
///
43+
/// We can understand this in terms of the number of points that need to be searched in order to reach
44+
/// the goal. In the below diagrams this corresponds to the area covered in the final snapshot.
45+
///
46+
/// In both cases every point gets searched - the entire area is filled. For this reason we can intuitively see that
47+
/// regular BFS and bidirectional BFS will perform similarly.
48+
///
49+
/// Regular BFS:
50+
/// ============
51+
///
52+
/// $---------$ $---------$ $---------$ $---------$ $---------$ $---------$
53+
/// | G| | G| | G| | G| |FFFFFFF G| |FFFFFFFFG|
54+
/// | | | | | | | | |FFFFFFFF | |FFFFFFFFF|
55+
/// | | | | | | | | |FFFFFFFFF| |FFFFFFFFF|
56+
/// | | | | | | | | |FFFFFFFFF| |FFFFFFFFF|
57+
/// | | => | | => | | => | | => ... => |FFFFFFFFF| => |FFFFFFFFF|
58+
/// | | | | | | |F | |FFFFFFFFF| |FFFFFFFFF|
59+
/// | | | | |F | |FF | |FFFFFFFFF| |FFFFFFFFF|
60+
/// | | |F | |FF | |FFF | |FFFFFFFFF| |FFFFFFFFF|
61+
/// |S | |SF | |SFF | |SFFF | |SFFFFFFFF| |SFFFFFFFF|
62+
/// $---------$ $---------$ $---------$ $---------$ $---------$ $---------$
63+
///
64+
/// Bidirectional BFS:
65+
/// ==================
66+
///
67+
/// $---------$ $---------$ $---------$ $---------$ $---------$ $---------$
68+
/// | G| | BG| | BBG| | BBBG| | BBBBBBBG| |FBBBBBBBG|
69+
/// | | | B| | BB| | BBB| |F BBBBBBB| |FFBBBBBBB|
70+
/// | | | | | B| | BB| |FF BBBBBB| |FFFBBBBBB|
71+
/// | | | | | | | B| |FFF BBBBB| |FFFFBBBBB|
72+
/// | | => | | => | | => | | => ... => |FFFF BBBB| => |FFFFFBBBB|
73+
/// | | | | | | |F | |FFFFF BBB| |FFFFFFBBB|
74+
/// | | | | |F | |FF | |FFFFFF BB| |FFFFFFFBB|
75+
/// | | |F | |FF | |FFF | |FFFFFFF B| |FFFFFFFFB|
76+
/// |S | |SF | |SFF | |SFFF | |SFFFFFFF | |SFFFFFFFF|
77+
/// $---------$ $---------$ $---------$ $---------$ $---------$ $---------$
78+
fn run_corner_to_corner() {
79+
let instant = Instant::now();
80+
bfs(&BOTTOM_LEFT, &successors, |p| *p == TOP_RIGHT);
81+
let duration_bfs = instant.elapsed();
82+
83+
let instant = Instant::now();
84+
bfs_bidirectional(&BOTTOM_LEFT, &TOP_RIGHT, successors, successors);
85+
let duration_bfs_bidirectional = instant.elapsed();
86+
87+
print!(
88+
"
89+
Corner to Corner
90+
================
91+
BFS took {duration_bfs:?}
92+
Bidirectional BFS took {duration_bfs_bidirectional:?}
93+
"
94+
);
95+
}
96+
97+
/// Center to corner:
98+
/// =================
99+
///
100+
/// In this case bidirectional BFS will outperform regular BFS.
101+
///
102+
/// We can understand this in terms of the number of points that need to be searched in order to reach
103+
/// the goal. In the below diagrams this corresponds to the area covered in the final snapshot.
104+
///
105+
/// In this case for the regular BFS every point still needs to be searched - again, the entire area is filled.
106+
/// However, for the bidirectional BFS some points remain unsearched - the entire area is not filled. For this
107+
/// reason we can intuitively see that bidirectional BFS will outperform regular BFS here.
108+
///
109+
/// Regular BFS:
110+
/// ============
111+
///
112+
/// $---------$ $---------$ $---------$ $---------$ $---------$
113+
/// | G| | G| | G| | FFFFFFFG| |FFFFFFFFG|
114+
/// | | | | | | |FFFFFFFFF| |FFFFFFFFF|
115+
/// | | | F | | F | |FFFFFFFFF| |FFFFFFFFF|
116+
/// | | | FFF | | FFF | |FFFFFFFFF| |FFFFFFFFF|
117+
/// | S | => | FFSFF | => | FFSFF | => ... => |FFFFSFFFF| => |FFFFSFFFF|
118+
/// | | | FFF | | FFF | |FFFFFFFFF| |FFFFFFFFF|
119+
/// | | | F | | F | |FFFFFFFFF| |FFFFFFFFF|
120+
/// | | | | | | |FFFFFFFFF| |FFFFFFFFF|
121+
/// | | | | | | | FFFFFFF | |FFFFFFFFF|
122+
/// $---------$ $---------$ $---------$ $---------$ $---------$
123+
///
124+
/// Bidirectional BFS:
125+
/// ==================
126+
///
127+
/// $---------$ $---------$ $---------$ $---------$ $---------$
128+
/// | G| | BG| | BBG| | BBBG| | FBBBG|
129+
/// | | | B| | BB| | F BBB| | FFFBBB|
130+
/// | | | | | F | | FFF B| | FFFFFBB|
131+
/// | | | F | | FFF | | FFFFF | | FFFFFFFB|
132+
/// | S | => | FSF | => | FFSFF | => | FFFSFFF | => |FFFFSFFFF|
133+
/// | | | F | | FFF | | FFFFF | | FFFFFFF |
134+
/// | | | | | F | | FFF | | FFFFF |
135+
/// | | | | | | | F | | FFF |
136+
/// | | | | | | | | | F |
137+
/// $---------$ $---------$ $---------$ $---------$ $---------$
138+
fn run_center_to_corner() {
139+
let instant = Instant::now();
140+
bfs(&CENTER, &successors, |p| *p == TOP_RIGHT);
141+
let duration_bfs = instant.elapsed();
142+
143+
let instant = Instant::now();
144+
bfs_bidirectional(&CENTER, &TOP_RIGHT, successors, successors);
145+
let duration_bfs_bidirectional = instant.elapsed();
146+
147+
print!(
148+
"
149+
Center to Corner
150+
================
151+
BFS took {duration_bfs:?}
152+
Bidirectional BFS took {duration_bfs_bidirectional:?}
153+
"
154+
);
155+
}

src/directed/bfs.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,9 @@ where
160160
/// let result = bfs_bidirectional(&(1, 1), &(4, 6), SUCCESSORS, SUCCESSORS);
161161
/// assert_eq!(result.expect("no path found").len(), 5);
162162
/// ```
163+
///
164+
/// Find also a more interesting example, comparing regular
165+
/// and bidirectional BFS [here](https://github.com/evenfurther/pathfinding/blob/main/examples/bfs_bidirectional.rs).
163166
#[allow(clippy::missing_panics_doc)]
164167
pub fn bfs_bidirectional<N, FNS, FNP, IN>(
165168
start: &N,

0 commit comments

Comments
 (0)