How to break a range down into smaller non-overlapping ranges - ruby

What is the most beautiful way to break a larger range into smaller non overlapping ranges?
range = 1..375
Desired Output:
1..100
101..200
201..300
301..375

You can use #each_slice in combination with #map:
(1..375).each_slice(100).map { |a,*,b| (a..b) }
#=> [1..100, 101..200, 201..300, 301..375]

The following may not be the most elegant solution but it is designed to be relatively efficient, by avoiding the creation of temporary arrays.
def divide_range(range, sz)
start = range.begin
(range.size/sz).times.with_object([]) do |_,arr|
arr << (start..start+sz-1)
start += sz
end.tap { |arr| (arr << (start..range.end)) if start < range.end }
end
divide_range(1..375, 100)
#=> [1..100, 101..200, 201..300, 301..375]
divide_range(1..400, 100)
#=> [1..100, 101..200, 201..300, 301..400]
divide_range(50..420, 50)
#=> [50..99, 100..149, 150..199, 200..249, 250..299, 300..349,
# 350..399, 400..420]
n = 1_000_000_000_000
divide_range(1..n, n/2)
#=> [1..500000000000, 500000000001..1000000000000]

Currently, I'm using the step method, but I don't like having to check the top of the range and do calculations to avoid overlapping:
For example:
range = 1..375
interval = 100
range.step(interval).each do |start|
stop = [range.last, start + (interval - 1)].min
puts "#{start}..#{stop}"
end
I've taken this code and extended Range as well:
class Range
def in_sub_ranges(interval)
step(interval).each do |start|
stop = [range.last, start + (interval - 1)].min
yield(start..stop)
end
end
end
This allows me to do
range.in_sub_ranges(100) { |sub| puts sub }

Related

In ruby, Is it better to receive *array or to duplicate and array inside a method?

I was playing around with some implementations of Quicksort in Ruby. After implementing some of the inlace algorithms, I felt that using Ruby's partition method, even though it would not provide an in-place solution, it would provide a very nice readable solution.
My first solution was this, which other than always using the last element of the array as the pivot, seemed pretty nice.
def quick_sort3(ary)
return ary if ary.size <= 1
left,right = ary.partition { |v| v < ary.last }
pivot_value = right.pop
quick_sort3(left) + [pivot_value] + quick_sort3(right)
end
After some searching I found this answer which had a very similar solution with a better choice of the initial pivot, reproduced here using the same variable names and block passed to partition.
def quick_sort6(*ary)
return ary if ary.empty?
pivot_value = ary.delete_at(rand(ary.size))
left,right = ary.partition { |v| v < pivot_value }
return *quick_sort6(*left), pivot_value, *quick_sort6(*right)
end
I felt I could improve my solution by using the same method to select a random pivot.
def quick_sort4(ary)
return ary if ary.size <= 1
pivot_value = ary.delete_at(rand(ary.size))
left,right = ary.partition { |v| v < pivot_value }
quick_sort4(left) + [pivot_value] + quick_sort4(right)
end
The down side to this version quick_sort4 vs the linked answer quick_sort6, is that quick_sort4 changes the input array, while quick_sort6 does not. I am assuming this is why Jorg chose to receive the splat array vs array?
My fix for this was to simply duplicate the passed in array and then perform the delete_at on the copied array rather than the original array.
def quick_sort5(ary_in)
return ary_in if ary_in.size <= 1
ary = ary_in.dup
pivot_value = ary.delete_at(rand(ary.size))
left,right = ary.partition { |v| v < pivot_value }
quick_sort5(left) + [pivot_value] + quick_sort5(right)
end
My question is there any significant differences between quick_sort6 which uses the splats and quick_sort5 which uses dup? I am assuming the use of the splats was to avoid changing the input array, but is there something else I am missing?
In terms of performance, quick_sort6 is your best bet. Using some random data:
require 'benchmark'
def quick_sort3(ary)
return ary if ary.size <= 1
left,right = ary.partition { |v| v < ary.last }
pivot_value = right.pop
quick_sort3(left) + [pivot_value] + quick_sort3(right)
end
def quick_sort6(*ary)
return ary if ary.empty?
pivot_value = ary.delete_at(rand(ary.size))
left,right = ary.partition { |v| v < pivot_value }
return *quick_sort6(*left), pivot_value, *quick_sort6(*right)
end
def quick_sort4(ary)
return ary if ary.size <= 1
pivot_value = ary.delete_at(rand(ary.size))
left,right = ary.partition { |v| v < pivot_value }
quick_sort4(left) + [pivot_value] + quick_sort4(right)
end
def quick_sort5(ary_in)
return ary_in if ary_in.size <= 1
ary = ary_in.dup
pivot_value = ary.delete_at(rand(ary.size))
left,right = ary.partition { |v| v < pivot_value }
quick_sort5(left) + [pivot_value] + quick_sort5(right)
end
random_arrays = Array.new(5000) do
Array.new(500) { rand(1...500) }.uniq
end
Benchmark.bm do |benchmark|
benchmark.report("quick_sort3") do
random_arrays.each do |ra|
quick_sort3(ra.dup)
end
end
benchmark.report("quick_sort6") do
random_arrays.each do |ra|
quick_sort6(ra.dup)
end
end
benchmark.report("quick_sort4") do
random_arrays.each do |ra|
quick_sort4(ra.dup)
end
end
benchmark.report("quick_sort5") do
random_arrays.each do |ra|
quick_sort5(ra.dup)
end
end
end
Gives as result
user system total real
quick_sort3 1.389173 0.019380 1.408553 ( 1.411771)
quick_sort6 0.004399 0.000022 0.004421 ( 0.004487)
quick_sort4 1.208003 0.002573 1.210576 ( 1.214131)
quick_sort5 1.458327 0.000867 1.459194 ( 1.459882)
The problem with splat style in this case is that it would create an awkward API.
Most times the consumer code would have an array of things that need to be sorted:
stuff = [1, 2, 3]
sort(stuff)
The splat style makes the consumers do this instead:
stuff = [1, 2, 3]
sort(*stuff)
The two calls might end up doing the same thing, but as a user I am sorting an array, therefore I expect to pass the array to the method, not pass each array element individually to the method.
Another label for this phenomenon in abstraction leakage - you are allowing the implementation of the sort method define its interface. Usually in Ruby this is frowned upon.

Euler 23 in Ruby

All right. I think I have the right idea to find the solution to Euler #23 (The one about finding the sum of all numbers that can't be expressed as the sum of two abundant numbers).
However, it is clear that one of my methods is too damn brutal.
How do you un-brute force this and make it work?
sum_of_two_abunds?(num, array) is the problematic method. I've tried pre-excluding certain numbers and it's still taking forever and I'm not even sure that it's giving the right answer.
def divsum(number)
divsum = 1
(2..Math.sqrt(number)).each {|i| divsum += i + number/i if number % i == 0}
divsum -= Math.sqrt(number) if Math.sqrt(number).integer?
divsum
end
def is_abundant?(num)
return true if divsum(num) > num
return false
end
def get_abundants(uptonum)
abundants = (12..uptonum).select {|int| is_abundant?(int)}
end
def sum_of_two_abunds?(num, array)
#abundant, and can be made from adding two abundant numbers.
array.each do |abun1|
array.each do |abun2|
current = abun1+abun2
break if current > num
return true if current == num
end
end
return false
end
def non_abundant_sum
ceiling = 28123
sum = (1..23).inject(:+) + (24..ceiling).select{|i| i < 945 && i % 2 != 0}.inject(:+)
numeri = (24..ceiling).to_a
numeri.delete_if {|i| i < 945 && i % 2 != 0}
numeri.delete_if {|i| i % 100 == 0}
abundants = get_abundants(ceiling)
numeri.each {|numerus| sum += numerus if sum_of_two_abunds?(numerus, abundants) == false}
return sum
end
start_time = Time.now
puts non_abundant_sum
#Not enough numbers getting excluded from the total.
duration = Time.now - start_time
puts "Took #{duration} s "
Solution 1
A simple way to make it a lot faster is to speed up your sum_of_two_abunds? method:
def sum_of_two_abunds?(num, array)
array.each do |abun1|
array.each do |abun2|
current = abun1+abun2
break if current > num
return true if current == num
end
end
return false
end
Instead of that inner loop, just ask the array whether it contains num - abun1:
def sum_of_two_abunds?(num, array)
array.each do |abun1|
return true if array.include?(num - abun1)
end
false
end
That's already faster than your Ruby code, since it's simpler and running faster C code. Also, now that that idea is clear, you can take advantage of the fact that the array is sorted and search num - abun1 with binary search:
def sum_of_two_abunds?(num, array)
array.each do |abun1|
return true if array.bsearch { |x| num - abun1 <=> x }
end
false
end
And making that Rubyish:
def sum_of_two_abunds?(num, array)
array.any? do |abun1|
array.bsearch { |x| num - abun1 <=> x }
end
end
Now you can get rid of your own special case optimizations and fix your incorrect divsum (which for example claims that divsum(4) is 5 ... you should really compare against a naive implementation that doesn't try any square root optimizations).
And then it should finish in well under a minute (about 11 seconds on my PC).
Solution 2
Or you could instead ditch sum_of_two_abunds? entirely and just create all sums of two abundants and nullify their contribution to the sum:
def non_abundant_sum
ceiling = 28123
abundants = get_abundants(ceiling)
numeri = (0..ceiling).to_a
abundants.each { |a| abundants.each { |b| numeri[a + b] = 0 } }
numeri.compact.sum
end
That runs on my PC in about 3 seconds.

Code wars: Flap Display with while loops

I'm trying to work through a level 5 kata by using while loops. Essentially the problem is to turn each letter rotors[n] number of times and then move on to the next rotors number until you get an output word.
flap_display(["CAT"],[1,13,27])
should output ["DOG"]
Here's what I have so far
def flap_display(lines, rotors)
stuff = "ABCDEFGHIJKLMNOPQRSTUVWXYZ?!##&()|<>.:=-+*/0123456789"
i = 0
j = 0
new_word = lines
while i < rotors.length
while j < new_word[0].length
new_word[0][j] = stuff[stuff.index(new_word[0][j]) + rotors[i]]
j += 1
end
i += 1
j = 0
end
new_word
end
This technically traverses the stuff string and assigns the right letters. However it fails two important things: it does not skip each letter when it rotates to the correct position (C should stop rotating when it hits D, A when it hits O etc) and it does not account for reaching the end of the stuff list and eventually returns a nil value for stuff[stuff.index(new_word[0][j]) + rotors[i]]. How can I fix these two problems using basic loops and enumerables or maybe a hash?
A fuller statement of the problem is given here. This is one Ruby-like way it could be done.
FLAPS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ ?!##&()|<>.:=-+*/0123456789"
NBR_FLAPS = FLAPS.size
def flap_display(str, rot)
rot_cum = rot.each_with_object([]) { |n,a| a << a.last.to_i + n }
str.gsub(/./) { |c| FLAPS[(c.ord + rot_cum.shift - 65) % NBR_FLAPS] }
end
flap_display("CAT", [1,13,27])
#=> "DOG"
flap_display("DOG", [-1,-13,-27])
#=> "CAT"
flap_display("CAT", [5,37,24])
#=> "H*&"
'A'.ord #=> 65 and rot_cum contains the cumulative values of rot:
arr = [1, 13, 27]
rot_cum = arr.each_with_object([]) { |n,a| a << a.last.to_i + n }
#=> [1, 14, 41]
I've written a.last.to_i rather than a.last to deal with the case where a is empty, so a.last #=> nil, meaning a.last.to_i => nil.to_i => 0. See NilClass#to_i. Those opposed to such trickery could write:
rot_cum = arr.drop(1).each_with_object([arr.first]) { |n,a| a << a.last + n }

Finding the sum of the digits of a factorial

factorial_sum(5) should return 3. The error I'm getting is that "inject is an undefined method". I was also wondering if it's possible to combine the two functions. I wasn't sure as I am just starting out on recursion. Thanks!
def factorial_sum(x)
factorial = factorial(x)
factorial.to_s.split('').collect { |i| i.to_i }
sum = factorial.inject { |sum, n| sum + n }
end
def factorial(x)
if x < 0
return "Negative numbers don't have a factorial"
elsif x == 0
1
else
factorial = x * factorial(x - 1)
end
end
puts factorial_sum(5)
factorial.to_s.split('').collect { |i| i.to_i }
This line is a no-op. You build a list and then throw it away. You probably meant factorial = ...
I have to say though that this would be pretty easy to find with a little effort and some print statements...
By the way, here's a slightly more concise way:
(1..x).reduce(:*).to_s.chars.map(&:to_i).reduce(:+)
A direct way without temporarily converting it into strings, and without recursion.
s, q = 0, 120
while q > 0
q, r = q.divmod(10)
s += r
end
s # => 3

more ruby way of doing project euler #2

I'm trying to learn Ruby, and am going through some of the Project Euler problems. I solved problem number two as such:
def fib(n)
return n if n < 2
vals = [0, 1]
n.times do
vals.push(vals[-1]+vals[-2])
end
return vals.last
end
i = 1
s = 0
while((v = fib(i)) < 4_000_000)
s+=v if v%2==0
i+=1
end
puts s
While that works, it seems not very ruby-ish—I couldn't come up with any good purely Ruby answer like I could with the first one ( puts (0..999).inject{ |sum, n| n%3==0||n%5==0 ? sum : sum+n }).
For a nice solution, why don't you create a Fibonacci number generator, like Prime or the Triangular example I gave here.
From this, you can use the nice Enumerable methods to handle the problem. You might want to wonder if there is any pattern to the even Fibonacci numbers too.
Edit your question to post your solution...
Note: there are more efficient ways than enumerating them, but they require more math, won't be as clear as this and would only shine if the 4 million was much higher.
As demas' has posted a solution, here's a cleaned up version:
class Fibo
class << self
include Enumerable
def each
return to_enum unless block_given?
a = 0; b = 1
loop do
a, b = b, a + b
yield a
end
end
end
end
puts Fibo.take_while { |i| i < 4000000 }.
select(&:even?).
inject(:+)
My version based on Marc-André Lafortune's answer:
class Some
#a = 1
#b = 2
class << self
include Enumerable
def each
1.upto(Float::INFINITY) do |i|
#a, #b = #b, #a + #b
yield #b
end
end
end
end
puts Some.take_while { |i| i < 4000000 }.select { |n| n%2 ==0 }
.inject(0) { |sum, item| sum + item } + 2
def fib
first, second, sum = 1,2,0
while second < 4000000
sum += second if second.even?
first, second = second, first + second
end
puts sum
end
You don't need return vals.last. You can just do vals.last, because Ruby will return the last expression (I think that's the correct term) by default.
fibs = [0,1]
begin
fibs.push(fibs[-1]+fibs[-2])
end while not fibs[-1]+fibs[-2]>4000000
puts fibs.inject{ |sum, n| n%2==0 ? sum+n : sum }
Here's what I got. I really don't see a need to wrap this in a class. You could in a larger program surely, but in a single small script I find that to just create additional instructions for the interpreter. You could select even, instead of rejecting odd but its pretty much the same thing.
fib = Enumerator.new do |y|
a = b = 1
loop do
y << a
a, b = b, a + b
end
end
puts fib.take_while{|i| i < 4000000}
.reject{|x| x.odd?}
.inject(:+)
That's my approach. I know it can be less lines of code, but maybe you can take something from it.
class Fib
def first
#p0 = 0
#p1 = 1
1
end
def next
r =
if #p1 == 1
2
else
#p0 + #p1
end
#p0 = #p1
#p1 = r
r
end
end
c = Fib.new
f = c.first
r = 0
while (f=c.next) < 4_000_000
r += f if f%2==0
end
puts r
I am new to Ruby, but here is the answer I came up with.
x=1
y=2
array = [1,2]
dar = []
begin
z = x + y
if z % 2 == 0
a = z
dar << a
end
x = y
y = z
array << z
end while z < 4000000
dar.inject {:+}
puts "#{dar.sum}"
def fib_nums(num)
array = [1, 2]
sum = 0
until array[-2] > num
array.push(array[-1] + array[-2])
end
array.each{|x| sum += x if x.even?}
sum
end

Resources