Skip to content

Commit 9977ab3

Browse files
Implement Adaptive Edge Exploration (AEE) algorithm
1 parent cca0847 commit 9977ab3

File tree

2 files changed

+186
-2
lines changed

2 files changed

+186
-2
lines changed

Cargo.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,5 @@ name = "aoc-2019-d24"
3939
path = "adventofcode/2019/d24/d24.rs"
4040

4141
[[bin]]
42-
name = "dijkstra"
43-
path = "graph/dijkstra.rs"
42+
name = "adaptive_edge_exploration"
43+
path = "graph/adaptive_edge_exploration.rs"

graph/adaptive_edge_exploration.rs

+184
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
use std::collections::{HashMap, VecDeque};
2+
use rand::Rng;
3+
4+
// Adaptive Edge Exploration (AEE) Algorithm
5+
//
6+
// This algorithm explores a graph by adaptively choosing edges based on their
7+
// weights and the graph's structure. It aims to find paths that balance between
8+
// shortest distance and interesting graph features, introducing some randomness
9+
// to explore diverse paths.
10+
11+
// Define the Graph structure
12+
struct Graph {
13+
edges: HashMap<usize, Vec<(usize, usize)>>,
14+
}
15+
16+
// Define the Node structure for the exploration queue
17+
#[derive(Clone, Eq, PartialEq)]
18+
struct Node {
19+
vertex: usize,
20+
path: Vec<usize>,
21+
total_cost: usize,
22+
interest_score: f64,
23+
}
24+
25+
impl Graph {
26+
// Constructor for Graph
27+
fn new() -> Self {
28+
Graph {
29+
edges: HashMap::new(),
30+
}
31+
}
32+
33+
// Method to add an edge to the graph
34+
fn add_edge(&mut self, from: usize, to: usize, cost: usize) {
35+
self.edges.entry(from).or_insert(Vec::new()).push((to, cost));
36+
self.edges.entry(to).or_insert(Vec::new()); // Ensure all vertices are in the map
37+
}
38+
39+
// Adaptive Edge Exploration (AEE) algorithm
40+
fn adaptive_edge_exploration(&self, start: usize, end: usize) -> Option<(Vec<usize>, usize)> {
41+
let mut rng = rand::thread_rng();
42+
let mut queue = VecDeque::new();
43+
let mut visited = HashMap::new();
44+
45+
queue.push_back(Node {
46+
vertex: start,
47+
path: vec![start],
48+
total_cost: 0,
49+
interest_score: 0.0,
50+
});
51+
52+
while let Some(current) = queue.pop_front() {
53+
if current.vertex == end {
54+
return Some((current.path, current.total_cost));
55+
}
56+
57+
if let Some(&prev_cost) = visited.get(&current.vertex) {
58+
if current.total_cost >= prev_cost {
59+
continue;
60+
}
61+
}
62+
63+
visited.insert(current.vertex, current.total_cost);
64+
65+
if let Some(neighbors) = self.edges.get(&current.vertex) {
66+
let mut adaptive_neighbors = neighbors.clone();
67+
adaptive_neighbors.sort_by(|a, b| {
68+
let a_score = self.calculate_interest_score(a.0, &current);
69+
let b_score = self.calculate_interest_score(b.0, &current);
70+
b_score.partial_cmp(&a_score).unwrap()
71+
});
72+
73+
for &(neighbor, edge_cost) in adaptive_neighbors.iter() {
74+
if !visited.contains_key(&neighbor) || current.total_cost + edge_cost < visited[&neighbor] {
75+
let mut new_path = current.path.clone();
76+
new_path.push(neighbor);
77+
let new_interest_score = self.calculate_interest_score(neighbor, &current);
78+
79+
queue.push_back(Node {
80+
vertex: neighbor,
81+
path: new_path,
82+
total_cost: current.total_cost + edge_cost,
83+
interest_score: new_interest_score,
84+
});
85+
}
86+
}
87+
}
88+
89+
// Introduce some randomness to explore different paths
90+
if rng.gen::<f64>() < 0.1 {
91+
queue.make_contiguous().shuffle(&mut rng);
92+
}
93+
}
94+
95+
None // No path found
96+
}
97+
98+
// Calculate interest score for a neighbor
99+
fn calculate_interest_score(&self, neighbor: usize, current: &Node) -> f64 {
100+
let neighbor_degree = self.edges.get(&neighbor).map_or(0, |edges| edges.len());
101+
let path_length = current.path.len() as f64;
102+
let total_cost = current.total_cost as f64;
103+
104+
// Combine factors to create an interest score
105+
(neighbor_degree as f64).sqrt() + (1.0 / (path_length + 1.0)) + (1.0 / (total_cost + 1.0))
106+
}
107+
}
108+
109+
#[cfg(test)]
110+
mod tests {
111+
use super::*;
112+
113+
#[test]
114+
fn test_simple_path() {
115+
let mut graph = Graph::new();
116+
graph.add_edge(0, 1, 4);
117+
graph.add_edge(1, 2, 3);
118+
graph.add_edge(2, 3, 2);
119+
120+
let result = graph.adaptive_edge_exploration(0, 3);
121+
assert!(result.is_some());
122+
let (path, cost) = result.unwrap();
123+
assert_eq!(path, vec![0, 1, 2, 3]);
124+
assert_eq!(cost, 9);
125+
}
126+
127+
#[test]
128+
fn test_multiple_paths() {
129+
let mut graph = Graph::new();
130+
graph.add_edge(0, 1, 4);
131+
graph.add_edge(0, 2, 1);
132+
graph.add_edge(1, 3, 1);
133+
graph.add_edge(2, 1, 2);
134+
graph.add_edge(2, 3, 5);
135+
136+
let result = graph.adaptive_edge_exploration(0, 3);
137+
assert!(result.is_some());
138+
let (path, cost) = result.unwrap();
139+
assert!(path.starts_with(&[0]) && path.ends_with(&[3]));
140+
assert!(cost <= 6); // The cost should be at most 6 (0->2->1->3 or 0->1->3)
141+
}
142+
143+
#[test]
144+
fn test_no_path() {
145+
let mut graph = Graph::new();
146+
graph.add_edge(0, 1, 4);
147+
graph.add_edge(1, 2, 3);
148+
graph.add_edge(3, 4, 2);
149+
150+
let result = graph.adaptive_edge_exploration(0, 4);
151+
assert_eq!(result, None);
152+
}
153+
154+
#[test]
155+
fn test_graph_with_cycle() {
156+
let mut graph = Graph::new();
157+
graph.add_edge(0, 1, 1);
158+
graph.add_edge(1, 2, 2);
159+
graph.add_edge(2, 3, 3);
160+
graph.add_edge(3, 1, 1);
161+
graph.add_edge(3, 4, 4);
162+
163+
let result = graph.adaptive_edge_exploration(0, 4);
164+
assert!(result.is_some());
165+
let (path, cost) = result.unwrap();
166+
assert!(path.starts_with(&[0]) && path.ends_with(&[4]));
167+
assert!(cost <= 10); // The cost should be at most 10 (0->1->2->3->4)
168+
}
169+
170+
#[test]
171+
fn test_large_graph() {
172+
let mut graph = Graph::new();
173+
for i in 0..1000 {
174+
graph.add_edge(i, i + 1, 1);
175+
}
176+
graph.add_edge(0, 1000, 999);
177+
178+
let result = graph.adaptive_edge_exploration(0, 1000);
179+
assert!(result.is_some());
180+
let (path, cost) = result.unwrap();
181+
assert!(path.starts_with(&[0]) && path.ends_with(&[1000]));
182+
assert!(cost <= 999); // The cost should be at most 999 (direct path from 0 to 1000)
183+
}
184+
}

0 commit comments

Comments
 (0)