https://projecteuler.net/problem=1
Project Euler is a site with challenges that people solve with programs and then submit that code to be commented on. Normally, there's not a lot of comments unless you have a unique approach. That's fine.
I've solved many of them in python but I'm going to go through them with Rust.
Problem 1
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.
The problem is asking that we find all of the natural numbers that are below 1000 that are multiples of 3 or 5. When we find them, add them together and then give the sum.
Natural Numbers are integers that are positive. So any whole number below 1000.
first we have to go through all those numbers.
for n in 0..1000 {}
A for loop will do the trick just fine. 0..1000 runs through all the numbers up to but not including 1000. I could start at 3 instead of 0, 3..1000. That would be fine as well since I know that 0, 1, and 2 aren't multiples of 3 or 5.
In the loop, we need to determine if the number (n) is divisible by 3 or 5. One at a time, we can use simple if and else if statements.
if n%3 == 0{}
else if n%5 == 0{}
n % 3 == 0
If we take n, the given number as we loop through, and divide it by 3, will it do so evenly or will there be a remainder? If there's no remainder, we get 0. The % used here will give us this answer. The statement n%3 == 0 will result in a true or false.
The same is true of the else if statement.
Great! We have the logic for going through every number, 0 to 1000 and then figuring out if it's a multiple of 3 or 5.
We don't need an else statement. If the given number (n) is not divisible by 3 or 5, then we will do nothing with it and can continue on.
Lastly, we need something to hold the sum of the values as we go through.
let mut sum: i32 = 0;
The variable sum is declared as mutable as we will need to change the value every time we find a multiple.
Finally, we need some way to see what the final value is. We'll use the print to console macro for this.
The final code might look like this.
let mut sum: i32 = 0;
for n in 0..1000{
if n%3 == 0{
sum += n;
}
else if n%5 == 0{
sum += n;
}
}
println!("{}", sum);
Now I freely admit that this is not an inspired solution. It doesn't take into account any kind of performance. It's pretty brute force as it is but it's a beginner problem to highlight beginner problems in code.
This solution comes in at 1.29 seconds.
A more elegant approach
my friend showed me a way to make this a little more compact.
let x = (0..1000).collect::<Vec<i32>>();
let x: i32 = x.iter().filter(|&n| n%3 == 0 || n%5 == 0).sum();
dbg!(x);
First, he declared a variable x as a vector collecting all the values from 0 to 1000.
Then he declared x as a number using shadowing. Shadowing is convenient because it allows you to not only change the value of a variable but the type. You're basically forcing the variable x as it exists to leave scope and release it's name and then assign it's name to a new value. In this case, he used shadowing to iterate (iter()) through all the values in the vector and then filter out only the values that are divisble by 3 and 5 evenly. Finally we added them altogether.
He did this in one line using what's called a Closure. It's similar to a lambda in python.
https://doc.rust-lang.org/rust-by-example/fn/closures.html
The dbg! macro is a debug macro that prints things to the console. The rust documentation recommends that using this feature not be used, recommending the debug! macro from the log crate. However, this is not a final release of a program and is simply a learning tool.
The logic is very elegant and works just the same as my code but it is smaller. On Rust Playground it ran in 0.91 seconds versus the 1.3 seconds that my original code did. It's not surprising really.
Other solutions from other users -
Another user (forrin ) using Rust, posted this solution.
fn main() { println!("Result: {}", basic_solution(999)); } fn basic_solution(target: i32) -> i32 { let mut sum = 0; for n in 1..=target { if n % 3 == 0 || n % 5 == 0 { sum+= n; } } return sum; }
This solution is fine. It does basically what mine does except he isolated it into a function and filtered out the if/else if to a if this or that.
Runs 1.25seconds on Rust playground.
Another solution, similar to my friend's.
fn main() {
let bound : i32 = 1000;
println!("out {}", (1..bound).filter(|&n| n % 3 == 0 || n % 5 == 0).sum::<i32>().to_string())
}
Before I continue, I'm not trying to call either of them out. My solution for this wasn't exactly inspired. I'm simply trying to delve into the workings of our answers and account for the performance differences.
1 comment:
In production code this is what I'd have done.
```rs
let x: i32 = (0..1000).filter(|&n| n%3 == 0 || n%5 == 0).sum();```
also here are some speed test in playground.
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=06b1085385eef3313e532ec834fb8ae5
Post a Comment