How to optimize the ruby code iterates several integers in a range? - ruby

New to Ruby, and trying to find a 3 digits number "abc":
for a in 0..9
for b in 0..9
for c in 0..9
if a*a+b*b+c*c == (100*a+10*b+c)/11.0
puts "#{a}#{b}#{c}"
end
end
end
end
This is too lengthy, is any way to optimize it, or write it in another "ruby" way?

Solution from: Wolfram Alpha :)
Here's another fun solution. Not really faster, just more compact and perhaps more ruby-like if that was what you were looking for:
(0..9).to_a.repeated_permutation(3).select { |a,b,c|
a*a+b*b+c*c == (100*a+10*b+c)/11.0
}
=> [[0, 0, 0], [5, 5, 0], [8, 0, 3]]

This is equivalent to finding a,b,c such that
100*a + 10*b + c = 11 * (a*a + b*b +c*c)
i.e. 100*a + 10*b + c must be divisible by 11. Simple number theory tells you that when a,b,c are digits, this means that
`a + c - b`
must be a multiple of 11 so
`a + c = b or a + c = 11 +b`
So for a given values of a and b you only need to check two values of c : b -a and 11 +b -a rather than 10. You can cut the search space in two again: if a > b you only need to check the latter of those two values and if a <= b you need only check the former.
Thus instead of checking 1000 triplets of numbers you should only need to check 100, which should be 10 times faster.
for a in 0..9
for b in 0..9
if a > b
c = 11 +b -a
else
c = b - a
end
if a*a+b*b+c*c == (100*a+10*b+c)/11.0
puts "#{a}#{b}#{c}"
end
end
end

Related

How do I use multiple integers in an equation (Ruby)

I coded a conversion tool from binary to integer, but it had a limit on how large the number can be. So, I tried to code a formula for binary. I came up with an equation, so I tried to put it into code. Everything worked, except for applying the equation to each digit. This is the equation I came up with:
Let d represent the integer
Let z represent any (and every) digit
d = z[2^(z-1)]
This is what I've coded so far:
answer = gets.chomp
n = answer.reverse # reverses the answer
y1 = answer.size # the amount of digits in the answer
x1 = answer
z = (1..y1).each { |z| puts z } # every number between 1 and number of digits
w = (1..1).each.to_a * y1.to_i #in case I need to multiply the entire array
s = x1 # [z] - 1 # any given digit minus one
v = 2 ** s.to_i # exponent
u = z.zip(w).map{|x, y| x * y} # an array: [1, 2, 3]
print u
t = u.to_i # Tried converting to integer
puts x1[t]
But when I ran that, for example, with the number 1011, I got this error:
[1, 2, 3, 4]
undefined method `to_i' for [1, 2, 3, 4]:Array
Did you mean? to_s
to_a
to_h
(repl):16:in `<main>'
I feel like I have tried everything, but if you somehow find a way to apply the equation to every digit, or if you come up with a simpler equation, please tell me.
This return an array u = z.zip(w).map{|x, y| x * y} so you are triying to conver an array to integer. If you want, you can do something like this:
array = [1,0,1] #your binary in array form
s = array.join('') #transform it into string
s.to_i(2) #this return the integer and result (2) represents base
Check this link
And for better: array.join('').to_i(2)

how to save variable within a while loop

I have a variable x which is set to 10. And I want to write a while loop that increments that. I know you can easily just use a for-loop for this, but easy isn't fun. The code I have is:
def add(a)
g = a + 1
puts g
end
def loop(d)
x = 0
while x <= 4
x += 1
add(d)
end
end
loop(9)
When ran I get 9, four times. How can I get this code to have an output of 9, 10, 11, 12?
Your problem is that you say add(d) and d is the parameter of your loop, loop(d). Ruby does blindly what you tell him : loop(9), so here d=9 and remains equal to 9. You need to increment the value of d. To do that add will now return the incremented value, and we assign the returned value to d (in loop).
To solve your problem you will want to do something like :
def add(a)
g = a + 1
puts g
g
end
def loop(d)
x = 0
while x <= 4
x += 1
d = add(d)
end
end
loop(9)
BUT and that's a huge but, your code is not the ruby way at all.
If I were to do it I would do it like this:
def loop(start_number, repeat_number, increment)
repeat_number.times do
start_number += increment
p start_number
end
end
loop(9, 4, 1)
If you rly wanna to use while and your expected outpit is Array numbers 9 up to 12.
Simply define the array variable before while loop and return it like this:
def loop(number)
x, a = 0, []
while x <= 3
a << number + x
x += 1
end
a
end
loop(9)
# => [9, 10, 11, 12]
BETTER WAY
But better way is use some ruby functions like times and map
in this case we use times and map
def loop(d)
4.times.map{|i| d + i}
end
p loop(9)
# => [9, 10, 11, 12]
maybe this will helps.

implement shell sort by ruby

I try to implement shell sort by ruby.
def shell_sort(list)
d = list.length
return -1 if d == 0
(0...list.length).each do |i|
d = d / 2
puts "d:#{d}"
(0...(list.length-d)).each do |j|
if list[j] >= list[j+d]
list[j], list[j+d] = list[j+d], list[j]
end
end
puts list.inspect
break if d == 1
end
list
end
puts shell_sort([10,9,8,7,6,5,4,3,2,1]).inspect
but the result is incorrect.
=>[2, 1, 3, 4, 5, 7, 6, 8, 9, 10]
I don't know where going wrong, hope someone can help me. Thanks in advance!
I referenced Shell Sort in here : Shell Sort - Wikepedia, and from that I have understood your algorithm is wrong. Iteration of gap sequence is alright, I mean you iterate only upto d/2 == 1.
But for a gap, let's say 2, you simply iterate from 0 to list.length-2 and swap every j and j+2 elements if list[j] is greater than list[j+2]. That isn't even a proper insertion sort, and Shell Sort requires Insertion sorts on gaps. Also Shell Sort requires that after you do an x gap sort, every xth element, starting from anywhere will be sorted (see the example run on the link and you can verify yourself).
A case where it can wrong in a 2 gap sort pass :
list = 5,4,3,2,1
j = 0 passed :
list = 3,4,5,2,1
j = 1 passed :
list = 3,2,5,4,1
j = 2 passed
list = 3,2,1,4,5
After it completes, you can see that every 2nd element starting from 0 isn't in a sorted order. I suggest that you learn Insertion Sort first, then understand where and how it is used in Shell Sort, and try again, if you want to do it by yourself.
Anyway, I have written one (save it for later if you want) taking your method as a base, with a lot of comments. Hope you get the idea through this. Also tried to make the outputs clarify the how the algorithm works.
def shell_sort(list)
d = list.length
return -1 if d == 0
# You select and iterate over your gap sequence here.
until d/2 == 0 do
d = d / 2
# Now you pick up an index i, and make sure every dth element,
# starting from i is sorted.
# i = 0
# while i < list.length do
0.step(list.length) do |i|
# Okay we picked up index i. Now it's just plain insertion sort.
# Only difference is that we take elements with constant gap,
# rather than taking them up serially.
# igap = i + d
# while igap < list.length do
(i+d).step(list.length-1, d) do |igap|
# Just like insertion sort, we take up the last most value.
# So that we can shift values greater than list[igap] to its side,
# and assign it to a proper position we find for it later.
temp = list[igap]
j = igap
while j >= i do
break if list[j] >= list[j - d]
list[j] = list[j-d]
j -= d
end
# Okay this is where it belongs.
list[j] = temp
#igap += d
end
# i += 1
end
puts "#{d} sort done, the list now : "
puts list.inspect
end
list
end
list = [10,9,8,7,6,5,4,3,2,1]
puts "List before sort : "
puts list.inspect
shell_sort(list)
puts "Sorted list : "
puts list.inspect
I think your algorithm needs a little tweaking.
The reason it fails is simply because on the last run (when d == 1) the smallest element (1) isn't near enough the first element to swap it in in one go.
The easiest way to make it work is to "restart" your inner loop whenever elements switch places. So, a little bit rough solution would be something like
(0...(list.length-d)).each do |j|
if list[j] >= list[j+d]
list[j], list[j+d] = list[j+d], list[j]
d *= 2
break
end
end
This solution is of course far from optimal, but should achieve required results with as little code as possible.
You should just do a last run on array. To simplify your code I extracted exchange part into standalone fucntion so you could see now where you should do this:
def exchange e, list
(0...(list.length-e)).each do |j|
if list[j] >= list[j+e]
list[j], list[j+e] = list[j+e], list[j]
end
end
end
def shell_sort(list)
d = list.length
return -1 if d == 0
(0...list.length).each do |i|
d = d / 2
puts "d:#{d}"
exchange(d, list)
puts list.inspect
if d == 1
exchange(d, list)
break
end
end
list
end
arr = [10,9,8,7,6,5,4,3,2,1]
p shell_sort(arr)
Result:
#> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Finding the longest collatz sequence for any number under 100

def longest_collatz_sequence(n)
longest_sequence = []
(1..n).each do |a|
sequence = [a]
until sequence.last == 1
if a % 2 == 0
sequence.push(a/2)
else
sequence.push(3 * a + 1)
end
end
if sequence.length > longest_sequence.length
longest_sequence = sequence
end
end
longest_sequence
end
longest_collatz_sequence(n) works for n = 2, but doesn't work for n > 2. What am I doing wrong?
Thanks!
You always push the same number, a/2 or 3*a + 1 and obviously it never stops if a/2 != 1 != 3*a + 1. You probably want to use the last number of sequence instead of a:
if sequence.last % 2 == 0
sequence.push(sequence.last/2)
else
sequence.push(3 * sequence.last + 1)
end
irb> longest_collatz_sequence(3)
=> [3, 10, 5, 16, 8, 4, 2, 1]
These kinds of bugs can be tracked down by using a debugger or introducing print statements at appropriate places, so that you can trace what is going on in your program. I think this might help you help yourself in the future, because not all bugs are easy to find using visual inspection alone.

Ruby - Find element not in common for two arrays

I've been thinking about a following problem - there are two arrays, and I need to find elements not common for them both, for example:
a = [1,2,3,4]
b = [1,2,4]
And the expected answer is [3].
So far I've been doing it like this:
a.select { |elem| !b.include?(elem) }
But it gives me O(N ** 2) time complexity. I'm sure it can be done faster ;)
Also, I've been thinking about getting it somehow like this (using some method opposite to & which gives common elements of 2 arrays):
a !& b #=> doesn't work of course
Another way might be to add two arrays and find the unique element with some method similar to uniq, so that:
[1,1,2,2,3,4,4].some_method #=> would return 3
The simplest (in terms of using only the arrays already in place and stock array methods, anyway) solution is the union of the differences:
a = [1,2,3,4]
b = [1,2,4]
(a-b) | (b-a)
=> [3]
This may or may not be better than O(n**2). There are other options which are likely to give better peformance (see other answers/comments).
Edit: Here's a quick-ish implementation of the sort-and-iterate approach (this assumes no array has repeated elements; otherwise it will need to be modified depending on what behavior is wanted in that case). If anyone can come up with a shorter way to do it, I'd be interested. The limiting factor is the sort used. I assume Ruby uses some sort of Quicksort, so complexity averages O(n log n) with possible worst-case of O(n**2); if the arrays are already sorted, then of course the two calls to sort can be removed and it will run in O(n).
def diff a, b
a = a.sort
b = b.sort
result = []
bi = 0
ai = 0
while (ai < a.size && bi < b.size)
if a[ai] == b[bi]
ai += 1
bi += 1
elsif a[ai]<b[bi]
result << a[ai]
ai += 1
else
result << b[bi]
bi += 1
end
end
result += a[ai, a.size-ai] if ai<a.size
result += b[bi, b.size-bi] if bi<b.size
result
end
As #iamnotmaynard noted in the comments, this is traditionally a set operation (called the symmetric difference). Ruby's Set class includes this operation, so the most idiomatic way to express it would be with a Set:
Set.new(a) ^ b
That should give O(n) performance (since a set membership test is constant-time).
a = [1, 2, 3]
b = [2, 3, 4]
a + b - (a & b)
# => [1, 4]
The solution for Array divergences is like:
a = [1, 2, 3]
b = [2, 3, 4]
(a - b) | (b - a)
# => [1, 4]
You can also read my blog post about Array coherences

Resources