Related
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.
I'm a beginner and I want to create a matrix. For example:
0 1 1
1 1 1
1 1 2
irb(main):001:0> t = [[1]*3]*3
=> [[1, 1, 1], [1, 1, 1], [1, 1, 1]]
irb(main):002:0> (0...3).each do |x| t[x][x]=x end
=> 0...3
irb(main):003:0> t
=> [[0, 1, 2], [0, 1, 2], [0, 1, 2]] # why all values changed?
What's wrong?
The way you construct the array does not cretae new arrays for each row but references the same array for all rows:
t.each do |row|
p row.object_id
end
# 70325094342320
# 70325094342320
# 70325094342320
It is the same as:
a = [1, 1, 1]
t = [a, a, a]
Try this to see the difference:
t = [[1] * 3, [1] * 3, [1] * 3]
Say I have an array [1,2,3] and I want every combination of these numbers that don't exceed 4. So I would have [1,2,3].someMethod(4) and it would give me:
[1,1,1,1]
[1,1,2]
[1,3]
[2,2]
So far I have:
(1..4).flat_map{|size| [1,2,3].repeated_combination(size).to_a }
but this gives me every possible combinations, including the ones that exceed my given limit. Is there an good way to either only get combinations that add up to my limit?
arr = [1,2,3]
(arr+[0]).repeated_combination(4).select { |a| a.reduce(:+) == 4 }.map { |a| a - [0] }
#=> [[1, 3], [2, 2], [1, 1, 2], [1, 1, 1, 1]]
Change == to <= if desired.
This answer, like the others, assumes arr contains natural numbers, including 1.
results = (1..4).each.with_object([]) do |size, results|
[1,2,3].repeated_combination(size) do |combo|
results << combo if combo.reduce(:+) == 4
end
end
p results
--output:--
[[1, 3], [2, 2], [1, 1, 2], [1, 1, 1, 1]]
Parameterizing the algorithm:
def do_stuff(values, target_total)
(1..target_total).each.with_object([]) do |size, results|
values.repeated_combination(size) do |combo|
results << combo if combo.reduce(:+) == 4
end
end
end
p do_stuff([1, 2, 3], 4)
You can filter out the arrays you don't want by using the select method. Just select all the arrays that have a sum == 4 (the sum is calculated by the inject method).
all_arrs = (1..4).flat_map do |size|
[1,2,3].repeated_combination(size).to_a
end
valid_arrs = all_arrs.select do |arr|
arr.inject { |a, b| a + b } == 4
end
print valid_arrs
# Output:
# [[1, 3], [2, 2], [1, 1, 2], [1, 1, 1, 1]]
A recursive approach.
def some_method(a, n)
return [[]] if n == 0
a.select { |e| e <= n }.\
flat_map { |e| some_method(a,n-e).map { |es| ([e] + es).sort } }.\
sort.\
uniq
end
p some_method([1,2,3], 4)
# => [[1, 1, 1, 1], [1, 1, 2], [1, 3], [2, 2]]
EDIT: Here is another recursive version without filtering duplicates but with opposite order. I added comments to make it clearer.
def some_method(a, n)
return [[]] if n == 0 # bottom (solution) found
return [] if a.empty? || n < 0 # no solution
max = a.max
# search all solutions with biggest value
l = some_method(a, n-max).map { |e| [max] + e }
# search all solutions without biggest value
r = some_method(a-[max],n)
l + r
end
p some_method([1,2,3], 4)
# => [[3, 1], [2, 2], [2, 1, 1], [1, 1, 1, 1]]
I'd like to get [[2, 1, 3], [1, 3, 2]] from [1, 2, 3] in Ruby.
For [1, 2, 3, 4], I'd like to get [[2, 1, 3, 4], [1, 3, 2, 4], [1, 2, 4, 3]]
Rule: Within two numbers, if left one is smaller then it swap the position.
I have the following codes so far but it returns [[2, 3, 1], [2, 3, 1]]
What am I doing wrong here? I appreciate any inputs.
In amidakuji.rb
class Amidakuji
def initialize(column, rung)
#column = column
#rung = rung
#myarr = []
#per_arr = []
#build_arr = []
end
def build_initial
#arr = (1..#column).to_a
end
def swap_element
i = 0
arr = build_initial
while i < #column - 1 do
#build_arr << swap(arr, i)
i += 1
end
#build_arr
end
def swap(arr, a)
if arr[a] < arr[a + 1]
arr[a], arr[a + 1] = arr[a + 1], arr[a]
end
arr
end
end
In amidakuji_spec.rb
it 'should create an array with swapped elements' do
expect(#kuji1.swap_element).to eq ([[2, 1, 3], [1, 3, 2]])
end
Results
Failures:
expected: [[2, 1, 3], [1, 3, 2]]
got: [[2, 3, 1], [2, 3, 1]]
You can do this quite compactly by using the methods Enumerable#each_cons and Enumerable#map.
Code
def doit(arr)
(0...arr.size).each_cons(2).map do |i,j|
a = arr.dup
a[i], a[j] = a[j], a[i]
a
end
end
Examples
doit([1,2,3]) #=> [[2, 1, 3], [1, 3, 2]]
doit([1,2,3,4]) #=> [[2, 1, 3, 4], [1, 3, 2, 4], [1, 2, 4, 3]]
doit([1,2,3,4,5]) #=> [[2, 1, 3, 4, 5], [1, 3, 2, 4, 5],
#=> [1, 2, 4, 3, 5], [1, 2, 3, 5, 4]]
Explanation
arr = [1,2,3,4]
b = (0...arr.size).each_cons(2)
#=> #<Enumerator: 0...4:each_cons(2)>
To view the contents of this enumerator:
b.to_a
#=> [[0, 1], [1, 2], [2, 3]]
Lastly
b.map do |i,j|
a = arr.dup
a[i], a[j] = a[j], a[i]
a
end
#=> [[2, 1, 3, 4], [1, 3, 2, 4], [1, 2, 4, 3]]
In the last step, consider the first element of b that is passed to map, which assigns the following values to the block variables:
i => 0
j => 1
We then make a copy of arr, swap the elements offsets 0 and 1, making
a => [2, 1, 3, 4]
and then enter a at the end of the block, causing map to replace [0, 1] with that array.
Given what you're trying to accomplish and the output you're getting, it looks like you're reusing the same array when you want distinct arrays instead. Specifically this line:
#build_arr << swap(arr, i)
is always passing the same 'arr' to swap.
So first time, it swaps the 1 and the 2 to give you [2, 1, 3]
Second time, it swaps the 1 and the 3 give you [2, 3, 1]
You push the same array onto #build_arr twice, which is why it repeats.
I want to program a counter which is represented by an array of numbers, starting with:
[0, 0, 0]
The constraint here is, that each position has a different cap, so it's not necessarily 9 or something else, but it is given. For instance:
[4, 2, 1]
Which would lead to the following incrementation sequence:
[0, 0, 0]
[0, 0, 1]
[0, 1, 0]
[0, 1, 1]
[0, 2, 0]
[0, 2, 1]
[1, 0, 0]
.
.
.
Of course I can think of a solution using modulo and adding each carryover onto the next position. But has someone an idea how to implement this efficiently, respectively with nice Ruby syntax without cluttering it too much?
That is my naive implementation:
max = [10, 1, 1, 1, 10]
counter = [0, 0, 0, 0, 0]
i = counter.length-1
while counter != max do
counter[i] = counter[i] + 1
while counter[i] > max[i]
counter[i] = 0
i = i - 1
counter[i] = counter[i] + 1
end
i = counter.length-1
end
I'm not sure about efficiency but here's my shot at it:
start = [0, 0, 0]
cap = [4, 2, 1]
start.zip(cap).map{ |i, c| (i..c).to_a }.reduce(&:product).map &:flatten
Produces something like:
[[0, 0, 0],
[0, 0, 1],
[0, 1, 0],
[0, 1, 1],
[0, 2, 0],
[0, 2, 1],
[1, 0, 0],
[1, 0, 1],
[1, 1, 0],
[1, 1, 1],
[1, 2, 0],
[1, 2, 1],
[2, 0, 0],
[2, 0, 1]...]
Edit: I was writing this before you made your edit. It seemed like you wanted a counter object, not just to output a list.
1) I would recommend specifying not the limits but (limit+1) of each of the digits. For example, for a [second, minute, hour, day, year] counter it makes more sense (to me) to write [60, 60, 24, 365] instead of [59,59,23,364].
2) You'll have to figure out what to do if your counter overflows the last limit of your array. I added an extra position that counts to infinity.
3) I would also recommend reversing the order of the array, at least in the internal representation to avoid inverting subscripts. If you don't want it like that, you can .reverse the bases in initialize and #digits in to_s
class MyCounter
def initialize bases
#bases = bases
#bases << 1.0/0 # Infinity
#digits = Array.new(bases.size, 0)
prod = 1
#digit_values = [1] + #bases[0..-2].map { |b| prod *= b }
end
attr_reader :digit_values
def to_s
#digits
end
def increment(digit=0)
v = #digits[digit] + 1
if v < #bases[digit]
#digits[digit] = v
else
#digits[digit] = 0
increment(digit+1)
end
self
end
def +(integer)
(#digits.size - 1).step(0,-1).each do |i|
#digits[i] += integer / #digit_values[i]
integer = integer % #digit_values[i]
end
self
end
end
c1 = MyCounter.new [2,3,5]
20.times { c1.increment; p c1 }
c2 = MyCounter.new [2,3,5]
c2 += 20
p c2
Create an array for each cap, with values from 0 upto cap. Take the first array and calculate the Cartesian product with the rest of the arrays.
caps = [4, 2, 1]
arrs = caps.map{|cap| (0..cap).to_a} #=>[[0, 1, 2, 3, 4], [0, 1, 2], [0, 1]]
p arrs.shift.product(*arrs)
# =>[[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [0, 2, 0], [0, 2, 1], ...
If you don't want a memory-consuming array with the results, then provide a block. product will yield each element to it, one by one.
arrs = caps.map{|cap| (0..cap).to_a}
arrs.shift.product(*arrs){|el| puts el.join} #no resulting array
#000
#001
#010
#011
#...