All possible products - ruby

I'm trying to find all possible product of two 3-digit numbers. When I work with small ranges, I'm able to get an output in short amount of time but when the ranges are big, it seems to take really long time. Is there any way to to shorten the time to get the result?
The problem I'm working on is:
"A palindromic number reads the same both ways. The largest palindrome made from the product of two 2-digit numbers is 9009 = 91 × 99.
Find the largest palindrome made from the product of two 3-digit numbers."
a = []
for x in 100..999
for y in 100..999
num = (x * y)
unless a.include? num
a.push num
end
end
end
p a

This is going to compute 100 x 101 and 101 x 100 separately, even though they're not going to be pushed to the array since they're already in it.
I'm bad at math, but maybe every time x goes up, y's minimum range can go up since that one was just used? people who are better at math can tell me if this is going to start missing numbers.
z= 100
for x in 100..999
for y in z..999
num = (x * y)
unless a.include? num
a.push num
end
z = z+1
end
end
I think doing this might make the "unless a.include? num" line unnecessary, too.

Looking at your code a quick optimization you can make is to use a set rather than an array to store the already computed products.
Since a is an array, a.include?(num) will have to iterate through the entire list of elements before returning true / false.
If a were to be a set, a.include?(num) will return in sub linear time.
Example:
require 'set'
a = Set.new
for x in 100..999
for y in 100..999
num = (x * y)
unless a.include? num
a.add(num)
end
end
end
puts a.to_a.join(", ")
Moreover one of the nice properties of a set is that it only stores unique elements so the following would be equivalent:
require 'set'
a = Set.new
for x in 100..999
for y in 100..999
num = (x * y)
a.add(num)
end
end
puts a.to_a.join(", ")

What are you really trying to do, i.e. what is the original problem, and why do you need all of these products?
Are you printing every single one out? Is someone asking you for a concrete list of every single one?
If not, there is likely a better way to deal with this problem. For example, if all you wanted is to check if a number X will be an element in "that list of products", all you'd have to do is:
range = 100..999
range.any? { |i| range.include?(x / i) }

Related

Stack level too deep in recursion for largest palindrome product question (Project Euler)

I'm trying to implement a recursive solution to the largest palindrome product problem
What I'm trying to do is start both numbers at 999 and iterate down to 100 for num1 and then restart num1 at 999 and iterate num2 down by 1.
The goal is basically to mimic a nested for-loop.
def largest_palindrome_prod(num1 = 999, num2 = 999, largest_so_far = 0)
prod = num1 * num2
largest_so_far = prod if prod > largest_so_far && check_pal(prod)
if num2 == 100
return largest_so_far
elsif num1 == 100
largest_palindrome_prod(num1 = 999, num2 -= 1, largest_so_far)
else
largest_palindrome_prod(num1 -= 1, num2, largest_so_far)
end
end
#I know this function works, just here for reference
def check_pal(num)
num = num.to_s if num.is_a? Integer
if num.length < 2
true
else
num[0] == num[-1] ? check_pal(num[1..-2]) : false
end
end
rb:10:inlargest_palindrome_prod': stack level too deep`
I'm getting this error which is referring to the else statement in the largest_palindrome_prod function, but I can't figure out wast could be causing the stack error.
You don't have an infinite recursion bug. The stack is just running out of space because of the size of your input. To prove this, you can run your same function with the range of 2-digit numbers, instead of the 3-digit ones. It returns fine, which shows that there is no flaw with your logic.
How to get around this? Two options.
Option 1: You could simply not use recursion here (just use a regular nested loop instead)
Option 2: Keep your same code and enable tail call optimization:
# run_code.rb
RubyVM::InstructionSequence.compile_option = {
tailcall_optimization: true,
trace_instruction: false
}
require './palindrome_functions.rb'
puts largest_palindrome_prod
# => 906609
Note, for a reason I don't fully understand, the tail call optimization must be enabled in a different file than the code being run. So if you simply moved the compile_option line to the palindrome_functions.rb file, it wouldn't work.
I cant really give you a full explanation of tail call optimization (look it up on Wikipedia) but from my understanding, its a heavy optimization for recursive functions that only works when the recursive call is at the end of the function body. Your function meets this criteria.
#maxpleaner has answered your question and has shown how you can use recursion that avoids the stack level error. He also mentioned the option (which I expect he favours) of simply looping, rather than employing recursion. Below is one looping solution. The following method is used in the search1.
def check_ranges(range1, range2 = range1)
range1.flat_map do |n|
[n].product((range2.first..[n, range2.last].min).to_a)
end.map { |x,y| x*y }.
sort.
reverse_each.
find do |z|
arr = z.digits
arr == arr.reverse
end
end
Let's first find the largest palindrome of the product of two numbers between 960 and 999 (if there are any):
check_ranges(960..999)
#=> nil
There are none. Note that this calculation was very cheap, requiring the examination of only 40*40/2 #=> 800 products. Next, find the largest palindrome that is equal to the product of two numbers between 920 and 999.
check_ranges(920..999)
#=> 888888
Success! Note that this method re-checks the 800 products we checked earlier. It makes more sense to examine only the cases represented by the following two calls to brute_force:
check_ranges(960..999, 920..959)
#=> 888888
check_ranges(920..959)
#=> 861168
The first call computes 40*40 #=> 1600 products; the second, 800 products.
Of course, we have not yet necessarily found the largest product that is a palindrome. We do, however, have a lower bound on the largest product, which we can use to advantage. Since
888888/999
#=> 889
we infer that if the product of two numbers is larger than 888888, both of those numbers must be at least 889. We therefore need only check:
check_ranges(889..999, 889..919)
#=> 906609
check_ranges(889..919)
#=> 824428
We are finished. This tells us that 906609 is the largest product of two 3-digit numbers that is a palindrome.
The question does not ask what are the two numbers whose product is the largest palindrome, but we can easily find them:
(889..999).to_a.product((889..919).to_a).find { |x,y| x*y == 906609 }
#=> [993, 913]
993*913
#=> 906609
Moreover, let:
a = (889..999).to_a.product((889..919).to_a).map { |x,y| x*y }.
sort.
reverse
Then:
a.index { |n| n == 906609 }
#=> 84
tells us that only the largest 84 elements of this sorted group of 111*31 #=> 3441 products had to be examined before a palindrome (906609) was found.
All of this needs to be organized into a method. Though challenging for a newbie, it should be a good learning experience.
1. It would be useful to test which is faster, arr = z.digits; arr == arr.reverse or s = z.to_s; s == s.reverse.
#maxpleaner already answered, #Cary Swoveland already showed one brute force way using ranges and product. I'd like to show another brute force using a nested loop, easier to follow (IMO):
n = 9999
res = [0]
bottom = 10**(n.digits.size - 1)
n.downto(bottom) do |k|
k.downto(bottom) do |j|
# puts "#{k}, #{j}"
res = [k, j, k * j] if check_pal(k * j) && k * j > res.last
end
end
res
#=> [9999, 9901, 99000099]
I guess it can be optimized further, for example, using
n.downto(n*99/100) do |k|
k.downto(k*99/100) do |j|
Returned [99979, 99681, 9966006699] in 0.7 seconds.
Not required, but this increases the speed:
def check_pal(num)
word = num.to_s
word.reverse == word
end

Ruby prime number sum

I am trying to take the sum of the n first prime numbers. I found a way of showing the first 100, but I don't know how to get rid of 1 and how to make a sum with the numbers. I was thinking about storing them into an array, but I can not figure it out.
num = 1
last = 100
while (num <= last)
condition = true
x = 2
while (x <= num / 2)
if (num % x == 0)
condition = false
break
end
x = x + 1
end
primes = [] # Here
if condition
puts num.to_s
primes << num.to_s # Here
end
num = num + 1
end
puts primes.inject(:+) # Here
Based on what I understood from what you guys are saying I added these lines (the ones commented # Here). It still does not print the sum of them. What I meant with getting rid of 1 is that I know that 1 is not considered a prime number, and I do not get how to make it without 1. Thank you very much guys for your time and answers, and please understand that I am just starting to study this.
If you want to add a list of numbers together you can use the following:
list_of_prime_numbers.inject(0) {|total,prime| total + prime}
This will take the list of numbers, and add them one by one to an accumulator (total) that was injected into the loop (.inject(0)), add it to the current number (prime) and then return the total which then becomes the value of total in the next iteration.
I'm not quite sure what you mean by:
I don't know how to get rid of 1
but if you mean to not use the first number (which is 1 in a list of primes starting from 0)
then you could do:
list_of_prime_numbers[1...list_of_prime_numbers.length].
inject(0) {|total,prime| total + prime}
Which would only get all the numbers except the first up to but not including the length of the array
and as for getting the number into the array you could push it into the array like so:
list_of_prime_numbers << prime_number
You can make use of Prime Enumerable in ruby
require 'prime'
((1..100).select { |number| Prime.prime?(number) }).inject(:+)
OR
Prime.each(100).inject(:+)
Hope this helps.

Ruby - Is there a recursive solution to find the divisors of a number?

I am doing a ruby problem that wants a method to find all divisors of a number except itself with the output being a sorted array. If the number is prime, list that it is prime.
I am currently trying to teach myself recursion. Simple recursive problems like finding the factorial of a number is pretty basic to understand but I wanted to know if this particular problem could be done recursively. It seems it fits the criteria of one that could but I could not figure it out.
Example n = 15, divisors besides itself are [3,5].
My code that solved the problem.
require 'prime'
def divisors(n)
return "#{n} is prime" if Prime.prime?(n)
x = n/2
arr = []
until x == 1
arr << x if n % x == 0
x -= 1
end
arr.sort
end
Any help doing this recursively would be great or just letting me know it's not a problem that can be done this way would be helpful too.
def divisors(n, x=nil)
return "#{n} is prime" if Prime.prime?(n)
x ||= n/2
arr = []
return arr if x == 1
if n % x == 0
arr << x
end
(arr.concat divisors(n, x - 1)).sort
end
The function is refactored to handle three things:
the initial call (x ||= /2)
base cases (early returns)
iteration logic done through recursion.
An important thing is that the variable which changes during the iteration (x) is placed as a parameter for the method (with a default value, so it can essentially be used as a private parameter)
By the way, I personally found learning Elixir very helpful in understanding recursion. With pattern matching and multiple functional clauses, the initial call, base case, and iteration can be split into their own methods.

Iteration order

I'm working on Project Euler problem #4:
A palindromic number reads the same both ways. The largest palindrome made from the product of two 2-digit numbers is 9009 = 91 × 99.
Find the largest palindrome made from the product of two 3-digit numbers.
My code as follows is wrong:
def ispalindrome?(number)
number.to_s == number.to_s.reverse
end
palindromes = []
(100..999).each { |x|
(100..999).each { |y|
palindromes.push (x * y) if ispalindrome?(x * y)
}
}
palindromes.last # => 580085
What's going on here?
This has nothing to do with ruby. Simple math :)
Replace palindromes.last with palindromes.max
As someone else said, replacing palindromes.last with palindromes.max will work.
The reason is that, as products of three-digit numbers, 580085 = 995 * 583 and 906609 = 993 * 913.
Think carefully about the order in which you consider pairs of x and y. If you consider (993, 913) and then later (995, 583) (as happens in the first and third examples), then the last palindrome found will be 580085.
You just forgot to sort your array before taking the value, I used your code from the first try just added
palindromes.sort
and it gave me 906609
The problem is that you are not returning the biggest number but the last number that was added , and it depends on the order you loop through the numbers.
you need to change your last command to:
puts palindromes.max

Ruby and a Math Problem

Let's say I have to have a cart with a subtotal of 1836.36. I must achieve this exact amount by adding up several products from a list with a range of prices.
Say, I have a few products at 9.99, 29.99, 59.99 and I can add several of each to meet the desired subtotal. How would one approach this problem using Ruby?
I've thought of feeding the list of prices into a script and somehow getting the script to add until it reaches the subtotal and then spit out the prices required to reach the subtotal... just not sure how to approach it.
Any suggestions are welcome and thanks in advance. Looking forward to ideas.
9.99*x + 29.99*y + 59.99*z = 1836.36
brute force iterate through all the permutations of x,y,z within a range of integers
For example:
(0..9).each do |x|
(0..9).each do |y|
(0..9).each do |z|
puts "x #{x} y #{y} z #{z}" if (x * 9.99 + y * 29.99 + z * 59.99 == 1836.36)
end
end
end
discard any answer whose sum is not 1835.36.
Something like that... haven't tested it. You could probably tweak and optimize it to ignore cases that would certainly fail to pass.

Resources