Efficient way to do addition on big array - ruby

I have an array with +20000 integer elements.
I want to create a new array where each element in the old array is added a modifying number. On a small sample array it would look like this:
old_array = [2,5,6,8]
modifying_number = 3
new_array = [5,8,9,11]
Is there any more efficient way than doing an iteration like this?
class Array
def addition_by(x)
collect { |n| n + x }
end
end

No. N iterations are the minimal complexity of this algorithm.
You can do it in place by modifying source array with collect!(if you for some reasons not need a source array). Complexity will be the same, additional big object will not created.

20k records is not much to worry about performance.
ary = Array.new(20000) { 1 }
ary.map! { |el| el + 1 }
would work totally fine.
I would just suggest to modify the initial array inplace instead of creating a new one (using method with bang), so it will definitely use less resources.

I guess it would mean implementing map in another way? This question deals with such a task. I've included benchmarks of the answers by #JörgWMittag and #uishra. Although it has to be said speed was not a requirement in the linked question so the answers cannot be criticised in that regard. I've also included #CarySwoveland's answer from this question.
require 'fruity'
require 'matrix'
class Array
#jörg_w_mittag
def new_map
return enum_for(__callee__) unless block_given?
inject([]) {|acc, el| acc << yield(el) }
end
#uishra
def my_map(&block)
result = []
each do |element|
result << block.call(element)
end
result
end
#cary_swoveland
def vec_map(k)
(Vector[*[k]*self.size] + Vector[*self]).to_a
end
end
arr = (1..30000).to_a
k = 3
10.times do
compare do
core_map { ar = arr.dup; ar.map { |n| n + k } }
jörg_w_mittag { ar = arr.dup; ar.new_map { |n| n + k } }
uishra { ar = arr.dup; ar.my_map { |n| n + k } }
cary_swoveland { ar = arr.dup; ar.vec_map k }
end
puts
end
A summary of the results/output:
Results on five occasions
#Running each test once. Test will take about 1 second.
#core_map is faster than jörg_w_mittag by 2x ± 1.0
#jörg_w_mittag is similar to uishra
#uishra is similar to cary_swoveland
Results on two occasions
#Running each test once. Test will take about 1 second.
#core_map is faster than jörg_w_mittag by 2x ± 0.1
#jörg_w_mittag is similar to uishra
#uishra is similar to cary_swoveland
Results on three occasions
#Running each test once. Test will take about 1 second.
#core_map is faster than uishra by 2x ± 1.0
#uishra is similar to jörg_w_mittag
#jörg_w_mittag is similar to cary_swoveland

require 'matrix'
class Array
def vec_map(k)
(Vector[*[k]*self.size] + Vector[*self]).to_a
end
end
[1,2,3].vec_map 4
#=> [5, 6, 7]

Related

Permutational Primes Kata optimatization

I am solving the Permutational Primes Kata using Ruby. I manage to find out Brute-force solution but it exceeds the time limits. I need to optimize my code but I don't have any idea how to do this. The Kata.
require 'prime'
def permutational_primes(n_max, k_perms)
result_h = {}
result_keys = []
Prime.each(n_max) do |prime|
perms = prime.digits.permutation.to_a.map(&:join).map(&:to_i).uniq
prime_no_length = prime.to_s.length
perms = perms.delete_if { |el| el.to_s.length < prime_no_length }
# elimianate number greater than n_max
perms = perms.delete_if { |el| el > n_max }
next if (perms & result_keys).any?
perms = perms.delete_if { |el| !Prime.prime?(el) }
# minus one because we include
if perms.count - 1 == k_perms
result_h[prime] = perms
result_keys.append(prime)
end
end
return [0, 0, 0] if result_keys.empty?
[result_h.count, result_keys[0], result_keys[result_keys.count-1]]
end
Some low-hanging fruit:
Benchmarking is one way to understand what parts are more computationally expensive than others (likely Prime.prime?)
Find work that you're doing repeatedly or checks for information that you already know every Prime.each loop and cache it, trading memory for computation, e.g. Prime.prime?
All the common Enumerable methods create new Arrays. Instead, reuse existing arrays, e.g. map!
I have not yet thoroughly tested the following, but this general approach should speed things up considerably. There are two main elements that improved efficiency: use of the method Prime::each and maintaining a set of prime permutations already tested, to avoid unnecessary testing of duplicates.
require 'prime'
require 'set'
def permutational_primes(nMax, permutes)
skips = Set.new
enum = Prime.each
arr = []
while (prime = enum.next) < nMax
next if skips.include?(prime)
a = prime.digits.permutation.with_object([]) do |perm,a|
next if perm[-1].zero?
n = perm.reverse.join.to_i
if n < nMax && !skips.include?(n) && Prime.prime?(n)
skips << n
a << n
end
end
a.each { |n| skips << n }
next if a.size != permutes + 1
arr << a.min
end
[arr.size, arr.size.zero? ? 0 : arr.min, arr.size.zero? ? 0 : arr.max]
end
permutational_primes(1000, 3)
#=> [3, 149, 379]
This passes all tests but times out. I'm working on another optimization.
Explanation is under construction...

Ruby - finding the first N palindromic prime using lazy evaluation

i think my code is correct - yet i do not return an array in time for N = 200. Error is "Terminated due to timeout"
what can i do to improve the performance of this code?
def is_palindrome? (n)
n.to_s==n.to_s.reverse
end
def is_prime? (n)
return false if n< 2
(2..Math.sqrt(n)).none? {|f| n % f == 0}
end
prime_palindrome =-> (n) do
1.upto(Float::INFINITY).lazy.select { |n| is_prime?(n) && is_palindrome(n) }.first(n)
end
n = gets.to_i
p prime_palindrome.call(n)
Ruby knows how to do this faster.
require 'prime'
Prime.each.lazy.
select { |p| p.to_s.then { |s| s == s.reverse } }.
take(200).to_a
Lazy enumerators (as used in #Amadan's answer) are useful but seem to have a reputation for being somewhat slow. I thought it might be interesting to do a simple benchmark here, comparing Amadan's answer with a straightforward calculation using a while loop.
require 'prime'
Lazy enumerator
def amadan(n)
Prime::EratosthenesSieve.instance.send(:initialize)
Prime.each.lazy.
select { |p| p.to_s.then { |s| s == s.reverse } }.
take(n).to_a
end
while loop
def cary(n)
Prime::EratosthenesSieve.instance.send(:initialize)
arr = []
enum = Prime.each
while n > 0
p = enum.next
s = p.to_s
if s == s.reverse
arr << p
n -= 1
end
end
arr
end
The first line of each method, Prime::EratosthenesSieve... is included to make the benchmark more realistic. See the discussion in the comments.
Benchmark
require 'fruity'
#
n = 200
compare(amadan: -> { amadan(n) }, cary: -> { cary(n) })
Running each test once. Test will take about 10 seconds.
cary is faster than amadan by 10.000000000000009% ± 1.0%
Results are similar for other values of `n`.

Project Euler Task 17. Find number of all letters from a range of numbers

The problem:
If the numbers 1 to 5 are written out in words: one, two, three, four, five, then there are 3 + 3 + 5 + 4 + 4 = 19 letters used in total.
If all the numbers from 1 to 1000 (one thousand) inclusive were written out in words, how many letters would be used?
My solution:
require 'humanize'
arr, total = [], 0
(1..1000).to_a.map(&:humanize).each {|x| arr << x.delete(" ").delete("-")}.map {|y| total += y.length }
p total
Other solution privided, by #Arif in other question, gives the right answer (according to Project Euler):
p (1..1000).to_a.map(&:humanize).join.tr(" -", "").size
Why my solution is wrong? From my knowledge, these two implementations should give the same output...
Edit:
Found one more strange thing.
If I change 1000 to any number up to 20 and implementations looks like this:
(1..20).to_a.map(&:humanize).each {|x| arr << x.delete(" ").delete("-")}.map {|y| total += y.length }
p (1..20).to_a.map(&:humanize).join.tr(" -", "").size
I receive the same answer.....? What?
Your problem is that you are messing map with each.
(1..1000).to_a.map(&:humanize)
.each {|x| arr << x.delete(" ").delete("-") } # ??? each
.map {|y| total += y.length } # ??? map
The call to Enumerable#each returns the original Enumerable, not arr or whatever you’ve expected. Yes, you fulfilled arr, but the latter map is called on the original array, having all these spaces and dashes.
The correct way to implement it your way:
(1..1000).to_a.map(&:humanize)
.map { |x| x.delete(" ").delete("-") } # sic!, no need for arr
.each { |y| total += y.length } # no need to map here
Or, more ruby idiomatic:
(1..1000).to_a.map(&:humanize)
.map { |x| x.delete(" ").delete("-") }
.reduce(:+) # reduce by summing everything up
Or, even better (to_a is redundant here):
(1..1000).map(&:humanize)
.reduce(0) { |memo, x| memo + x.delete(" ").delete("-").length }

Ruby lazy enumerator returning different object types depending on use

I was attempting to solve Project Euler #58 in a functional manner with ruby.
Briefly, I created an enumerator to return the corner number of each ring. I was then chaining functional operators on the enumerator. When I get my result, I find that it has a different class depending on how I use it.
spiral = Enumerator.new do |yielder|
n = 3
step = 2
loop do
vals = n.step(nil, step).take(4)
yielder.yield vals
step += 2
n = vals.last + step
end
end
primes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113]
levels = spiral
.lazy
.map { |ring| ring.count { |n| primes.include? n } }
.with_object({:total=>1.0, :primes=>0})
.take_while do |ring_primes, counts|
counts[:total] += 4
counts[:primes] += ring_primes
(counts[:primes] / counts[:total]) > 0.5
end
The class of levels is a lazy enumerator. I would expect it to contain the number of primes in each ring [3, 2, 3, etc.] - see the project euler reference.
If I just print from the enumerator, I get what I expect:
levels.each do |level|
puts "#{level}"
end
Returns:
3
2
3
1
But if I loop .with_index I get an array result back where the expected value is the first member and the second is my .with_object parameter
levels.each.with_index do |level, ix|
puts "#{ix}: #{level}"
end
Returns:
0: [3, {:total=>5.0, :primes=>3}]
1: [2, {:total=>9.0, :primes=>5}]
2: [3, {:total=>13.0, :primes=>8}]
3: [1, {:total=>17.0, :primes=>9}]
Why does the lazy enumerator work this way and how could I predict for it in the future?
Update
I asked around on the IRC ruby channel and no one there had any idea about it. They said they had discussed it a day or two ago and hadn't come to any conclusions.
In general, it seems one must just deal with it and move on.
What's happening here is you're conveniently ignoring the structure that's returned and plucking out the first item to display. In this case the first item is the counts structure you produce.
Have a look at this:
levels.each do |*level|
puts level.inspect
end
That shows you what's actually in the levels results. When Ruby calls a lambda it will discard any additional data that doesn't fit with the number of arguments the block accepts.
If you don't need that metadata, strip it out:
levels = spiral
.lazy
.map { |ring| ring.count { |n| primes.include? n } }
.with_object({:total=>1.0, :primes=>0})
.take_while do |ring_primes, counts|
counts[:total] += 4
counts[:primes] += ring_primes
(counts[:primes] / counts[:total]) > 0.5
end
.map { |r,_| r }
That removes the extraneous element in the results.
Here's a way of cleaning up your Enumerator a bit:
class Spiral
include Enumerable
def each
Enumerator.new do |yielder|
n = 3
step = 2
loop do
vals = n.step(nil, step).take(4)
yielder.yield vals
step += 2
n = vals.last + step
end
end
end
end
Then you can create one with:
Spiral.new.each ...

More ruby-like solution to this problem?

I am learning ruby and practicing it by solving problems from Project Euler.
This is my solution for problem 12.
# Project Euler problem: 12
# What is the value of the first triangle number to have over five hundred divisors?
require 'prime'
triangle_number = ->(num){ (num *(num + 1)) / 2 }
factor_count = ->(num) do
prime_fac = Prime.prime_division(num)
exponents = prime_fac.collect { |item| item.last + 1 }
fac_count = exponents.inject(:*)
end
n = 2
loop do
tn = triangle_number.(n)
if factor_count.(tn) >= 500
puts tn
break
end
n += 1
end
Any improvements that can be made to this piece of code?
As others have stated, Rubyists will use methods or blocks way more than lambdas.
Ruby's Enumerable is a very powerful mixin, so I feel it pays here to build an enumerable in a similar way as Prime. So:
require 'prime'
class Triangular
class << self
include Enumerable
def each
sum = 0
1.upto(Float::INFINITY) do |i|
yield sum += i
end
end
end
end
This is very versatile. Just checking it works:
Triangular.first(4) # => [1, 3, 7, 10]
Good. Now you can use it to solve your problem:
def factor_count(num)
prime_fac = Prime.prime_division(num)
exponents = prime_fac.collect { |item| item.last + 1 }
exponents.inject(1, :*)
end
Triangular.find{|t| factor_count(t) >= 500} # => 76576500
Notes:
Float::INFINITY is new to 1.9.2. Either use 1.0/0, require 'backports' or do a loop if using an earlier version.
The each could be improved by first checking that a block is passed; you'll often see things like:
def each
return to_enum __method__ unless block_given?
# ...
Rather than solve the problem in one go, looking at the individual parts of the problem might help you understand ruby a bit better.
The first part is finding out what the triangle number would be. Since this uses sequence of natural numbers, you can represent this using a range in ruby. Here's an example:
(1..10).to_a => [1,2,3,4,5,6,7,8,9,10]
An array in ruby is considered an enumerable, and ruby provides lots of ways to enumerate over data. Using this notion you can iterate over this array using the each method and pass a block that sums the numbers.
sum = 0
(1..10).each do |x|
sum += x
end
sum => 55
This can also be done using another enumerable method known as inject that will pass what is returned from the previous element to the current element. Using this, you can get the sum in one line. In this example I use 1.upto(10), which will functionally work the same as (1..10).
1.upto(10).inject(0) {|sum, x| sum + x} => 55
Stepping through this, the first time this is called, sum = 0, x = 1, so (sum + x) = 1. Then it passes this to the next element and so sum = 1, x = 2, (sum + x) = 3. Next sum = 3, x = 3, (sum + x) = 6. sum = 6, x = 4, (sum + x) = 10. Etc etc.
That's just the first step of this problem. If you want to learn the language in this way, you should approach each part of the problem and learn what is appropriate to learn for that part, rather than tackling the entire problem.
REFACTORED SOLUTION (though not efficient at all)
def factors(n)
(1..n).select{|x| n % x == 0}
end
def triangle(n)
(n * (n + 1)) / 2
end
n = 2
until factors(triangle(n)).size >= 500
puts n
n += 1
end
puts triangle(n)
It looks like you are coming from writing Ocaml, or another functional language. In Ruby, you would want to use more def to define your methods. Ruby is about staying clean. But that might also be a personal preference.
And rather than a loop do you could while (faction_count(traingle_number(n)) < 500) do but for some that might be too much for one line.

Resources