Why method "div" is faster thant "div2"? - ruby

Im trying to decipher why the div method is faster than the div2 method, and I cant find the reason.
def div2(num)
[*1..num].select do |n|
n if num % n == 0
end
end
p div2(58463982)
def div(num)
result = []
(1..num).each do |n|
break if result.include?(num / n)
result.concat([n, num / n]).uniq! if num % n == 0
end
result.sort!
end
p div(58463982)

I will let others explain why div is faster than div2. I want to show how to compute the factors of the given natural number in a way that is considerably faster.
Every integer can be expressed as the product of a collection of prime numbers, each taken to a power of one or more. We can use the method Prime::prime_division to obtain those prime numbers and powers. For example,
require 'prime'
arr = Prime.prime_division(58463982)
#=> [[2, 1], [3, 2], [53, 1], [61283, 1]]
This means that:
(2**1) * (3**2) * (53**1) * (61283**1)
#=> 58463982
One divisor of 58463982 equals, for example:
(2**1) * (3**2) * (53**0) * (61283**1)
#=> 2 * 9 * 1 * 61283
#=> 1103094
To confirm:
58463982 % 1103094
#=> 0
Another would be
(2**0) * (3**1) * (53**1) * (61283**0)
#=> 1 * 3 * 53 * 1
#=> 159
We find that all factors of a given number can be computed (combinatorially) as follows, using the methods Array#product and Enumerable#reduce (a.k.a. inject).
def all_factors(n)
primes, exponents = Prime.prime_division(n).transpose
first_exp_range, *rest_exp_range = exponents.map { |e| [*0..e] }
first_exp_range.product(*rest_exp_range).map do |exps|
primes.zip(exps).reduce(1) { |t,(p,e)| t*(p**e) }
end.sort
end
Depending on requirements, .sort at the end may not be required.
We may test:
require 'time'
t = Time.now
p all_factors(58463982)
p Time.now - t
#=> [1, 2, 3, 6, 9, 18, 53, 106, 159, 318, 477, 954, 61283, 122566,
# 183849, 367698, 551547, 1103094, 3247999, 6495998, 9743997,
# 19487994, 29231991, 58463982]
#
#=> 0.001405 (seconds)
By constrast, computing the factors of 58463982 with div2 and div required 4.467112 and 0.021103 seconds, respectively.
This is clearly much faster than those methods.
We may step through the example to view the calculations being performed.
n = 58463982
then
primes, exponents = Prime.prime_division(n).transpose
#=> [[2, 3, 53, 61283], [1, 2, 1, 1]]
so
primes
#=> [2, 3, 53, 61283]
exponents
#=> [1, 2, 1, 1]
Then,
first_exp_range, *rest_exp_range = exponents.map { |e| [*0..e] }
#=> [[0, 1], [0, 1, 2], [0, 1], [0, 1]]
so
first_exp_range
#=> [0, 1]
rest_exp_range
#=> [0, 1, 2], [0, 1], [0, 1]
Then
a = first_exp_range.product(*res_exp_range)
#=> [[0, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [0, 0, 1, 1],
# [0, 1, 0, 0], [0, 1, 0, 1], [0, 1, 1, 0], [0, 1, 1, 1],
# [0, 2, 0, 0], [0, 2, 0, 1], [0, 2, 1, 0], [0, 2, 1, 1],
# [1, 0, 0, 0], [1, 0, 0, 1], [1, 0, 1, 0], [1, 0, 1, 1],
# [1, 1, 0, 0], [1, 1, 0, 1], [1, 1, 1, 0], [1, 1, 1, 1],
# [1, 2, 0, 0], [1, 2, 0, 1], [1, 2, 1, 0], [1, 2, 1, 1]]
Then,
b = a.map { |exps| primes.zip(exps).reduce(1) { |t,(p,e)| t*(p**e) } }
#=> [1, 61283, 53, 3247999, 3, 183849, 159, 9743997, 9, 551547,
# 477, 29231991, 2, 122566, 106, 6495998, 6, 367698, 318,
# 19487994, 18, 1103094, 954, 58463982]
To view the result sorted,
b.sort
#=> [1, 2, 3, 6, 9, 18, 53, 106, 159, 318, 477, 954, 61283, 122566,
# 183849, 367698, 551547, 1103094, 3247999, 6495998, 9743997,
# 19487994, 29231991, 58463982]

The div2 method create a list from 1 to num then iterates over all of the elements in it.
The div method can break early, and so does not have to iterate as many times.

Related

Getting all tuples in Ruby

Say I have the following input:
inp = [2, 9, 3]
I need output as all tuples in mixed counting, like this:
outp = [[0, 0, 0], [0, 0, 1], [0, 0, 2], [0, 1, 0], [0, 1, 1], ..., [1, 8, 2]]
I know algorithm from Knuth vol 4a as direct loop solution, but I've heard ruby has some magic inside.
I am mostly C++ developer. My direct solution now looks like:
inparr = [2, 9, 3]
bmix = Array.new(inparr.size) { |i| 0 }
outp = Array.new
loop do
# some debug output
puts bmix.to_s
#visit next tuple
outp << bmix.clone
digit = inparr.size
while digit > 0 do
digit -= 1
if bmix[digit] + 1 < inparr[digit]
bmix[digit] += 1
break
end
bmix[digit] = 0
end
break if (bmix.select{|x| x != 0}.empty?)
end
How to rewrite it in several simple lines?
inp.
map { |i| (0...i).to_a }.
reduce(&:product).
map(&:flatten)
Used operations: Range, Enumerable#map, Enumerable#reduce, Array#product, Array#flatten.
You could use recursion.
def recurse(inp)
first, *rest = inp
rest.empty? ? [*0..first-1] : (0..first-1).flat_map do |e|
recurse(rest).map { |arr| [e, *arr] }
end
end
recurse [2, 4, 3]
#=> [[0, 0, 0], [0, 0, 1], [0, 0, 2],
# [0, 1, 0], [0, 1, 1], [0, 1, 2],
# [0, 2, 0], [0, 2, 1], [0, 2, 2],
# [0, 3, 0], [0, 3, 1], [0, 3, 2],
# [1, 0, 0], [1, 0, 1], [1, 0, 2],
# [1, 1, 0], [1, 1, 1], [1, 1, 2],
# [1, 2, 0], [1, 2, 1], [1, 2, 2],
# [1, 3, 0], [1, 3, 1], [1, 3, 2]]
If first, *rest = [2,4,3], then first #=> 2 and rest #=> [4,3].
See Enumerable#flat_map and Array#map. a ? b : c is called a ternery expression.
If e #=> 1 and arr #=> [2,1] then [e, *arr] #=> [1,2,1].
I will go to great lengths to avoid the use of Array#flatten. It's irrational, but to me it's an ugly method. That's usually possible using flat_map and/or the splat operator *.
Here's a mix of the 2 existing answers. It might be a bit more concise and readable:
head, *rest = inp.map{ |n| n.times.to_a }
head.product(*rest)
As an example:
inp = [2, 4, 3]
# => [2, 4, 3]
head, *rest = inp.map{ |n| n.times.to_a }
# => [[0, 1], [0, 1, 2, 3], [0, 1, 2]]
head.product(*rest)
# => [[0, 0, 0], [0, 0, 1], [0, 0, 2], [0, 1, 0], [0, 1, 1], [0, 1, 2], [0, 2, 0], [0, 2, 1], [0, 2, 2], [0, 3, 0], [0, 3, 1], [0, 3, 2], [1, 0, 0], [1, 0, 1], [1, 0, 2], [1, 1, 0], [1, 1, 1], [1, 1, 2], [1, 2, 0], [1, 2, 1], [1, 2, 2], [1, 3, 0], [1, 3, 1], [1, 3, 2]]

Algorithm: Factor Combinations

I'm working on the following algorithm from Leetcode:
Numbers can be regarded as product of its factors. For example,
8 = 2 x 2 x 2;
= 2 x 4.
Write a function that takes an integer n and return all possible combinations of its factors.
Note:
You may assume that n is always positive.
Factors should be greater than 1 and less than n.
Examples:
input: 1
output:
[]
input: 37
output:
[]
input: 12
output:
[
[2, 6],
[2, 2, 3],
[3, 4]
]
input: 32
output:
[
[2, 16],
[2, 2, 8],
[2, 2, 2, 4],
[2, 2, 2, 2, 2],
[2, 4, 4],
[4, 8]
]
Here's the code that I have thus far:
def get_factors(n)
factors = []
(2...n).each do |candidate|
if n % candidate == 0
factors << [candidate, (n/candidate)]
get_factors(n/candidate).each do |factor_set|
factors << [candidate] + factor_set
end
end
end
factors
end
This code works really well, but doesn't handle duplicates (e.g [3, 2, 2] will be inserted along with [2, 2, 3]). I tried using a Set with the following code,
def get_factors(n)
seen = Set.new
factors = []
(2...n).each do |candidate|
if n % candidate == 0 && !seen.include?(candidate)
factors << [candidate, (n/candidate)]
get_factors(n/candidate).each do |factor_set|
factors << [candidate] + factor_set
end
end
seen << (n/candidate)
end
factors
end
but that only works to solve some test cases and not others. I'm not sure how to go about ensuring no duplicates in an efficient way? The really inefficient way is to generate some sort of hash value for each array depending on it's elements (and not dependent on order), and while this would work, there definitely should be a better way. Any ideas?
I think always going forward is a good policy (i.e when checking, say, with 5, do not check with 2, 3, 4 etc). That way, searching for duplicates can be eliminated.
Since the algorithm already uses a lot of time, I don't see a problem sorting each answer and removing duplicates. This requires no proof to ensure it works, which the answer provided by mac does.
Code
require 'prime'
def get_factors(n)
primes, nbr = Prime.prime_division(n).transpose
powers = nbr.map { |m| (0..m).to_a }
powers.shift.
product(*powers).
map { |pows| primes.zip(pows).reduce(1) { |t,(pr,po)| t * (pr**po) } }.
sort
end
The array returned includes 1 and n (which are factors of n). If those values should be excluded, replace .sort with .sort - [1, n].
Examples
get_factors(24)
#=> [1, 2, 3, 4, 6, 8, 12, 24]
get_factors(64)
#=> [1, 2, 4, 8, 16, 32, 64]
get_factors(90)
#=> [1, 2, 3, 5, 6, 9, 10, 15, 18, 30, 45, 90]
Explanation
Consider
n = 60
The steps are as follows.
a = Prime.prime_division(30)
#=> [[2, 2], [3, 1], [5, 1]]
Ergo, the primes of 30 are 2, 3 and 5, and
60 = 2**2 * 3**1 * 5**1
See Prime::prime_division. Continuing,
primes, nbr = a.transpose
#=> [[2, 3, 5], [2, 1, 1]]
primes
#=> [2, 3, 5]
nbr
#=> [2, 1, 1]
powers = nbr.map { |m| (0..m).to_a }
#=> [[0, 1, 2], [0, 1], [0, 1]]
This means that each factor will be the product of 0, 1 or 2 2's, 0 or 1 3's and 0 or 1 5's.
b = powers.shift
#=> [0, 1, 2]
powers
#=> [[0, 1], [0, 1]]
c = b.product(*powers)
#=> [[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 0], [1, 0, 1],
# [1, 1, 0], [1, 1, 1], [2, 0, 0], [2, 0, 1], [2, 1, 0], [2, 1, 1]]
d = c.map { |pows| primes.zip(pows).reduce(1) { |t,(pr,po)| t * (pr**po) } }
#=> [1, 5, 3, 15, 2, 10, 6, 30, 4, 20, 12, 60]
d.sort
#=> [1, 5, 3, 15, 2, 10, 6, 30, 4, 20, 12, 60]
Consider now the calculation of d. The 10th element of c that is passed to the block is [2, 0, 1]. The block calculation for that element is as follows.
pows = [2, 0, 1]
e = primes.zip(pows)
#=> [[2, 2], [3, 0], [5, 1]]
e.reduce(1) { |t,(pr,po)| t * (pr**po) }
#=> 20
The reduce calculation is equivalent to
2**2 * 3**0 * 5**1
#=> 4 * 1 * 5 => 20
The calculations for the other values of c passed to the block are similar.
A simple way is to replace the last line of your method with
factors.map(&:sort).uniq
which sorts all the subarrays and then eliminates duplicates.

How to calculate the number of ways to represent n cents

I'm working on the following algorithm and wanted to know if my implementation is correct:
Given an infinite number of quarters, dimes, nickels and pennies,
write code to calculate the number of ways of representing n cents
This is without memoizing:
def count_ways(n)
return 0 if n < 0
return 1 if n == 0
count_ways(n-25) + count_ways(n-5) + count_ways(n-10) + count_ways(n-1)
end
No, you will be double-counting solutions, because you could first pick a quarter and then a dime or the other way around, but those solutions are essentially the same.
The easiest way to prevent double-counting is to make sure you never pick a coin that is bigger than the ones you already picked.
In code:
def count_ways(n, max_coin)
return 0 if n < 0
return 1 if n == 0
result = count_ways(n-1, 1)
result = result + count_ways(n- 5, 5) if max_coin >= 5
result = result + count_ways(n-10, 10) if max_coin >= 10
result = result + count_ways(n-25, 25) if max_coin >= 25
result
end
And call it with 25 as initial max coin
We can see if your code is correct quite easily. let's try making change for a dime. There are four ways: 1 dime, 2 nickels, 1 nickel and 5 pennies, and 10 pennies, yet count_ways(10) #=> 9.
You can do it as follows, using recursion.
Code
def count_ways(cents, coins)
if coins.size == 1
return (cents % coins.first) == 0 ? [cents/coins.first] : nil
end
coin, *remaining_coins = coins
(0..cents/coin).each_with_object([]) { |n, arr|
count_ways(cents-n*coin, remaining_coins).each { |a| arr << [n, *a] } }
end
Examples
coins = [25, 10, 5, 1]
count_ways(32, coins)
#=> [[0, 0, 0, 32], [0, 0, 1, 27], [0, 0, 2, 22], [0, 0, 3, 17], [0, 0, 4, 12],
# [0, 0, 5, 7], [0, 0, 6, 2], [0, 1, 0, 22], [0, 1, 1, 17], [0, 1, 2, 12],
# [0, 1, 3, 7], [0, 1, 4, 2], [0, 2, 0, 12], [0, 2, 1, 7], [0, 2, 2, 2],
# [0, 3, 0, 2], [1, 0, 0, 7], [1, 0, 1, 2]]
count_ways(100, coins)
#=> [[0, 0, 0, 100], [0, 0, 1, 95], [0, 0, 2, 90], [0, 0, 3, 85], [0, 0, 4, 80],
# [0, 0, 5, 75], [0, 0, 6, 70], [0, 0, 7, 65], [0, 0, 8, 60], [0, 0, 9, 55],
# ...
# [3, 1, 2, 5], [3, 1, 3, 0], [3, 2, 0, 5], [3, 2, 1, 0], [4, 0, 0, 0]]
count_ways(100, coins).size
#=> 242
Explanation
The best way to show how the recursion works is to salt the code with puts statements and then run it against a simple example.
INDENT = 8
#indentation = 0
def indent
#indentation += INDENT
end
def undent
#indentation = [#indentation-INDENT, 0].max
end
def ind
' '*#indentation
end
def count_ways(cents, coins)
puts "#{ind}** entering count_ways with cents=#{cents}, coins=#{coins}"
if coins.size == 1
puts "#{ind}<< returning [cents]=#{[cents]} as coins.size == 1"
undent
end
return [cents] if coins.size == 1
coin, *remaining_coins = coins
puts "#{ind}coin=#{coin}. remaining_coins=#{remaining_coins}"
puts "#{ind}0..cents/coin=#{0..cents/coin}"
arr = (0..cents/coin).each_with_object([]) do |n, arr|
puts "#{ind} n=#{n}, arr=#{arr}"
puts "#{ind} >> calling count_ways(#{cents}-#{n}*#{coin}, remaining_coins)"
indent
aa = count_ways(cents-n*coin, remaining_coins)
puts "#{ind} aa=#{aa}"
aa.each do |a|
arr << [n, *a]
puts "#{ind} arr << [#{n}, *#{a}], arr=#{arr}"
end
puts "#{ind} after all coins, arr=#{arr}"
end
puts "#{ind}<< returning arr=#{arr}"
undent
arr
end
Now let's run count_ways(12, coins) which should return the four ways of making change for 12 cents: [[0, 0, 0, 12], [0, 0, 1, 7], [0, 0, 2, 2], [0, 1, 0, 2]].
count_ways(12, coins)
** entering count_ways with cents=12, coins=[25, 10, 5, 1]
coin=25. remaining_coins=[10, 5, 1]
0..cents/coin=0..0
n=0, arr=[]
>> calling count_ways(12-0*25, remaining_coins)
** entering count_ways with cents=12, coins=[10, 5, 1]
coin=10. remaining_coins=[5, 1]
0..cents/coin=0..1
n=0, arr=[]
>> calling count_ways(12-0*10, remaining_coins)
** entering count_ways with cents=12, coins=[5, 1]
coin=5. remaining_coins=[1]
0..cents/coin=0..2
n=0, arr=[]
>> calling count_ways(12-0*5, remaining_coins)
** entering count_ways with cents=12, coins=[1]
<< returning [cents]=[12] as coins.size == 1
aa=[12]
arr << [0, *12], arr=[[0, 12]]
after all coins, arr=[[0, 12]]
n=1, arr=[[0, 12]]
>> calling count_ways(12-1*5, remaining_coins)
** entering count_ways with cents=7, coins=[1]
<< returning [cents]=[7] as coins.size == 1
aa=[7]
arr << [1, *7], arr=[[0, 12], [1, 7]]
after all coins, arr=[[0, 12], [1, 7]]
n=2, arr=[[0, 12], [1, 7]]
>> calling count_ways(12-2*5, remaining_coins)
** entering count_ways with cents=2, coins=[1]
<< returning [cents]=[2] as coins.size == 1
aa=[2]
arr << [2, *2], arr=[[0, 12], [1, 7], [2, 2]]
after all coins, arr=[[0, 12], [1, 7], [2, 2]]
<< returning arr=[[0, 12], [1, 7], [2, 2]]
aa=[[0, 12], [1, 7], [2, 2]]
arr << [0, *[0, 12]], arr=[[0, 0, 12]]
arr << [0, *[1, 7]], arr=[[0, 0, 12], [0, 1, 7]]
arr << [0, *[2, 2]], arr=[[0, 0, 12], [0, 1, 7], [0, 2, 2]]
after all coins, arr=[[0, 0, 12], [0, 1, 7], [0, 2, 2]]
n=1, arr=[[0, 0, 12], [0, 1, 7], [0, 2, 2]]
>> calling count_ways(12-1*10, remaining_coins)
** entering count_ways with cents=2, coins=[5, 1]
coin=5. remaining_coins=[1]
0..cents/coin=0..0
n=0, arr=[]
>> calling count_ways(2-0*5, remaining_coins)
** entering count_ways with cents=2, coins=[1]
<< returning [cents]=[2] as coins.size == 1
aa=[2]
arr << [0, *2], arr=[[0, 2]]
after all coins, arr=[[0, 2]]
<< returning arr=[[0, 2]]
aa=[[0, 2]]
arr << [1, *[0, 2]], arr=[[0, 0, 12], [0, 1, 7], [0, 2, 2], [1, 0, 2]]
after all coins, arr=[[0, 0, 12], [0, 1, 7], [0, 2, 2], [1, 0, 2]]
<< returning arr=[[0, 0, 12], [0, 1, 7], [0, 2, 2], [1, 0, 2]]
aa=[[0, 0, 12], [0, 1, 7], [0, 2, 2], [1, 0, 2]]
arr << [0, *[0, 0, 12]], arr=[[0, 0, 0, 12]]
arr << [0, *[0, 1, 7]], arr=[[0, 0, 0, 12], [0, 0, 1, 7]]
arr << [0, *[0, 2, 2]], arr=[[0, 0, 0, 12], [0, 0, 1, 7], [0, 0, 2, 2]]
arr << [0, *[1, 0, 2]], arr=[[0, 0, 0, 12], [0, 0, 1, 7], [0, 0, 2, 2], [0, 1, 0, 2]]
after all coins, arr=[[0, 0, 0, 12], [0, 0, 1, 7], [0, 0, 2, 2], [0, 1, 0, 2]]
<< returning arr=[[0, 0, 0, 12], [0, 0, 1, 7], [0, 0, 2, 2], [0, 1, 0, 2]]
=> [[0, 0, 0, 12], [0, 0, 1, 7], [0, 0, 2, 2], [0, 1, 0, 2]]
The order of the coins doesn't matter, so coins.min isn't going to help you in this case – it's over-complicating things.
First we must develop an intuition about the relationship between the kinds of coins and the amount of change to count
The number of ways to change amount a using n kinds of coins equals
the number of ways to change amount a using all but the first kind of coin, plus
the number of ways to change amount a − d using all n kinds of coins, where d is the denomination of the first kind of coin.
source: SICP Chapter 1.2
### change_coins :: (Int, [Int]) -> Int
def change_coins amount, (x,*xs)
if amount == 0
1
elsif amount < 0 or x.nil?
0
else
change_coins(amount, xs) + change_coins(amount - x, [x,*xs])
end
end
change_coins 11, [1, 2, 5] # => 11
change_coins 2, [3] # => 0
change_coins 100, [1, 5, 10, 25, 50] # => 292
Sensible return values
For example, in this problem we have to return -1 if the amount of money cannot be made up by any combination of the coins.
The -1 case is dumb. There are zero ways to change 2 cents using only a 3-cent coin; therefore we return 0.
If you really must return -1, just use a stupid wrapper
def cc amount, xs
count = change_coins amount, xs
if count == 0 then -1 else count end
end
Order doesn't matter
change_coins 11, [5, 1, 2] # => 11
change_coins 2, [3] # => 0
change_coins 100, [50, 1, 25, 10, 5] # => 292

`each` loop with additional parameter

I expected row to be [0, 0, 0, 0] and row_index to be nil in the following:
img_array = [
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
]
img_array.each do |row, row_index|
...
end
Actually, row is 0 and row_index is 0. Can anyone explain this?
img_array = [
[0, 1, 2, 3],
[4, 5, 6, 7],
[8, 9, 0, 1]
]
We have:
enum = img_array.each
#=> #<Enumerator: [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 0, 1]]:each>
Array#each therefore creates an enumerator that is an instance of the class Enumerator. The method Enumerator#each passes each element of enum to the block and assigns the block variables:
enum.each { |row, row_index| puts "row=#{row}, row_index=#{row_index}" }
# row=0, row_index=1
# row=4, row_index=5
# row=8, row_index=9
We can see what each of the elements of enum are by using the method Enumerator#next:
enum.next #=> StopIteration: iteration reached an end
Whoops! I forgot to reset the enumerator:
enum.rewind
#=> #<Enumerator: [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 0, 1]]:each>
enum.next #=> [0, 1, 2, 3]
enum.next #=> [4, 5, 6, 7]
enum.next #=> [8, 9, 0, 1]
enum.next #=> StopIteration: iteration reached an end
Alternatively, we could convert the enumerator to an array (no need to rewind):
enum.to_a #=> [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 0, 1]]
When the element ([0, 1, 2, 3]) is passed to the block, the block variables are assigned as follows:
row, row_index = [0, 1, 2, 3] #=> [0, 1, 2, 3]
row #=> 0
row_index #=> 1
If the variables were |row, row_index, col_index|, the assignment would be:
row, row_index, col_index = [0, 1, 2, 3] #=> [0, 1, 2, 3]
row #=> 0
row_index #=> 1
col_index #=> 2
If they were |row, row_index, *rest|, it would be:
row, row_index, *rest = [0, 1, 2, 3] #=> [0, 1, 2, 3]
row #=> 0
row_index #=> 1
rest #=> [2, 3]
This is called parallel (or multiple) assignment. You can read up on the rules for such assignments at the link I've given.
The method .each() on array in Ruby will call your block with only one argument.
So if you write your code like this:
img_array.each do |row, row_index|
# do something here
end
It will equivalent to this:
img_array.each do |element|
row, row_index = element
# and now
# row = element[0]
# row_index = element[1]
end
I made an example that easier to understand
img_array = [
['a', 'b', 'd', 'e'],
['f', 'g', 'h', 'j']
]
img_array.each do |row, row_index|
p row
p row_index
end
And the result will be:
"a"
"b"
"f"
"g"
Run it online here: https://ideone.com/ifDaVZ

Binary Sequence Combination Generator, in Ruby

Code works, but feels very brute force, suggestions?
Goal of the code is to supply an array length, and then as fast as possible generate all possible unique binary combinations with that array length.
CODE:
class Array
def sequence(i = 0, *a)
return [a] if i == size
self[i].map {|x|
sequence(i+1, *(a + [x]))
}.inject([]) {|m, x| m + x}
end
end
[(0..1),(0..1),(0..1)].sequence
OUTPUTS:
[[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1]]
permutation and repeated_permutation are built in, so you can do:
def sequence(n)
[0, 1].repeated_permutation(n).to_a
end
p sequence(3) #=>[[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1]]
"All unique binary combinations" with n bits is nothing but (0...2**n), so the only task is to efficiently convert from an integer to its binary representation, and the following is a solution that does not rely on string generation/manipulation:
def sequence(n)
ret = []
(2**n).times do |number|
ret << []
(n - 1).downto(0) do |bit|
ret.last << number[bit]
end
end
ret
end
sequence(3)
# => [[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1]]
Or, if you prefer a version more oriented on list operations, this is pretty much the same:
def sequence(n)
(0...2**n).map {|number|
(1..n).map {|bit|
number[n-bit]
}
}
end

Resources