From f5f6590a94dcf2d022a0143f35f1f55dd889180e Mon Sep 17 00:00:00 2001 From: Arlo Date: Thu, 2 May 2024 15:39:58 +0100 Subject: [PATCH] Fix: Was broken in a number of creative ways, now tested to be functional --- src/main.rs | 107 +++++++++++++++++++++++++++++----------------------- 1 file changed, 60 insertions(+), 47 deletions(-) diff --git a/src/main.rs b/src/main.rs index eeeb3a9..2bdc131 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::{collections::HashMap, hash::Hash}; fn main() { let mut pages: Graph = Graph::new(); @@ -8,69 +8,82 @@ fn main() { pages.add_page('D'); pages.add_page('E'); - println!("{pages:?}"); - pages.link_page('B', 'A'); - pages.link_page('C', 'B'); - pages.link_page('D', 'C'); - pages.link_page('B', 'D'); - pages.link_page('E', 'D'); - pages.link_page('A', 'E'); - pages.link_page('B', 'E'); - println!("{pages:?}"); + pages.add_link('A', 'E'); - println!("{:?} -> B", pages.get_links('B')); - let p = pages.clone(); - for page in p.items { - println!("PageRank {page} => {}", pages.page_rank(page, 10)) + pages.add_link('B', 'A'); + pages.add_link('B', 'D'); + pages.add_link('B', 'E'); + + pages.add_link('C', 'B'); + + pages.add_link('D', 'C'); + + pages.add_link('E', 'D'); + + // println!("{pages:?}"); + pages.page_rank(40); + for (page, score) in pages.get_sorted_scores() { + println!("{page} => {score:.2}") } } #[derive(Debug, Clone)] pub struct Graph { - items: Vec, - links: HashMap>, - pub damping_factor: f64 + nodes: HashMap>, + damping: f64, } -impl Graph { +#[derive(Debug, Clone)] +pub struct Node { + incoming_links: Vec, + outgoing_links: Vec, + score: f64, +} + +impl Node { pub fn new() -> Self { - Graph { items: vec![], links: HashMap::new(), damping_factor: 0.85 } + Node { incoming_links: vec![], outgoing_links: vec![], score: 1.0 } + } +} + +impl Graph { + pub fn new() -> Self { + Graph { nodes: HashMap::new(), damping: 0.85 } } - pub fn add_page(&mut self, item: T) { - self.items.push(item); - self.items.sort(); - let index = self.find_item(item); - self.links.insert(index, vec![]); - } - - pub fn link_page(&mut self, item: T, link: T) { - let item = self.find_item(item); - let link = self.find_item(link); - self.add_link(item, link) + pub fn add_page(&mut self, value: T) { + self.nodes.insert(value, Node::new()); } - fn add_link(&mut self, item_index: usize, link_index: usize) { - self.links.get_mut(&item_index).unwrap().push(link_index); + pub fn add_link(&mut self, link: T, page: T) { + self.nodes.get_mut(&link).unwrap().incoming_links.push(page.clone()); + self.nodes.get_mut(&page).unwrap().outgoing_links.push(link); } - fn find_item(&self, item: T) -> usize { - self.items.binary_search(&item).unwrap() + pub fn page_rank(&mut self, depth: usize) { + if depth == 0 { return } + + let nodes = self.nodes.clone(); + for (_, node) in self.nodes.iter_mut() { + let mut link_scores = 0.0; + for link in node.incoming_links.iter() { + let n = nodes.get(link).unwrap(); + link_scores += n.score / n.outgoing_links.len() as f64 + } + node.score = (1.0 - self.damping) + (self.damping * link_scores) + } + + self.page_rank(depth - 1) } - pub fn get_links(&self, item: T) -> Vec { - let item = self.find_item(item); - self.links.get(&item).unwrap() - .to_vec().iter().map(|i| self.items[*i]).collect() - } + pub fn get_sorted_scores(&self) -> Vec<(T, f64)> { + let mut scores = vec![]; + for (key, node) in &self.nodes { + scores.push((key.clone(), node.score)) + } + scores.sort_by(|a, b| + a.1.partial_cmp(&b.1).unwrap()); - pub fn page_rank(&self, item: T, depth: i32) -> f64 { - if depth == 0 { return 0.0; } - - let links = self.get_links(item); - let link_sums: f64 = links.clone().iter().map( - |i| self.page_rank(*i, depth - 1) / self.get_links(*i).len() as f64 - ).sum(); - return (1.0 - self.damping_factor) + self.damping_factor * link_sums + scores } } \ No newline at end of file