Ruby - Why does this minor function change result in a big time difference? - ruby

This script is for Project Euler #14
I am refactoring it and all I did was grab 1 line of code n.even? ? n = n/2 : n = (3*n) + 1 and made it into it's own function. This change increase the execution time by 5 seconds.
Why would that happen?
Before:
def longest_collatz_sequence1(count)
check = []
while count >= 1
n = count
seq = [count]
while n > 1
n.even? ? n = n/2 : n = (3*n) + 1
seq << n
end
count -= 1
check << seq
value = sequence_check(check) if check.length == 2
end
puts "The number that produces the largest chain is: #{value[0][0]}"
end
def sequence_check(check)
check[0].length > check[1].length ? check.delete_at(1) : check.delete_at(0)
check
end
s = Time.new
longest_collatz_sequence1 1000000
puts "elapsed: #{Time.new-s}"
#~12.2 seconds
After:
def longest_collatz_sequence1(count)
check = []
while count >= 1
n = count
seq = [count]
while n > 1
n=collatz(n)
seq << n
end
count -= 1
check << seq
value = sequence_check(check) if check.length == 2
end
puts "The number that produces the largest chain is: #{value[0][0]}"
end
def collatz(n)
n.even? ? n = n/2 : n = (3*n) + 1
end
def sequence_check(check)
check[0].length > check[1].length ? check.delete_at(1) : check.delete_at(0)
check
end
s = Time.new
longest_collatz_sequence1 1000000
puts "elapsed: #{Time.new-s}"
#~17.7 seconds

Related

How to loop over an entire method until you achieve what you want ? (ruby)

I'm learning ruby and practicing with codewars, and I've come to a challenge that I feel I mainly understand (rudimentarily) but I'm unable to figure out how to continue looping over the method until I reach the result I'm looking for.
The challenge is asking to reduce a number, by multiplying its digits, until the multiplication results in a single digit. In the end it wants you to return the number of times you had to multiply the number until you arrived at a single digit. Example -> given -> 39; 3 * 9 = 27, 2 * 7 = 14, 1 * 4 = 4; answer -> 3
Here's my code :
def persistence(n)
if n < 10
return 0
end
arr = n.to_s.split("")
sum = 1
count = 0
arr.each do |num|
sum *= num.to_i
if num == arr[-1]
count += 1
end
end
if sum < 10
return count
else
persistence(sum)
end
end
Thanks for your help!
Your function is looking great with recursion but you are reseting the count variable to 0 each time the loop runs, I think if you use an auxiliar method it should run ok:
this is in base of your code with minor improvements:
def persistence(n)
return 0 if n < 10
count = 0
multiply_values(n, count)
end
def multiply_values(n, count)
arr = n.to_s.chars
sum = 1
arr.each do |num|
sum *= num.to_i
if num == arr[-1]
count += 1
end
end
if sum < 10
return count
else
multiply_values(sum, count)
end
end
a shorter solution could be to do:
def persistence(n)
return 0 if n < 10
multiply_values(n, 1)
end
def multiply_values(n, count)
sum = n.to_s.chars.map(&:to_i).reduce(&:*)
return count if sum < 10
multiply_values(sum, count + 1)
end
and without recursion:
def persistence(n)
return 0 if n < 10
count = 0
while n > 10
n = n.to_s.chars.map(&:to_i).reduce(&:*)
count += 1
end
count
end
Let's look at a nicer way to do this once:
num = 1234
product = num.to_s.split("").map(&:to_i).reduce(&:*)
Breaking it down:
num.to_s.split("")
As you know, this gets us ["1", "2", "3", "4"]. We can easily get back to [1, 2, 3, 4] by mapping the #to_i method to each string in that array.
num.to_s.split("").map(&:to_i)
We then need to multiply them together. #reduce is a handy method. We can pass it a block:
num.to_s.split("").map(&:to_i).reduce { |a, b| a * b }
Or take a shortcut:
num.to_s.split("").map(&:to_i).reduce(&:*)
As for looping, you could employ recursion, and create product_of_digits as a new method for Integer.
class Integer
def product_of_digits
if self < 10
self
else
self.to_s.split("").map(&:to_i).reduce(&:*).product_of_digits
end
end
end
We can now simply call this method on any integer.
1344.product_of_digits # => 6

Project Euler #50 - Consecutive prime sum - Ruby

I'm trying to solve Project Euler problem #50 (https://projecteuler.net/problem=50) where the problem is defined as:
Which prime, below one-million, can be written as the sum of the most
consecutive primes?
I've come up with two different solutions both giving the same wrong answer which leads me to believe the error happens as I'm building my list of primes, however, I can't seem to find any error. My solutions also seem to work for N = 10 and N = 100 but not N = 1000. Any help is appreciated.
Solution 1: (output = 958577)
require 'Prime'
# Initialising primes
N = 1_000_000
primes = {}
(2..N).each do |i|
primes[i] = true
end
i = 2
while i * i <= N
if primes[i]
j = i
while i * j <= N
primes[i * j] = false
j += 1
end
end
i += 1
end
# New prime list where total sum is less than N
new_primes = []
i = 2
sum = 0
while sum + i < N
if primes[i]
new_primes << i
sum += i
end
i += 1
end
# Keep removing last prime from list until total sum is prime
while true
if Prime.prime?( new_primes.inject(0, :+) )
puts new_primes.inject(0, :+)
break
else
new_primes.delete_at(-1)
end
end
Solution 2: (output = 958577)
require 'Prime'
# Initialising primes
N = 1_000_000
primes = {}
(2..N).each do |i|
primes[i] = true
end
i = 2
while i * i <= N
if primes[i]
j = i
while i * j <= N
primes[i * j] = false
j += 1
end
end
i += 1
end
sum = 0
max = 0
i = 2
while i < N
if primes[i]
sum += i
if sum < N && Prime.prime?(sum)
max = sum
end
end
i += 1
end
puts max
(w.r.t to Solution 2) Your method to find primes seems correct. The problem is with your logic. If the primes less than N are p_1,p_2,..,p_k, then you are only considering
only the sums p_1, p_1+p_2, p_1+p_2+p_3,...,p_1+p_2+..+p_k. What about sums not starting from p_1, say p_3+p_4.

How to I make floyd triangle shape using for loop?

I need output of Floyd triangle like:
1
0 1
1 0 1
0 1 0 1
I tried. I didn't get it exactly. Can anyone explain the logic?
This is the code I tried:
k = 0
for i in 1..5
for j in 1..5
if (i%2)==0;
k = (j%2==0) ? 1:0;
else;
k = (j%2==0) ? 0:1;
puts k,'';
end
end
puts
end
The main issue here is that in order to get the "triangle" shape of your output, you need your inner loop to increment from 1 to i instead of 1 to 5.
k = 0
for i in 1..5
for j in 1..i
if (i%2)==0
k = j + 1
else
k = j
end
print "#{k%2} "
end
puts
end
Here's a one line approach:
5.times {|line| puts (line + 1).times.with_object(""){|num, str| (num + line).even? ? (str << " 1 ") : (str << " 0 ") } }
to make it more clear:
lines = 5
lines.times do |line|
str = ""
line = line + 1 # 5.times runs from 0 to 4 and we need 1 to 5
line.times do |num|
# the condition is a bit different because I changes the code a bit
if (line + num).even?
str << " 0 "
else
str << " 1 "
end
end
puts str
end
Alright the following should work, but i hope it's readable. If you need more explanation or have specific questions let me know
i = 1
while i <= 4 do
if i%2 > 0
output = 1
else
output = 0
end
j = 1
while j <= i do
print( "#{output} " )
if output == 1
output = 0
else
output = 1
end
j+=1
end
print( "\n" )
i+=1
end
You can try following code for output you are expecting:
k = 0
for i in 1..4
for j in 1..i // inner loop code runs i times for each outer loop iteration
if (i%2)==0;
k = (j%2==0) ? 1:0;
else;
k = (j%2==0) ? 0:1;
end
print k,' ';
end
puts
end
Click Here to see output.
You can also get idea about for loops through this link.
The prefered ruby way:
layers = 4 # Change to as many layers as you want
layers.times do |i| # i starts from 0
(i + 1).times do |j| # j also starts from 0
print (i + j + 1) & 1, ' '
end
puts
end
The for way:
layers = 4
for i in 0...layers
for j in 0...(i + 1)
print (i + j + 1) & 1, ' '
end
puts
end

Factorial in Ruby

I'm trying to do in the Ruby program the summation of a factorial. and I can not solve. a clarification, when they run out points .... means that the logic is fine. if you get a F in one of the points, it means that the logic is wrong.
Write a program to calculate the sum of 1 + 1 / (2!) + 1 / (3!) + 1 / (4!) + .... + 1 / (n!) For a given n. Write the program in two ways: using While, For
def factorial(n)
#(n == 0) ? 1 : n * factorial(n - 1)
fact = 1
for i in 1..n
fact = fact * i
end
return fact
end
def sumatoriaWhile(n)
total = n
sumatoria = 0.0
while n > 1
total = total * (n - 1)
n = n - 1
sumatoria =sumatoria + total.to_f
end
return (1 + (1 / total.to_f)).round(2)
end
def sumatoriaFor(n)
fact = 1
sumatoria = 0.0
for i in 1..n
for j in 1..i
fact = fact * j
end
sumatoria = sumatoria + (1 / fact.to_f)
i = i + 1
end
return sumatoria.round(2)
end
#--- zona de test ----
def test_factorial
print validate(120, factorial(5))
print validate(5040, factorial(7))
print validate(362880, factorial(9))
end
def test_sumatoriaWhile
print validate(1.50, sumatoriaWhile(2))
print validate(1.83, sumatoriaWhile(3))
end
def test_sumatoriaFor
print validate(1.50, sumatoriaFor(2))
print validate(1.83, sumatoriaFor(3))
end
def validate (expected, value)
expected == value ? "." : "F"
end
def test
puts "Test program"
puts "---------------------------"
test_factorial
test_sumatoriaWhile
test_sumatoriaFor
puts " "
end
test
My friend, Thank you for your prompt response. First of all, I appreciate the help given. I am learning ruby programming and want to learn more as you and the other people. if indeed the answer is wrong. I have modified the answer. and also need to know what the function of While. and I hereby amended program again. and now I get an F on the part of WHILE.
def factorial(n)
fact = 1
for i in 1..n
fact = fact * i
end
return fact
end
def sumatoriaWhile(n)
total = n
sumatoria = 0.0
while n < 1
total = total * (n - 1)
sumatoria = sumatoria + (1.0 / total.to_f)
n = n - 1
end
return sumatoria.round(2)
end
def sumatoriaFor(n)
fact = 1
sumatoria = 0.0
for i in 1..n
fact = fact * i
sumatoria = sumatoria + (1.0 / fact.to_f)
end
return sumatoria.round(2)
end
#--- zona de test ----
def test_factorial
print validate(120, factorial(5))
print validate(5040, factorial(7))
print validate(362880, factorial(9))
end
def test_sumatoriaWhile
print validate(1.50, sumatoriaWhile(2))
print validate(1.67, sumatoriaWhile(3))
end
def test_sumatoriaFor
print validate(1.50, sumatoriaFor(2))
print validate(1.67, sumatoriaFor(3))
end
def validate (expected, value)
expected == value ? "." : "F"
end
def test
puts "Test de prueba del programa"
puts "---------------------------"
test_factorial
test_sumatoriaWhile
test_sumatoriaFor
puts " "
end
test
I have a hard time figuring out what you're doing in the summation function. Here's a straightforward function:
def sumatoriaFor(n)
return 0 if n <= 0
factorial = 1
sum = 0.0
for i in 1..n
factorial *= i
sum += 1.0 / factorial.to_f
end
return sum.round(2)
end
def sumatoriaWhile(n)
return 0 if n <= 0
i = 1
factorial = 1
sumatoria = 0.0
while i <= n
factorial *= i
sumatoria += (1.0 / factorial.to_f)
i = i + 1
end
return sumatoria.round(2)
end
The while look is straight forward too now. Also your validation is wrong:
1 + 1/2 + 1/6 ~= 1 + 0.5 + 0.17 = 1.67

Getting a 'nil:Nil Class' error in Ruby, but the array doesn't seem to empty

I'm trying to code the 'Sieve of Eratosthenes' in Ruby and I'm having difficulty in the second 'while' loop. I want to test to see if integers[j] % integers[0] == 0, but the compiler keeps giving me a nil:Nil Class error at this line. I can't figure out the problem.
n = gets.chomp.to_i
puts
while n < 2
puts 'Please enter an integer >= 2.'
puts
n = gets.chomp.to_i
puts
end
integers = []
i = 0
while i <= n - 3
integers[i] = i + 2
i += 1
end
primes = []
j = 1
while integers != []
primes.push integers[0]
while j <= integers.length
if integers[j] % integers[0] == 0
integers.delete(integers[j])
end
j += 1
end
integers.shift
j = 1
end
puts integers
puts
puts primes
Thanks in advance for any help!
It's an off-by-one error. You're testing for j <= integers.length. So, for example, if you array has five items, the last iteration will be integers[5]. But the last index in a five-item array is 4 (because it starts at 0). You want j < integers.length.

Resources