How do I properly use a for loop in Ruby? - ruby

I'm trying to improve my Ruby skills using the Project Euler series of questions and I'm wondering why this code does not work for this question: "Even Fibonacci numbers, Problem 2"
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.
My code:
def fibo(n)
first, second, sequence = 0,1,[]
for e in n
first, second = second, first + second
sequence << e if e%2 == 0
end
sequence.inject(:+)
end
puts fibo(4000000)
Any help would be appreciated!

In the line:
for e in n
4,000,000 is being substituted for n, so you are saying:
for e in 4000000
which is not a valid statement - you cannot iterate on an integer.
If you would like to iterate through all the values from 0 to n, try:
for e in (0..n)
This iterates through the values in the range 0 to n.
However, there is a deeper problem here. It looks as though you want to iterate up to the value 4000000, but instead your code would iterate through the first 4000000 Fibonacci numbers, which is much more than you want. You may want to consider saying:
while second <= 4000000

I suggest you check out Ruby Koans if you're starting out with Ruby. It's a fun way of getting used to the ways of the language.
That said your code is not calculating Fibonacci correctly, it's not summing the Fibonacci numbers and also has some other errors (for e in n is not valid because n is not an Enumerator type). Fix it like this:
def fibo(n)
first, second, sum = 0, 1, 0
loop do
first, second = second, first + second
sum += second if second%2 == 0
break if second >= n
end
sum
end

Related

Execution Timed Out (12000 ms): How can I optimize this simple kata to run faster?

I'm practicing my coding chops after a long break and ran into this kata on CodeWars
With an input of numbers in an array, return the sums of its parts. So for example:
def parts_sums(ls)
sums = []
until ls.size == 0
sums << ls.inject(:+)
ls.shift
end
sums << 0
end
######### INPUT #######
parts_sums([0, 1, 3, 6, 10])
######### EXPECTED OUTPUT ######
[20, 20, 19, 16, 10, 0]
0 + 1 + 3 + 6 + 10 = 20
1 + 6 + 3 + 10 = 20
3 + 6 + 10 = 19
6 + 10 = 16
10 = 10
0 = 0
My solution solves the kata, however once I reach arrays of around 30,000+ my solution takes too long to solve.
So my question is to the community, how would I even attempt to make this go faster. I know that recursion is usually slow, and that for loops and its variants are usually sufficient to get the job done. What happens when that fails? What are some things to try to make my code above faster?
I'm looking for some advice and some examples if anyone has any. Appreciate the input. Thanks.
def parts_sums(ls)
ls.each_with_object([ls.sum]) { |n,arr| arr << arr.last - n }
end
parts_sums([0, 1, 3, 6, 10])
#=> [20, 20, 19, 16, 10, 0]
The issue with the code is that you are performing an inject on every iteration of your loop, which is unnecessarily slow.
You only need to sum the elements of the array once, outside of any loop. Once you have that sum, you can iterate through the elements of the array and perform a constant time subtraction from the current sum and push it into the sums array.
def part_sums(ls)
sum = ls.inject(:+)
sums = [sum]
ls.each do |val|
sum -= val
sums << sum
end
sums
end
There is also no need to shift, if you iterate through the array with the each iterator or keep a counter and use a while loop.
This version of the function runs much faster:
def parts_sums_2(ls)
sums = []
last_sum = 0
(ls.length - 1).downto(0).each do |i|
last_sum += ls[i]
sums.prepend last_sum
end
sums + [0]
end
The key here is going backwards through the array - starting with the smallest sum (only the last element). Each subsequent step moves one index towards the beginning, and adds that value to the previous sum.
Since the problem statement requires you to shift each step, your result must have the largest sums at the beginning, even though these are the last ones to be computed. This is why my code uses prepend rather than push.
This is O(N) time complexity instead of O(N^2), which is an order of magnitude difference.
With 100_000 inputs, your original function took 7.040443 seconds, while mine here took 0.000008 seconds
Also in general you should try to avoid mutating the input to your methods (as you were doing with shift).

Sum of Fibonacci even number

I'm currently working on small ruby projects from project Euler site. I was given a task to sum even fibonacci numbers that are less than 4 millions. Unfortunately there is a small bug in my code, because when I change the limit e.i. to 100, it prints 188 instead of 44. Surprisingly this program gives the right answer but i don't really know in what way my code is wrong.
a=[]; a[0]=1; a[1]=1;
i = 1
while a[-1] < 608
a[i+1]=(a[i] + a[i-1])
i +=1
end
x = 0
a.each do |num|
if num % 2 == 0
x += num
end
end
print "The sum of even Fibonacci number is: #{x}"
The problem comes from the second iteration. You are stopping the generation of Fibonacci numbers when one of the numbers cross the limit (ie when the last number is > 100).
It turns out that after the generation step, the array is [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144], this explains your wrong result 188 = 144+44.
So, your code works only when the last element generated is odd, which is the case in Euler's problem test. In order to correct that, change your second iteration from a.each do ... end to a[0...-1].each do ... end In order to iterate through the array except the last element.
BTW I would recommend you not to use an array here.
You are just wasting memory and ruby is losing time on extending it (this can be solved via Array.new(ARRAY_SIZE)).
Since you don't actually need a fibbonaci sequence you can just have something like this:
LIMIT = 4_000_000
a = 1
b = 1
next_number = a + b
sum = 0
while next_number < LIMIT
sum += next_number if next_number.even?
a = b
b = next_number
next_number = a + b # or next_number += a
end
UPD. Oh my god I don't know why this question appeared in my feed. Sorry for necroposting:)

Code to write a random array of x numbers with no duplicates [duplicate]

This is what I have so far:
myArray.map!{ rand(max) }
Obviously, however, sometimes the numbers in the list are not unique. How can I make sure my list only contains unique numbers without having to create a bigger list from which I then just pick the n unique numbers?
Edit:
I'd really like to see this done w/o loop - if at all possible.
(0..50).to_a.sort{ rand() - 0.5 }[0..x]
(0..50).to_a can be replaced with any array.
0 is "minvalue", 50 is "max value"
x is "how many values i want out"
of course, its impossible for x to be permitted to be greater than max-min :)
In expansion of how this works
(0..5).to_a ==> [0,1,2,3,4,5]
[0,1,2,3,4,5].sort{ -1 } ==> [0, 1, 2, 4, 3, 5] # constant
[0,1,2,3,4,5].sort{ 1 } ==> [5, 3, 0, 4, 2, 1] # constant
[0,1,2,3,4,5].sort{ rand() - 0.5 } ==> [1, 5, 0, 3, 4, 2 ] # random
[1, 5, 0, 3, 4, 2 ][ 0..2 ] ==> [1, 5, 0 ]
Footnotes:
It is worth mentioning that at the time this question was originally answered, September 2008, that Array#shuffle was either not available or not already known to me, hence the approximation in Array#sort
And there's a barrage of suggested edits to this as a result.
So:
.sort{ rand() - 0.5 }
Can be better, and shorter expressed on modern ruby implementations using
.shuffle
Additionally,
[0..x]
Can be more obviously written with Array#take as:
.take(x)
Thus, the easiest way to produce a sequence of random numbers on a modern ruby is:
(0..50).to_a.shuffle.take(x)
This uses Set:
require 'set'
def rand_n(n, max)
randoms = Set.new
loop do
randoms << rand(max)
return randoms.to_a if randoms.size >= n
end
end
Ruby 1.9 offers the Array#sample method which returns an element, or elements randomly selected from an Array. The results of #sample won't include the same Array element twice.
(1..999).to_a.sample 5 # => [389, 30, 326, 946, 746]
When compared to the to_a.sort_by approach, the sample method appears to be significantly faster. In a simple scenario I compared sort_by to sample, and got the following results.
require 'benchmark'
range = 0...1000000
how_many = 5
Benchmark.realtime do
range.to_a.sample(how_many)
end
=> 0.081083
Benchmark.realtime do
(range).sort_by{rand}[0...how_many]
end
=> 2.907445
Just to give you an idea about speed, I ran four versions of this:
Using Sets, like Ryan's suggestion.
Using an Array slightly larger than necessary, then doing uniq! at the end.
Using a Hash, like Kyle suggested.
Creating an Array of the required size, then sorting it randomly, like Kent's suggestion (but without the extraneous "- 0.5", which does nothing).
They're all fast at small scales, so I had them each create a list of 1,000,000 numbers. Here are the times, in seconds:
Sets: 628
Array + uniq: 629
Hash: 645
fixed Array + sort: 8
And no, that last one is not a typo. So if you care about speed, and it's OK for the numbers to be integers from 0 to whatever, then my exact code was:
a = (0...1000000).sort_by{rand}
Yes, it's possible to do this without a loop and without keeping track of which numbers have been chosen. It's called a Linear Feedback Shift Register: Create Random Number Sequence with No Repeats
[*1..99].sample(4) #=> [64, 99, 29, 49]
According to Array#sample docs,
The elements are chosen by using random and unique indices
If you need SecureRandom (which uses computer noise instead of pseudorandom numbers):
require 'securerandom'
[*1..99].sample(4, random: SecureRandom) #=> [2, 75, 95, 37]
How about a play on this? Unique random numbers without needing to use Set or Hash.
x = 0
(1..100).map{|iter| x += rand(100)}.shuffle
You could use a hash to track the random numbers you've used so far:
seen = {}
max = 100
(1..10).map { |n|
x = rand(max)
while (seen[x])
x = rand(max)
end
x
}
Rather than add the items to a list/array, add them to a Set.
If you have a finite list of possible random numbers (i.e. 1 to 100), then Kent's solution is good.
Otherwise there is no other good way to do it without looping. The problem is you MUST do a loop if you get a duplicate. My solution should be efficient and the looping should not be too much more than the size of your array (i.e. if you want 20 unique random numbers, it might take 25 iterations on average.) Though the number of iterations gets worse the more numbers you need and the smaller max is. Here is my above code modified to show how many iterations are needed for the given input:
require 'set'
def rand_n(n, max)
randoms = Set.new
i = 0
loop do
randoms << rand(max)
break if randoms.size > n
i += 1
end
puts "Took #{i} iterations for #{n} random numbers to a max of #{max}"
return randoms.to_a
end
I could write this code to LOOK more like Array.map if you want :)
Based on Kent Fredric's solution above, this is what I ended up using:
def n_unique_rand(number_to_generate, rand_upper_limit)
return (0..rand_upper_limit - 1).sort_by{rand}[0..number_to_generate - 1]
end
Thanks Kent.
No loops with this method
Array.new(size) { rand(max) }
require 'benchmark'
max = 1000000
size = 5
Benchmark.realtime do
Array.new(size) { rand(max) }
end
=> 1.9114e-05
Here is one solution:
Suppose you want these random numbers to be between r_min and r_max. For each element in your list, generate a random number r, and make list[i]=list[i-1]+r. This would give you random numbers which are monotonically increasing, guaranteeing uniqueness provided that
r+list[i-1] does not over flow
r > 0
For the first element, you would use r_min instead of list[i-1]. Once you are done, you can shuffle the list so the elements are not so obviously in order.
The only problem with this method is when you go over r_max and still have more elements to generate. In this case, you can reset r_min and r_max to 2 adjacent element you have already computed, and simply repeat the process. This effectively runs the same algorithm over an interval where there are no numbers already used. You can keep doing this until you have the list populated.
As far as it is nice to know in advance the maxium value, you can do this way:
class NoLoopRand
def initialize(max)
#deck = (0..max).to_a
end
def getrnd
return #deck.delete_at(rand(#deck.length - 1))
end
end
and you can obtain random data in this way:
aRndNum = NoLoopRand.new(10)
puts aRndNum.getrnd
you'll obtain nil when all the values will be exausted from the deck.
Method 1
Using Kent's approach, it is possible to generate an array of arbitrary length keeping all values in a limited range:
# Generates a random array of length n.
#
# #param n length of the desired array
# #param lower minimum number in the array
# #param upper maximum number in the array
def ary_rand(n, lower, upper)
values_set = (lower..upper).to_a
repetition = n/(upper-lower+1) + 1
(values_set*repetition).sample n
end
Method 2
Another, possibly more efficient, method modified from same Kent's another answer:
def ary_rand2(n, lower, upper)
v = (lower..upper).to_a
(0...n).map{ v[rand(v.length)] }
end
Output
puts (ary_rand 5, 0, 9).to_s # [0, 8, 2, 5, 6] expected
puts (ary_rand 5, 0, 9).to_s # [7, 8, 2, 4, 3] different result for same params
puts (ary_rand 5, 0, 1).to_s # [0, 0, 1, 0, 1] repeated values from limited range
puts (ary_rand 5, 9, 0).to_s # [] no such range :)

Simply Pseudocode Question

I'm new to psuedocode, and I'm having trouble putting all the pieces together:
Here is the definition of a function named foo whose inputs are two integers and an array of integers a[1] ... a[n].
1 Foo(k,m, a[1],...,a[n])
2 if (k < 1 or m > n or k > m) return 0
3 else return a[k] + Foo(k+1,m,a[1],...,a[n])
Suppose that the input integers are k=2 and m=5 and the input array contains [5, 6, 2, 3, 4, 8, 2]. What value does Foo return? Using summation notation, give a general formula for what Foo computes.
This one is making my head hurt. Here's what I did so far:
Line 2 has three conditional statements:
If k<1 // if 2<1..this is false
If m>n // if 5 is greater than the amount of values in the array, which is 7, so this is false
If k>m // if 2>5, this is false
So this function will display line 3. Line 3 says:
return a[k] which is a[2] which is the second value of the array, which is 6. So take 6 and add it to (2+1, 5, a[1].....,a[n])
Is what I have done correct up there? If so, how would I know what a[n] is? Am I supposed to be finding that? What would be the final result of all this?
Simple answer: that function returns the sum of all the numbers a[k], a[k+1], ... a[m].
What you're doing is correct so far. The "n" is just a placeholder meaning the last element of the array. So if your input array is {5,6,2,3,4,8,2}, n = 7 (cause your have seven elements), and a[n] = 2.
But why it returns the sum of all numbers a[k], a[k+1], ... a[m], you should find out for yourself. Just continue with your analysis. :)
So take 6 and add it to (2+1, 5,
a[1].....,a[n])
Take 6 and add it to Foo(2+1, 5, a[1].....,a[n]). It's a recursive function. You have to evaluate the function again with k=3 and m=5.
I think you are confused because your pseudocode looks like real code to me. I may be wrong, but we are taught to write pseudocode differently, using plain English phrases.

How do I generate a list of n unique random numbers in Ruby?

This is what I have so far:
myArray.map!{ rand(max) }
Obviously, however, sometimes the numbers in the list are not unique. How can I make sure my list only contains unique numbers without having to create a bigger list from which I then just pick the n unique numbers?
Edit:
I'd really like to see this done w/o loop - if at all possible.
(0..50).to_a.sort{ rand() - 0.5 }[0..x]
(0..50).to_a can be replaced with any array.
0 is "minvalue", 50 is "max value"
x is "how many values i want out"
of course, its impossible for x to be permitted to be greater than max-min :)
In expansion of how this works
(0..5).to_a ==> [0,1,2,3,4,5]
[0,1,2,3,4,5].sort{ -1 } ==> [0, 1, 2, 4, 3, 5] # constant
[0,1,2,3,4,5].sort{ 1 } ==> [5, 3, 0, 4, 2, 1] # constant
[0,1,2,3,4,5].sort{ rand() - 0.5 } ==> [1, 5, 0, 3, 4, 2 ] # random
[1, 5, 0, 3, 4, 2 ][ 0..2 ] ==> [1, 5, 0 ]
Footnotes:
It is worth mentioning that at the time this question was originally answered, September 2008, that Array#shuffle was either not available or not already known to me, hence the approximation in Array#sort
And there's a barrage of suggested edits to this as a result.
So:
.sort{ rand() - 0.5 }
Can be better, and shorter expressed on modern ruby implementations using
.shuffle
Additionally,
[0..x]
Can be more obviously written with Array#take as:
.take(x)
Thus, the easiest way to produce a sequence of random numbers on a modern ruby is:
(0..50).to_a.shuffle.take(x)
This uses Set:
require 'set'
def rand_n(n, max)
randoms = Set.new
loop do
randoms << rand(max)
return randoms.to_a if randoms.size >= n
end
end
Ruby 1.9 offers the Array#sample method which returns an element, or elements randomly selected from an Array. The results of #sample won't include the same Array element twice.
(1..999).to_a.sample 5 # => [389, 30, 326, 946, 746]
When compared to the to_a.sort_by approach, the sample method appears to be significantly faster. In a simple scenario I compared sort_by to sample, and got the following results.
require 'benchmark'
range = 0...1000000
how_many = 5
Benchmark.realtime do
range.to_a.sample(how_many)
end
=> 0.081083
Benchmark.realtime do
(range).sort_by{rand}[0...how_many]
end
=> 2.907445
Just to give you an idea about speed, I ran four versions of this:
Using Sets, like Ryan's suggestion.
Using an Array slightly larger than necessary, then doing uniq! at the end.
Using a Hash, like Kyle suggested.
Creating an Array of the required size, then sorting it randomly, like Kent's suggestion (but without the extraneous "- 0.5", which does nothing).
They're all fast at small scales, so I had them each create a list of 1,000,000 numbers. Here are the times, in seconds:
Sets: 628
Array + uniq: 629
Hash: 645
fixed Array + sort: 8
And no, that last one is not a typo. So if you care about speed, and it's OK for the numbers to be integers from 0 to whatever, then my exact code was:
a = (0...1000000).sort_by{rand}
Yes, it's possible to do this without a loop and without keeping track of which numbers have been chosen. It's called a Linear Feedback Shift Register: Create Random Number Sequence with No Repeats
[*1..99].sample(4) #=> [64, 99, 29, 49]
According to Array#sample docs,
The elements are chosen by using random and unique indices
If you need SecureRandom (which uses computer noise instead of pseudorandom numbers):
require 'securerandom'
[*1..99].sample(4, random: SecureRandom) #=> [2, 75, 95, 37]
How about a play on this? Unique random numbers without needing to use Set or Hash.
x = 0
(1..100).map{|iter| x += rand(100)}.shuffle
You could use a hash to track the random numbers you've used so far:
seen = {}
max = 100
(1..10).map { |n|
x = rand(max)
while (seen[x])
x = rand(max)
end
x
}
Rather than add the items to a list/array, add them to a Set.
If you have a finite list of possible random numbers (i.e. 1 to 100), then Kent's solution is good.
Otherwise there is no other good way to do it without looping. The problem is you MUST do a loop if you get a duplicate. My solution should be efficient and the looping should not be too much more than the size of your array (i.e. if you want 20 unique random numbers, it might take 25 iterations on average.) Though the number of iterations gets worse the more numbers you need and the smaller max is. Here is my above code modified to show how many iterations are needed for the given input:
require 'set'
def rand_n(n, max)
randoms = Set.new
i = 0
loop do
randoms << rand(max)
break if randoms.size > n
i += 1
end
puts "Took #{i} iterations for #{n} random numbers to a max of #{max}"
return randoms.to_a
end
I could write this code to LOOK more like Array.map if you want :)
Based on Kent Fredric's solution above, this is what I ended up using:
def n_unique_rand(number_to_generate, rand_upper_limit)
return (0..rand_upper_limit - 1).sort_by{rand}[0..number_to_generate - 1]
end
Thanks Kent.
No loops with this method
Array.new(size) { rand(max) }
require 'benchmark'
max = 1000000
size = 5
Benchmark.realtime do
Array.new(size) { rand(max) }
end
=> 1.9114e-05
Here is one solution:
Suppose you want these random numbers to be between r_min and r_max. For each element in your list, generate a random number r, and make list[i]=list[i-1]+r. This would give you random numbers which are monotonically increasing, guaranteeing uniqueness provided that
r+list[i-1] does not over flow
r > 0
For the first element, you would use r_min instead of list[i-1]. Once you are done, you can shuffle the list so the elements are not so obviously in order.
The only problem with this method is when you go over r_max and still have more elements to generate. In this case, you can reset r_min and r_max to 2 adjacent element you have already computed, and simply repeat the process. This effectively runs the same algorithm over an interval where there are no numbers already used. You can keep doing this until you have the list populated.
As far as it is nice to know in advance the maxium value, you can do this way:
class NoLoopRand
def initialize(max)
#deck = (0..max).to_a
end
def getrnd
return #deck.delete_at(rand(#deck.length - 1))
end
end
and you can obtain random data in this way:
aRndNum = NoLoopRand.new(10)
puts aRndNum.getrnd
you'll obtain nil when all the values will be exausted from the deck.
Method 1
Using Kent's approach, it is possible to generate an array of arbitrary length keeping all values in a limited range:
# Generates a random array of length n.
#
# #param n length of the desired array
# #param lower minimum number in the array
# #param upper maximum number in the array
def ary_rand(n, lower, upper)
values_set = (lower..upper).to_a
repetition = n/(upper-lower+1) + 1
(values_set*repetition).sample n
end
Method 2
Another, possibly more efficient, method modified from same Kent's another answer:
def ary_rand2(n, lower, upper)
v = (lower..upper).to_a
(0...n).map{ v[rand(v.length)] }
end
Output
puts (ary_rand 5, 0, 9).to_s # [0, 8, 2, 5, 6] expected
puts (ary_rand 5, 0, 9).to_s # [7, 8, 2, 4, 3] different result for same params
puts (ary_rand 5, 0, 1).to_s # [0, 0, 1, 0, 1] repeated values from limited range
puts (ary_rand 5, 9, 0).to_s # [] no such range :)

Resources