Reputation: 9385
I'm new to Ruby and thought that it would be a great way to learn more by solving the problems at Project Euler.
Here's what I came up with using brute force for Problem 5:
#What is the smallest positive number that is evenly divisible by all of the numbers from 1 to 20?
end_point = 1_000_000_000
start_point = 2_520
(start_point..end_point).each do |number|
flag = true
(2..20).each do |divisor|
flag = flag & ( number % divisor ) == 0 ? true : false
end
print number.to_s + "\n" if flag
end
This runs for a long time and gives no answer.
Then I used the same logic to write a C++ program to do the same task:
#include<iostream>
using namespace std;
int main()
{
unsigned long int solution = 2520;
while(1)
{
bool flag = true;
for(int divisor=2;divisor<=20;divisor++)
{
if( solution % divisor == 0)
flag = flag & true;
else{
flag = false;
break;
}
}
if(flag == true){
cout<<solution<<endl;
break;
}
solution++;
}
return 0;
}
This one gives me the correct solution and runs for barely a second. The execution time doesn't really concern me as Ruby is interpreted and C++ compiled but Ruby's failure at returning the correct answer does surprise me. I think it might be because of me trying to write C++ Ruby style and not the actual Ruby way.
What am I doing wrong here?
Upvotes: 0
Views: 774
Reputation: 7307
I actually solved this problem without writing a program for it. This is the logic I used :
Write down all prime numbers less than 20 (target).
[2, 3, 5, 7, 11, 13, 17, 19]
For each prime number, raise it to the maximum power for which it is lesser than the target (20).
2**4 * 3**2 * 5 * 7 * 11 * 13 * 17 * 19
For doing it programmatically use the Sieve of Eratosthenes - http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes algorithm for iterating over the prime numbers. For each prime number in the list, find the maximum power to which it is raised to get a number lesser than the target (20). You can find the power using this formula : (Math.log(target)/Math.log(i)).floor
Assuming you get the array of primes : nums = [2, 3, 5, 7, 11, 13, 17, 19]
Then you can get the answer easily by using this :
nums.map {|i| i**((Math.log(target)/Math.log(i)).floor)}.inject(:*)
Upvotes: 3
Reputation: 46667
Note: I have not run either of the proposed solutions below to completion - they still take a long time - so correctness is not guaranteed!
The line where you update flag
is the problem. You're using &
(bitwise-and) rather than &&
(Boolean-and).
Among other problems, &
has higher operator precedence than ==
, so your line is interpreted as (since ? true : false
is redundant):
flag = (flag & (number % divisor)) == 0
Now, it appears that true & some_integer
is true
, which is not == 0
, so flag
is always set to false
.
Instead, you want:
flag = flag && (number % divisor == 0)
or, more succinctly and Rubyish:
flag &&= number % divisor == 0
An alternative solution would be to do:
i = 1
while true
break unless (2..20).any? {|d| i % d != 0}
i+=1
end
puts i
Upvotes: 2