I was given this problem to solve with Ruby:
Compute the sum of cubes for a given range a through b. Write a method called sum_of_cubes to accomplish this task.
I wrote this:
def sum_of_cubes(a, b)
sum = 0
for x in a..b
c = x ** 3
end
sum += c
end
I got the value of the cube of b. What is wrong with this code? How can I solve this problem with a simple loop?
Thanks!
I would use Enumerable#reduce
def sum_of_cubes min, max
(min..max).reduce(0) { |a, b| a + b ** 3 }
end
A little explanation of what's happening here
We start with range (min..max) which is an Enumerable
irb> (1..3).is_a? Enumerable
=> true
Using the reduce instance method we get from Enumerable, we can use a block of code that gets called for each item in our (enumerable) range, and ultimately returns a single value.
The function name makes sense if you think "take my group of items and reduce them to a single value."
Here's our block
{ |a, b| a + b ** 3 }
We called reduce with 0 which is the initial value given to the block's a param
The return value of the block is passed to the block's a param on subsequent calls
Each item in the range will be passed to the block's b param
Let's step through and see how it works
(1..3).reduce(0) { |a, b| a + b ** 3 }
the first block call gets a=0 (initial value) and b=1 (first item in our range)
the return value of our block is 0 + 1 ** 3 or 1
the second block call gets a=1 (return value from the last call) and b=2 (the second item in our range)
the return value of our block is 1 + 2 ** 3 or 9
the third block call gets a=9 (return value from the last call) and b=3 (the third and last item in our range)
the return value of our block is 9 + 3 ** 3 or 36
the final return value of reduce is the last-called block's return value
in this case 36
You need to have sum += c inside the loop. And then return sum when done.
Here’s another way to calculate this. It doesn’t address your problems with your loop but I think it’s worth mentioning.
The sum of cubes of integers 13 + 23 + 33 + ... + n3 is given by the formula (n(n + 1)/2)2, so the sum of cubes of a given range min..max is therefore given by:
(max(max + 1)/2)2 - ((min-1)((min-1) + 1)/2)2
In code this could look like:
def sum_of_cubes_fixed min, max
lower = min - 1
(((max * (max + 1))/2) ** 2) - (((lower * (lower + 1))/2) ** 2)
end
This code avoids the loop, and so is O(1) rather than O(n) (almost – I’m hand waving a bit here, the time complexity of the multiplications and exponentiations will depend on the size of the numbers). For small sized ranges you won’t notice this, but for larger sizes the difference between this and the loop version becomes increasingly obvious. I haven’t done any strict benchmarks, but a quick test on my machine with the range 1 to 10,000,000 takes several seconds with the reduce method but is almost instantaneous with this method.
Normally I would just use reduce for something like this, but the structure of the task suggested that there might be a better way. With the help of Google I found the formula and came up with a more efficient solution (at least for large ranges).
(a..b).map{|i| i**3 }.inject(&:+) map and inject does the job, elegantly.
EDIT: Although it walks through the list twice ;)
Related
I need to DRY this code but I don't know how.
I tried to dry the if condition but I don't know how to put the while in this.
def sum_with_while(min, max)
# CONSTRAINT: you should use a while..end structure
array = (min..max).to_a
sum = 0
count = 0
if min > max
return -1
else
while count < array.length
sum += array[count]
count += 1
end
end
return sum
end
Welcome to stack overflow!
Firstly, I should point out that "DRY" stands for "Don't Repeat Yourself". Since there's no repetition here, that's not really the problem with this code.
The biggest issue here is it's unrubyish. The ruby community has certain things it approves of, and certain things it avoids. That said, while loops are themselves considered bad ruby, so if you've been told to write it with a while loop, I'm guessing you're trying to get us to do your homework for you.
So I'm going to give you a couple of things to do a web search for that will help start you off:
ruby guard clauses - this will reduce your if-else-end into a simple if
ruby array pop - you can do while item = array.pop - since pop returns nil once the array is empty, you don't need a count. Again, bad ruby to do this... but maybe consider while array.any?
ruby implicit method return - generally we avoid commands we don't need
It's worth noting that using the techniques above, you can get the content of the method down to 7 reasonably readable lines. If you're allowed to use .inject or .sum instead of while, this whole method becomes 2 lines.
(as HP_hovercraft points out, the ternary operator reduces this down to 1 line. On production code, I'd be tempted to leave it as 2 lines for readability - but that's just personal preference)
You can put the whole thing in one line with a ternary:
def sum_with_while(min, max)
min > max ? -1 : [*(min..max)].inject(0){|sum,x| sum + x }
end
This is one option, cleaning up your code, see comments:
def sum_with_while(range) # pass a range
array = range.to_a
sum, count = 0, 0 # parallel assignment
while count < array.length
sum += array[count]
count += 1
end
sum # no need to return
end
sum_with_while(10..20)
#=> 165
More Rubyish:
(min..max).sum
Rule 1: Choose the right algorithm.
You wish to compute an arithmetic series.1
def sum_with_while(min, max)
max >= min ? (max-min+1)*(min+max)/2 : -1
end
sum_with_while(4, 4)
#=> 4
sum_with_while(4, 6)
#=> 15
sum_with_while(101, 9999999999999)
#=> 49999999999994999999994950
1. An arithmetic series is the sum of the elements of an arithmetic sequence. Each term of the latter is computed from the previous one by adding a fixed constant n (possibly negative). Heremax-min+1 is the number of terms in the sequence and (min+max)/2, if (min+max) is even, is the average of the values in the sequence. As (max-min+1)*(min+max) is even, this works when (min+max) is odd as well.
This part of code calculates the value of each arithmetic series up to and including the number that the user put in:
print "enter a number: "
num = gets.to_i
(1..num).inject(0) do |res, e|
res += e
p res
end
I think that (1..num) is the range, with num being the user input. I know that inject combines all elements of enum by applying a binary operation specified by a block or a symbol that names a method or operator.
I don't understand what each element in this line does:
(1..num).inject(0) do |res, e|
What does |res, e| mean? It must be the block that defines what inject does, but what does for instance res and e stand for? (e is probably element?)
What does (0) stand for?
What does the command do do?
what is its connection in regard to (1..num) and inject(0)?
Am I right to assume that p at the end just stands for puts or print?
inject takes an optional start value, and a block taking an intermediate value and element and returning a new intermediate value.
So:
What does (0) stand for?
The start value parameter to inject.
What does the command "do" do?
It is not a command; it marks the start of the block (terminated by end). .inject(0) do ... end is almost (except for some syntactic issues) the same as .inject(0) { ... }. Usually, do ... end is used for multi-line blocks and { ... } for single-line blocks, but it is not a rule.
What does |res, e| mean?
Those are the block parameters (intermediate value and current element), here probably called after "result" and "element", respectively.
Let's see on a simplified example: (1..3).inject(0) do |res, e| res + e end will set the intermediate result to 0. Then it will pass this intermediate result and the first element of the enumerable being injected: res is 0 and e is 1. The value of the block is the value of its last expression, which is 1 (result of 0 + 1). This 1 now becomes the new intermediate value, and 2 becomes the next current element. The value of the block is 3 (result of 1 + 2). In the next iteration, intermediate value is 3, and the current element also 3, resulting in 6 (3 + 3). The range will stop yielding elements now that we reached its upper boundary, and inject returns with the last intermediate result calculated, 6.
Also, the last question am I right to assume that "p" at the end just stands for puts or print?
Almost. p is its own beast. p x is roughly synonymous with puts x.inspect; x - i.e. it prints the value in a bit different format, and unlike puts which always returns nil, p returns the value unchanged. Thus, p res at the end of your block will not destroy the code by making it return nil, but transparently return res.
inject is a method that boils a collection (eg an array or range) down to a single value. It does this by executing the block once for each element in the collection. The block takes two arguments: the current value being worked on, and the single value that will eventually be returned. inject itself takes one argument (aside from the block), which is its initial starting value.
Take this example.
x = [1, 2, 3, 4, 5].inject(0) do |result, current|
result + current
end
We have a list of numbers, [1, 2, 3, 4, 5]. We're going to boil them down into one single number.
We start with 0, because that's inject's argument. That means that the first time the block runs, result will be 0.
So the block runs for the first time. result is 0, current is 1, the first element. We say result + current (which is 1). It's the last expression inside the block, so it's what that block 'returns'.
At the end of the block, inject says "Okay, do we have more elements in the collection?" Yeah. So the block runs again. This time, result is whatever the last block returned, which was 1, and current is the second element, 2.
The block runs, and finishes with result + current, or 1 + 2, which is 3. There are still elements left, so we run again. This time, result is 3, and current is 3. result + current, 6. Still more values to go, on the next run result is 6 and current is 4. 6 + 4 = 10. Still more values to go, on the next run result is 10 and current is 5. 10 + 5 = 15.
Then the block finishes, and there are no more elements left. So inject itself returns with the final value, 15. So in the end, x = 15. We boiled down our list into one number by adding things up.
res in your example stands for result, and e for element. You can call them anything you want. You might call them sum while adding, or product if multiplying. But they don't have to be numbers. You could use inject to boil an array of strings, a range of characters, an array of arrays, whatever collection you want. The block just tells it how.
Excited to see that you're learning Ruby, welcome!
At your level of expertise best learn from a book.
Get yourself a copy of the "Pickaxe Book" — this is by far the best book.
The first edition fir Ruby 1.9 is available online, http://ruby-doc.com/docs/ProgrammingRuby
And here is a popular online tutorial, http://poignant.guide
Some quick answers
1..num is a range object
.inject(0) do ... end is a method call with TWO parameters, the value 0 and a code block
do |a, b| ... end is a code block with two parameters
res and e are VERY bad variable names, maybe better use sum and each?
p is a global method that prints debug information, similar to puts but not the same
Hope that helps to unblock you.
I don't quite understand how to "initialize a multidimensional array to equal 1" as the initial for loops seem to suggest here. I haven't learned to properly read pseudocode, and I don't fully understand how this program works.
function countRoutes(m,n)
grid ← array[m + 1][n + 1]
for i = 0 to m do
grid[i][0] ← 1
end for
for j = 0 to n do
grid[0][j] ← 1
end for
for i = 1 to m do
for j = 1 to n do
grid[i][j] ← grid[i − 1][j] + grid[i][j − 1]
end for
end for
return grid[m][n]
end function
Thanks for your help!
This isn't hard to translate.. Ruby uses = instead of left arrow for assignment, and def instead of function to define a subroutine (which it calls a method instead of a function), but that's about it. Let's go through it.
function countRoutes(m,n)
That's beginning a function definition. In Ruby we use a method instead, and the keyword to define such a thing is def. It's also convention in Ruby to use snake_case for multiword names instead of camelCase:
def count_routes(m, n)
Now to create the grid:
grid ← array[m + 1][n + 1]
Ruby arrays are dynamic, so you don't normally need to specify the size at creation time. But that also means you don't get initialization or two-dimensionality for free. So what we have to do here is create an array of m+1 arrays, each of which can be empty (we don't need to specify that the sub-arrays need to hold n+1 items). Ruby's Array constructor has a way to do just that:
grid = Array.new(m+1) do [] end
Now the initialization. Ruby technically has for loops, but nobody uses them. Instead, we use iterator methods. For counting loops, there's a method on integers called times. But the pseudocode counts from 0 through m inclusive; times also starts at 0, but only counts up to one less than the invocant (so that way when you call 3.times, the loop really does execute "three times", not four). In this case, that means to get the behavior of the pseudocode, we need to call times on m+1 instead of m:
(m+1).times do |i|
grid[i][0] = 1
end
As an aside, we could also have done that part of the initialization inside the original array creation:
grid = Array.new(m+1) do [1] end
Anyway, the second loop, which would be more awkward to incorporate into the original creation, works the same as the first. Ruby will happily extend an array to assign to not-yet-existent elements, so the fact that we didn't initialize the subarrays is not a problem:
(n+1).times do |j|
grid[0][j] = 1
end
For the nested loops, the pseudocode is no longer counting from 0, but from 1. Counting from 1 through m is the same number of loop iterations as counting from 0 through m-1, so the simplest approach is to let times use its natural values, but adjust the indexes in the assignment statement inside the loop. That is, where the pseudocode starts counting i from 1 and references i-1 and i, the Ruby code starts counting i from 0 and references i and i+1 instead.
m.times do |i|
n.times do |j|
grid[i+1][j+1] = grid[i][j+1] + grid[i+1][j]
end
end
And the return statement works the same, although in Ruby you can leave it off:
return grid[m][n]
end
Putting it all together, you get this:
def count_routes(m, n)
grid = Array.new(m+1) do [1] end
(n+1).times do |j|
grid[0][j] = 1
end
m.times do |i|
n.times do |j|
grid[i+1][j+1] = grid[i][j+1] + grid[i+1][j]
end
end
return grid[m][n]
end
The notation grid[i][j] ← something means assigning something to the element of grid taking place on i-th line in j-th position. So the first two loops here suggest setting all values of the first column and the first row of the grid (correspondingly, the first and the second loops) to 1.
I have an array with 12 entries.
When doing 12+1, I want to get the entry 1 of the array
When doing 12+4, I want to get the entry 4 of the array
etc...
I'm done with
cases_to_increment.each do |k|
if k > 12
k = k-12
end
self.inc(:"case#{k}", 1)
end
I found a solution with modulo
k = 13%12 = 1
k = 16%12 = 4
I like the modulo way but 12%12 return 0 and I need only numbers between 1..12
There is a way to do that without condition ?
You almost had the solution there yourself. Instead of a simple modulo, try:
index = (number % 12) + 1
Edit: njzk2 is correct, modulo is a very expensive function if you are using it with a value that is not a power of two. If, however, your total number of elements (the number you are modulo-ing with) is a power of 2, the calculation is essentially free.
Here's the exercise:
You have been given a list of sequential numbers from 1 to 10,000, but
they are all out of order; furthermore, a single number is missing
from the list. The object of the task is to find out which number is
missing.
The strategy to this problem is to sum the elements in the array, then sum the range 1 to 10,000, and subtract the difference. This is equal to the missing number. The formula for calculating the sum of the range from 1..n being n(n+1)/2.
This is my current approach:
def missing_number(array)
sum = 0
array.each do |element|
sum += element
end
((10000*10001)/2) - sum
end
Where I am getting tripped up is the output when I input an array such as this:
puts missing_number(*1..10000) #=> 0
Why does this happen?
Thanks!
No need to sort the array. An array of length N is supposed to have all but one of the numbers 1..(N+1) so the array length + 1 is the basis for figuring out what the grand_sum would be if all values were there.
def missing_number(array)
grand_sum = (array.length + 1) * (array.length + 2) / 2
grand_sum - array.inject(:+)
end
ADDENDUM
This method takes an array as an argument, not a range. You can't use a range directly because there wouldn't be a missing value. Before calling the method you need some mechanism for generating an array which meets the problem description. Here's one possible solution:
PROBLEM_SIZE = 10_000
# Create an array corresponding to the range
test_array = (1..PROBLEM_SIZE).to_a
# Target a random value for deletion -- rand(N) generates values in
# the range 0..N-1, inclusive, so add 1 to shift the range to 1..N
target_value = rand(PROBLEM_SIZE) + 1
# Delete the value and print so we can check the algorithm
printf "Deleting %d from the array\n", test_array.delete(target_value)
# Randomize the order of remaining values, as per original problem description
test_array.shuffle!
# See what the missing_number() method identifies as the missing number
printf "Algorithm identified %d as the deleted value\n", \
missing_number(test_array)
An alternative approach to solving the problem if it's not performance critical, because of its readability:
def missing_number(array)
(1..10_000).to_a - array
end
Instead of *1..10000, the argument should be (1..10000).to_a.
You shouldn't be using *1..10000, this will just expand to 10,000 arguments. (1..10000).to_a will return zero because there are no elements missing between 1..10000 you need to remove one. Below is some code with a detailed explanation.
def missing_number array
# put elements in order
array.sort!
# get value of last array element
last = array[-1]
# compute the expected total of the numbers
# 1 - last
# (n + 1)(n)/2
expected = (last + 1) * (last / 2)
# actual sum
actual = array.inject{|sum,x| sum + x}
# find missing number by subtracting
(expected - actual)
end
test = (1..10000).to_a
test.delete 45
puts "Missing number is: #{missing_number(test)}"