Why is sum so much faster than inject(:+)? - ruby

So I was running some benchmarks in Ruby 2.4.0 and realized that
(1...1000000000000000000000000000000).sum
calculates immediately whereas
(1...1000000000000000000000000000000).inject(:+)
takes so long that I just aborted the operation. I was under the impression that Range#sum was an alias for Range#inject(:+) but it seems like that is not true. So how does sum work, and why is it so much faster than inject(:+)?
N.B. The documentation for Enumerable#sum (which is implemented by Range) does not say anything about lazy evaluation or anything along those lines.

Short answer
For an integer range :
Enumerable#sum returns (range.max-range.min+1)*(range.max+range.min)/2
Enumerable#inject(:+) iterates over every element.
Theory
The sum of integers between 1 and n is called a triangular number, and is equal to n*(n+1)/2.
The sum of integers between n and m is the triangular number of m minus the triangular number of n-1, which is equal to m*(m+1)/2-n*(n-1)/2, and can be written (m-n+1)*(m+n)/2.
Enumerable#sum in Ruby 2.4
This property in used in Enumerable#sum for integer ranges :
if (RTEST(rb_range_values(obj, &beg, &end, &excl))) {
if (!memo.block_given && !memo.float_value &&
(FIXNUM_P(beg) || RB_TYPE_P(beg, T_BIGNUM)) &&
(FIXNUM_P(end) || RB_TYPE_P(end, T_BIGNUM))) {
return int_range_sum(beg, end, excl, memo.v);
}
}
int_range_sum looks like this :
VALUE a;
a = rb_int_plus(rb_int_minus(end, beg), LONG2FIX(1));
a = rb_int_mul(a, rb_int_plus(end, beg));
a = rb_int_idiv(a, LONG2FIX(2));
return rb_int_plus(init, a);
which is equivalent to:
(range.max-range.min+1)*(range.max+range.min)/2
the aforementioned equality!
Complexity
Thanks a lot to #k_g and #Hynek-Pichi-Vychodil for this part!
sum
(1...1000000000000000000000000000000).sum
requires three additions, a multiplication, a substraction and a division.
It's a constant number of operations, but multiplication is O((log n)²), so Enumerable#sum is O((log n)²) for an integer range.
inject
(1...1000000000000000000000000000000).inject(:+)
requires 999999999999999999999999999998 additions!
Addition is O(log n), so Enumerable#inject is O(n log n).
With 1E30 as input, inject with never return. The sun will explode long before!
Test
It's easy to check if Ruby Integers are being added :
module AdditionInspector
def +(b)
puts "Calculating #{self}+#{b}"
super
end
end
class Integer
prepend AdditionInspector
end
puts (1..5).sum
#=> 15
puts (1..5).inject(:+)
# Calculating 1+2
# Calculating 3+3
# Calculating 6+4
# Calculating 10+5
#=> 15
Indeed, from enum.c comments :
Enumerable#sum method may not respect method redefinition of "+"
methods such as Integer#+.

Related

Code Optimization - Generating Prime Numbers

I am trying to write a code for the following problem:
Input
The input begins with the number t of test cases in a single line (t<=10). In each of the next t lines there are two numbers m and n (1 <= m <= n <= 1000000000, n-m<=100000) separated by a space.
Output
For every test case print all prime numbers p such that m <= p <= n, one number per line, test cases separated by an empty line.
Sample Input:
2
1 10
3 5
Sample Output:
2
3
5
7
3
5
My code:
def prime?(number)
return false if number == 1
(2..number-1).each do |n|
return false if number % n == 0
end
true
end
t = gets.strip.to_i
for i in 1..t
mi, ni = gets.strip.split(' ')
mi = mi.to_i
ni = ni.to_i
i = mi
while i <= ni
puts i if prime?(i)
i += 1
end
puts "\n"
end
The code is running fine, only problem I am having is that it is taking a lot of time when run against big input ranges as compared to other programming languages.
Am I doing something wrong here? Can this code be further optimized for faster runtime?
I have tried using a for loop, normal loop, creating an array and then printing it.
Any suggestions.
Ruby is slower than some other languages, depending on what language you compare it to; certainly slower than C/C++. But your problem is not the language (although it influences the run-time behavior), but your way of finding primes. There are many better algorithms for finding primes, such as the Sieve of Eratosthenes or the Sieve of Atkin. You might also read the “Generating Primes” page on Wikipedia and follow the links there.
By the way, for the Sieve of Eratosthenes, there is even a ready-to-use piece of code on Stackoverflow. I'm sure a little bit of googling will turn up implementations for other algorithms, too.
Since your problem is finding primes within a certain range, this is the Sieve of Eratosthenes code found at the above link modified to suit your particular problem:
def better_sieve_upto(first, last)
sieve = [nil, nil] + (2..last).to_a
sieve.each do |i|
next unless i
break if i*i > last
(i*i).step(last, i) {|j| sieve[j] = nil }
end
sieve.reject {|i| !i || i < first}
end
Note the change from "sieve.compact" to a complexer "sieve.reject" with a corresponding condition.
Return true if the number is 2, false if the number is evenly divisible by 2.
Start iterating at 3, instead of 2. Use a step of two.
Iterate up to the square root of the number, instead of the number minus one.
def prime?(number)
return true if number == 2
return false if number <= 1 or number % 2 == 0
(3..Math.sqrt(number)).step(2) do |n|
return false if number % n == 0
end
true
end
This will be much faster, but still not very fast, as #Technation explains.
Here's how to do it using the Sieve of Eratosthenes built into Ruby. You'll need to precompute all the primes up to the maximum maximum, which will be very quick, and then select the primes that fall within each range.
require 'prime'
ranges = Array.new(gets.strip.to_i) do
min, max = gets.strip.split.map(&:to_i)
Range.new(min, max)
end
primes = Prime.each(ranges.map(&:max).max, Prime::EratosthenesGenerator.new)
ranges.each do |range|
primes.each do |prime|
next if prime < range.min
break if prime > range.max
puts prime
end
primes.rewind
puts "\n"
end
Here's how the various solutions perform with the range 50000 200000:
Your original prime? function: 1m49.639s
My modified prime? function: 0m0.687s
Prime::EratosthenesGenerator: 0m0.221s
The more ranges being processed, the faster the Prime::EratosthenesGenerator method should be.

How to add incrementally in ruby

How do I add incrementally?
x = 1
while x < 365
x+=x
puts x
end
but this isn't right
Thanks!
Mathematically speaking, you're looking for the summation of n for n = 1 to y. According to WolframAlpha, this summation can be reduced to y(y+1)/2. Therefore, you can calculate this as:
x = 365*(365+1)/2 #=> 66795
No need for any loops. This code way more efficient; O(1) instead of O(n).
If I correctly understood what you need:
(1..365).reduce(0) { |memo, i| memo + i }
#⇒ 66795
or, in a short form (credits to #Jörg W. Mittag):
(1..365).reduce(:+)
Here we use standard reduce procedure on Range. First of all we construct range, containing integers from 1 up to 365. Then we iterate these integers, carrying the total (named memo according to ruby convention).

How to create a method that returns the nth prime number?

I'm trying to write a method that returns the nth prime number.
I've worked out a solution but the problem is in my method. I create a large array of numbers that seems to process super slow. (1..104729).to_a to be exact. I chose 104729 because the max n can be is 10000 and the 10000th integer is 104729. I'm looking for a way to optimize my method.
Is 104729 is too large a value? Is there a way to write this so that I'm not creating a large array?
Here's the method:
def PrimeMover(num)
def is_prime(x)
i = 0
nums = (2..x).to_a
while nums[i] < nums.max
if x % nums[i] != 0
i += 1
else
return false
end
end
return true
end
primes_arr = (3..104729).to_a.select {|y| is_prime(y)}
primes_arr[num]
end
require "prime"
def find_prime(nth)
Prime.take(nth).last
end
Combine Ruby's built-in prime library, and a lazy enumerator for performance:
require 'prime'
(1...100_000).lazy.select(&:prime?).take(100).to_a
Or simply, as highlighted by Arturo:
Prime.take(100)
You can use Ruby's built in #prime? method, which seems pretty efficient.
The code:
require 'prime'
primes_arr = (3..104729).to_a.select &:prime?
runs in 2-3 seconds on my machine, which I find somewhat acceptable.
If you need even better performance or if you really need to write your own method, try implementing the Sieve of Erathostenes. Here are some Ruby samples of that: http://rosettacode.org/wiki/Sieve_of_Eratosthenes#Ruby
Here's an optimal a trial division implementation of is_prime without relying on the Prime class:
A prime number is a whole number divisible only by 1 and itself, and 1 is not prime. So we want to know if x divides into anything less than x and greater than 1. So we start the count at 2, and we end at x - 1.
def prime?(x)
return false if x < 2
2.upto(x - 1) do |n|
return false if (x % n).zero?
end
true
end
As soon as x % n has a remainder, we can break the loop and say this number is not prime. This saves you from looping over the entire range. If all the possible numbers were exhausted, we know the number is prime.
This is still not optimal. For that you would need a sieve, or a different detection algorithm to trial division. But it's a big improvement on your code. Taking the nth up to you.

Largest Prime Factor Ruby using Fermat's factorization method

I have this code that seems to be working for number 6-8 digits, in a normal time period.
When I enter bigger values the amount get ridiculously bug. It takes more than 4 hours to complete.
Here is my code.
#Fermat's factorization method
def get_largest_prime n
t=(Math.sqrt(n)+1).floor
k=0
prime_numbers=[]
while (t+k)<n
element = (t+k)**2-n
if is_integer? Math.sqrt(element)
#store prime numbers
prime_numbers << t+k+Math.sqrt(element)
prime_numbers << t+k-Math.sqrt(element)
#puts "Prime Factors of #{n} are: #{t+k+Math.sqrt(element)} and #{t+k-Math.sqrt(element)}"
end
k+=1
end
puts "Prime Factors: "+prime_numbers.to_s
end
#making sure 450.0 is 450, for example.
def is_integer? number
number.to_i == number ? true : false
end
get_largest_prime 600851475143
Running this will take more than 4 hours.
But running it for value ' 600851' for example or ' 60085167' does not take a lot of time. Any help ?
First note that Fermat factorisation doesn't give you prime factors in general.
Then, you run it until t+k >= n, that means you run the while loop n - t times, since t is roughly sqrt(n), that is an O(n) algorithm. For a largish n like 600851475143 (about 6*10^11), that is bound to take long.
You need to change the algorithm. When you have found a pair of divisors (both larger than 1), factorise them both recursively. If the smaller of the found factors is 1, that is a prime factor.
Doing that (forgive the bad style, I barely know ruby):
#Fermat's factorization method
def get_largest_prime n
t=(Math.sqrt(n)+1).floor
k=0
prime_numbers=[]
while (t+k)<n
element = (t+k)**2-n
if is_integer? Math.sqrt(element)
#store prime numbers
a = t+k+Math.sqrt(element)
b = t+k-Math.sqrt(element)
if b == 1
prime_numbers << a
break
end
prime_numbers += get_largest_prime a
prime_numbers += get_largest_prime b
break
#puts "Prime Factors of #{n} are: #{t+k+Math.sqrt(element)} and #{t+k-Math.sqrt(element)}"
end
k+=1
end
return prime_numbers
end
#making sure 450.0 is 450, for example.
def is_integer? number
number.to_i == number ? true : false
end
a = get_largest_prime 600851475143
puts "Prime Factors: "+a.to_s
solves the given problem quickly.
However, it will still take a long time for numbers that have no divisors close to the square root.
The standard factorisation by trial division has much better worst-case behaviour (O(sqrt(n) worst case). A mixed approach can be slightly faster than pure trial division, though.
Two effects here:
1) When an integer gets larger than 2**31 in Ruby, it uses a different, and slower, representation
2) There are no known factorisation algorithms that don't eventually perform badly once the number gets large enough - technically they all get slower worse than any polynomial of the (number of digits of) the number you want to factorise.
You could speed things up by using
Math.sqrt(element)
less. Assign result of it to a variable, before all the tests. Note this will not "fix" your problem. Ultimately it won't run fast enough above a certain number - even if you transferred everything to C (although you might squeeze out a couple of extra digits before C got slow)
Possibly, you can try the code below.
def prime_factor limit
(2..Math.sqrt(limit).to_i).inject([]) do |memo, var|
memo << var if limit % var == 0 and !memo.any? {|d| var % d == 0}
memo
end
end
prime_result = prime_factor 600851475143
puts prime_result.max
The less loops you use, the faster your code will run ;-) (less cpu cycles). Try to do everything recursively like here on this program that finds the largest prime factor

multiplying floating point numbers produces zero

the code below outputs 0.0. is this because of the overflow? how to avoid it? if not, why?
p ((1..100000).map {rand}).reduce :*
I was hoping to speed up this code:
p r.reduce(0) {|m, v| m + (Math.log10 v)}
and use this instead:
p Math.log10 (r.reduce :*)
but apparently this is not always possible...
The values produced by rand are all between 0.0 and 1.0. This means that on each multiplication, your number gets smaller. So by the time you have multiplied 1000 of them, it is probably indistinguishable from 0.
At some point, ruby will take your number to be so small that it is 0. for instance: 2.0e-1000 # => 0
Every multiplication reduces your number by about 1/21, so after about 50 of them, you are down 1/250, and after 100000 (actually, after about 700) you have underflowed the FP format itself, see here.
Ruby provides the BigDecimal class, which implements accurate floating point arithmetic.
require 'bigdecimal'
n = 100
decimals = n.times.map { BigDecimal.new rand.to_s }
result = decimals.reduce :*
result.nonzero?.nil? # returns nil if zero, self otherwise
# => false
result.precs # [significant_digits, maximum_significant_digits]
# => [1575, 1764]
Math.log10 result
# => -46.8031931083014
It is a lot slower than native floating point numbers, however. With n = 100_000, the decimals.reduce :* call went on for minutes on my computer before I finally interrupted it.

Resources