I'm starting to learn rust
and figured my first simple project would be to solve some of the Euler problems. I'd appreciate if anyone could tell me where I might be going wrong in my setup, language usage, or lack of rusty code.
Note, I'm not looking for feedback on the algorithms or solutions to the problem. My goal isn't to solve the Euler problems in the best and most efficient way, but to learn Rust by solving these problems. Solutions as posed may be sub-optimal.
Here's the general file structure.
src\ |- main.rs |- problems\ |- mod.rs |- p001.rs |- p002.rs ... // more files in the tree
And the contents of select files.
src/main.rs
mod problems; use std::collections::HashMap; use std::env::args; use std::time::Instant; use std::vec::Vec; fn main() { // Define the list of function pointers to each problem's solution let mut func_map: HashMap<&str, fn()> = HashMap::new(); func_map.insert("1", problems::p001::multiples_of_3_or_5); func_map.insert("2", problems::p002::even_fib_numbers); ... // More functions added here // Get the arguments from the command line. Then check if only one argument // was provided (namely the executable name, indicating the user provided // no arguments). In that case, just set the arguments to all the function // map keys so every single problem is solved. // TODO: This results in the keys being unsorted and the problems are solved // in a random order. It'd be nice to resolve that. let mut args: Vec<String> = args().collect(); if args.len() == 1 { args = func_map.keys().map(|k| String::from(*k)).collect(); } // Get a time at the start of solving all the problems. let start = Instant::now(); // Loop over all the requested problems, one by one, and solve them. for problem in args { match func_map.get(problem.as_str()) { Some(f) => { let problem_start = Instant::now(); f(); println!("Completed in {:?}\n", problem_start.elapsed()); } None => continue, } } println!("Solved all problems in {:?}\n", start.elapsed()); }
src/problems/mod.rs
pub mod p001; pub mod p002; ... // more modules added here
src/problems/p001.rs
//=============================== Project Euler ================================ // // Problem 1: Multiples of 3 or 5 // // If we list all the natural numbers below 10 that are multiples of 3 or 5, we // get 3, 5, 6 and 9. The sum of these multiples is 23. // // Find the sum of all the multiples of 3 or 5 below 1000. // //============================================================================== // Define the end point of values to use in the sum const END: u64 = 1000; // The most efficient algorithm here starts with the numbers 3 and 5 and // increments each by 3 and 5 respectively, adding the results to the running // sum and stopping once 1000 is hit. #[allow(dead_code)] pub fn multiples_of_3_or_5() { println!("Solving Problem 1: Multiples of 3 or 5"); let mut sum: u64 = 0; // Sum of all relevant values // Define our values that will hold the multiples of 3 and 5 let mut mul3: u64 = 3; let mut mul5: u64 = 5; // Loop over all multiples of 3, adding it to the sum each time while mul3 < END { sum += mul3; mul3 += 3; } // Loop over all multiples of 5, adding it to the sum each time, if it isn't // a multiple of 3 already. while mul5 < END { if 0 != mul5 % 3 { sum += mul5 } mul5 += 5; } println!("Sum of multiples of 3 or 5 below 1000 is {}", sum); } // This is a dumb, brute force approach that just loops over all possible numbers // from 1 to 1000, checking each one to see if it is divisible by 3 or 5 and adding // it to the running sum. Because it checks every number, it isn't as efficient as // just adding the numbers we already know are divisible by 3 or 5 (by virtue of // simply getting all the multiples of 3 and 5). #[allow(dead_code)] pub fn multiples_of_3_or_5_brute_force() { println!("Solving Problem 1: Multiples of 3 or 5"); let mut sum: u64 = 0; // Sum of all relevant values for ii in 1..END { if 0 == ii % 3 || 0 == ii % 5 { sum += ii; } } println!("Sum of multiples of 3 or 5 below 1000 is {}", sum); }
src/problems/p002.rs
//=============================== Project Euler ================================ // // Problem 2: Even Fibonnaci Numbers // // Each new term in the Fibonacci sequence is generated by adding the previous // two terms. By starting with 1 and 2, the first 10 terms will be: // // 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ... // // By considering the terms in the Fibonacci sequence whose values do not exceed // four million, find the sum of the even-valued terms. // //============================================================================== #[allow(dead_code)] pub fn even_fib_numbers() { println!("Solving Problem 2: Even Fibonacci Numbers"); // Define the initial two terms, along with a third temp variable let mut a: u64 = 1; let mut b: u64 = 2; let mut temp: u64; // Define the variable for the sum and initially set it equal to the first // two terms. let mut sum: u64 = 0; // Start a loop that will calculate successive fibonacci numbers and add them // to the sum if the value is even. while b <= 4_000_000 { if 0 == b % 2 { sum += b; } temp = a + b; a = b; b = temp; } println!("Sum of even Fibonacci terms below 4 million is {}", sum); }
main()
) runs a timer, and the "fizzbuzz" summation is presented in two versions (one un-called). I chose to address what I viewed as correctable shortcomings in the inefficient posted code for problem #2... While I understand and accept the OP is looking for a "Rust Review", the precursor to coding, in any language, should be choosing the best algorithm one can... Good coding is much more than typing syntactic statements...:-)
Cheers, and Happy New Year!:-)
\$\endgroup\$