In one of my algorithms I use Ruby's combination and permutation methods. I have to discuss this algorithms complexity. Where could I find information about their complexity/implementation?
I've tried implementing a simple 'hand made' function but the Ruby ones seem to run in near constant time!
Any information about where to look would be much appreciated.
Implementation can be shown on the same pages you linked. Hover your mouse over the name of the combination/permutation methods, and select click to toggle source.
You can view the latest and different versions of source here at the main repo: http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/
You can read the revision history of array.c to see perhaps why/when any changes were made to the combination/permutation methods. http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/array.c?view=log. This may give you some indication into complexity and design choices made by the developers.
You may even be able to ask certain contributors to the source for reasons as to why they made XYZ change to the method, they may/may not help.
There is nothing in the Ruby Language Specification that requires implementors to guarantee a certain algorithmic complexity, and there is most certainly nothing that forces a particular implementation.
Every Ruby Execution Engine has their own implementation, and they may or may not have the same algorithmic complexity.
For example, here is Rubinius's implementation of Array#combination, located in kernel/common/array.rb#L360-394:
def combination(num)
num = Rubinius::Type.coerce_to num, Fixnum, :to_int
return to_enum(:combination, num) unless block_given?
if num == 0
yield []
elsif num == 1
each do |i|
yield [i]
end
elsif num == size
yield self.dup
elsif num >= 0 && num < size
stack = Rubinius::Tuple.pattern num + 1, 0
chosen = Rubinius::Tuple.new num
lev = 0
done = false
stack[0] = -1
until done
chosen[lev] = self.at(stack[lev+1])
while lev < num - 1
lev += 1
chosen[lev] = self.at(stack[lev+1] = stack[lev] + 1)
end
yield chosen.to_a
lev += 1
begin
done = lev == 0
stack[lev] += 1
lev -= 1
end while stack[lev+1] + num == size + lev + 1
end
end
self
end
And Array#permutation, located in kernel/common/array.rb#L935-969:
def permutation(num=undefined, &block)
return to_enum(:permutation, num) unless block_given?
if undefined.equal? num
num = #total
else
num = Rubinius::Type.coerce_to num, Fixnum, :to_int
end
if num < 0 || #total < num
# no permutations, yield nothing
elsif num == 0
# exactly one permutation: the zero-length array
yield []
elsif num == 1
# this is a special, easy case
each { |val| yield [val] }
else
# this is the general case
perm = Array.new(num)
used = Array.new(#total, false)
if block
# offensive (both definitions) copy.
offensive = dup
Rubinius.privately do
offensive.__permute__(num, perm, 0, used, &block)
end
else
__permute__(num, perm, 0, used, &block)
end
end
self
end
As you can see, it delegates to a private helper method named Array#__permute__, defined in kernel/common/array.rb#L971-994:
def __permute__(num, perm, index, used, &block)
# Recursively compute permutations of r elements of the set [0..n-1].
# When we have a complete permutation of array indexes, copy the values
# at those indexes into a new array and yield that array.
#
# num: the number of elements in each permutation
# perm: the array (of size num) that we're filling in
# index: what index we're filling in now
# used: an array of booleans: whether a given index is already used
#
# Note: not as efficient as could be for big num.
#total.times do |i|
unless used[i]
perm[index] = i
if index < num-1
used[i] = true
__permute__(num, perm, index+1, used, &block)
used[i] = false
else
yield values_at(*perm)
end
end
end
end
Related
My program orders arrays 1 and 2, and iterates to check whether each element in 1 is the sqrt of each element in 2. I've tested the comparison without the loop and it works fine, so I don't think that there's an uninitialised variable.
def comp(array1, array2)
order1 = array1.sort
order2 = array2.sort
i = 0
while i < order1.length
if order1[i] * order1[i] == order2[i]
i += 1
else
false
end
end
order1[i] * order1[i] == order2[i]
end
Can you point me in the direction of the issue? I've also not used Math.sqrt because it times out on my interface.
Your i equals order1.length, after your loop, so the last line of your method is basically
order1[order1.length] * order1[order1.length] == order2[order1.length]
which is (assuming your arrays are the same length):
nil * nil == nil
which throws an error. Not sure why you need the last line, if you remove it and simply return a counter, your method works as expected if you use a dedicated counter, for the elements which match your condition, instead of using index for that (your index has to be incremented always):
def comp(array1, array2)
order1 = array1.sort
order2 = array2.sort
i = 0
counter = 0
while i < order1.length
if order1[i] * order1[i] == order2[i]
counter += 1
end
i += 1
end
counter
end
In Ruby it is pretty common to use proper enumerators for iterating over collections, so your while can be nicely substituted by Enumerable#each_with_index:
def comp(array1, array2)
order1 = array1.sort
order2 = array2.sort
counter = 0
order1.each_with_index do |el, i|
if el * el == order2[i]
counter += 1
end
end
counter
end
And as the last step, we can also Array#count how many elements in an array meet a certain condition without needing to specify a local variable, like so:
def comp(array1, array2)
order2 = array2.sort
array1.sort.each_with_index.count { |el, i| el ** 2 == order2[i] }
end
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.
I'm trying to create a recursive method sum_of_digits(i) that takes the sum of integers, i.e. '456' = 4+5+6 = 15
However, I receive a NoMethodError for chr.to_i in the following code:
def sum_of_digits(i)
input = i.to_s
if i == 0
return 0
elsif input.length == 1
return i
else
for n in 1..input.length
sum += input[i].chr.to_i % 10^(n-1)
end
end
return sum
end
Thank you!
String indexes are zero-based in ruby. The problem is here:
for n in 1..input.length
it should be written as
for n in 0..input.length-1
BTW, call to chr is superfluous as well, since you already have a string representation of a digit there. As well, sum must be declared in advance and set to zero.
Also, the whole code is not ruby idiomatic: one should avoid using unnecessary returns and for-loop. The modified version (just in case) would be:
def sum_of_digits(i)
input = i.to_s
case
when i == 0 then 0 # return zero
when input.length == 1 then i # return i
else
sum = 0
input.length.times do |index|
sum += input[index].to_i % 10^index
end
sum
end
end
or, even better, instead of
sum = 0
input.length.times do |index|
sum += input[index].to_i % 10^index
end
sum
one might use inject:
input.length.times.inject(0) do |sum, index|
sum += input[index].to_i % 10^index
end
I would like to pass an array of numbers to my is_prime? method and return if the numbers are valid or not. I do not want to use:
require 'prime'
a = [1,2,3,4,5]
Hash[a.zip(a.map(&Prime.method(:prime?)))]
This is learning experience. My current code is only outputing the first number in the array. Can someone help me understand what I am doing wrong? Thanks!
def is_prime?(*nums)
i = 2
nums.each do |num|
while i < num
is_divisible = ((num % i) == 0)
if is_divisible == false
x = "#{num}: is NOT a prime number." #false
else
x = "#{num}: is a prime number." #true
end
i +=1
end
return x
end
end
puts is_prime?(27,13,42)
You are returning in the loop.
A few bugs in your method:
def is_prime?(*nums)
nums.each do |num|
return false if num == 1
next if num == 2 # 2 is the only even prime
i = 2 # needs to be reset for each num
while i < num
return false if num % i == 0 # num is not prime
i += 1
end
end
true # We'll reach here only if all the numbers are prime
end
This will return your results in the same format as your usage of the prime library with the same logic as your custom function:
def is_prime?(*nums)
nums.each_with_object({}) do |num, hsh|
hsh[num] = num > 1 && 2.upto(num - 1).none? { |i| num % i == 0 }
end
end
puts is_prime?(27,13,42)
# => {27=>false, 13=>true, 42=>false}
Since you mention this is just for learning, I'm assuming you know that a sieve is a better way to go for this than brute force iteration.
If you want an explanation of how the above code works or further help understanding why your current code doesn't, let me know in the comments.
I need some feedback to figure out why I cant puts or print anything from my methods on the screen. This is a simple script I wrote to solve the problem of finding the 1001st prime number. Thanks
def primes
# iterates through numbers until it has the 1001th prime number and returns it.
# I chose to create the num_primes variable instead of counting the number of
# elements in in_prime_array every iteration
# based upon a guess that it would be faster to check.
is_prime_array = []
num_primes = 0
i = 2
loop do
is_prime_array << i && num_primes += 1 if is_prime?(i) == true
i += 1
break if num_primes == 1001
end
is_prime_array[1001]
end
def is_prime? (num)
# Checks to see if the individual number given is a prime number or not.
i = 2
loop do
if i == num
return true
elsif num % i == 0
return false
else
i += 1
end
end
end
Thanks for any help!
EDIT
I took your advice and tried this pice of code:
def is_prime? (num)
# Checks to see if the individual number given is a prime number or not.
i = 2
loop do
if i == num
return true
elsif num % i == 0
return false
else
i += 1
end
end
end
i = 0
count = 0
loop do
count += 1 if is_prime?(x)
puts "#{i}" if count == 1001
break
end
It still returns nothing. Hummm
i = 0
count = 0
loop do
if is_prime(i)
count += 1
end
if count == 10001
puts "#{i}"
break
end
end
Simple method :)
It's an off-by-one error. If you have 1001 elements in an array, the last element will be at index 1000.
Where you have
is_prime_array[1001]
Change it to
is_prime_array[1000]
And you can do this:
puts primes
=> 7927
You could also have
is_prime_array.last
instead of a specific index number.
What are you trying to "puts"? The first thing I notice is that there is no call to primes in the file, so nothing will happen if you try to run this code by itself. Maybe that's why you don't see anything printed.
Here's an example of how to print a few variables inside your loop:
loop do
...
puts "At iteration #{i}, we have prime=#{is_prime?(i)}"
If you don't know, enclosing a statement with #{<statement goes here>} inside a string is the same as appending the return value of <statement goes here> to the string at that position. This is the same as "Str " + blah + " rest of str" in a language like Java.