算法:因子组合

时间:2017-08-02 20:14:06

标签: ruby algorithm recursion

我正在研究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]
]

这是我到目前为止的代码:

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

此代码工作得很好,但不处理重复(例如[3,2,2]将与[2,2,3]一起插入)。我尝试使用带有以下代码的Set

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

但这仅适用于解决某些测试用例而非其他测试用例。我不确定如何以有效的方式确保没有重复?真正低效的方法是根据每个数组的元素(并且不依赖于顺序)为每个数组生成某种哈希值,虽然这样可行,但肯定应该有更好的方法。有什么想法吗?

4 个答案:

答案 0 :(得分:0)

我认为永远向前推进是一个很好的政策(例如,当用5检查时,不要用2,3,4等检查)。这样,可以消除搜索重复项。

答案 1 :(得分:0)

由于算法已经占用了大量时间,因此我没有看到排序每个答案并删除重复的问题。这不需要证明它确实有效,mac提供的答案就是这样。

答案 2 :(得分:0)

<强>代码

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

返回的数组包括1nn的因子)。如果应排除这些值,请将.sort替换为.sort - [1, n]

<强>实施例

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]

<强>解释

考虑

n = 60

步骤如下。

a = Prime.prime_division(30)
  #=> [[2, 2], [3, 1], [5, 1]]

Ergo,30的素数为235,以及

60 = 2**2 * 3**1 * 5**1 

Prime::prime_division。接着,

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]]

这意味着每个因素都是012 201 {的乘积{1}}和30 1

5

现在考虑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] 的计算。传递给块的d的第10个元素是c。该元素的块计算如下。

[2, 0, 1]

pows = [2, 0, 1] e = primes.zip(pows) #=> [[2, 2], [3, 0], [5, 1]] e.reduce(1) { |t,(pr,po)| t * (pr**po) } #=> 20 计算相当于

reduce

传递给块的2**2 * 3**0 * 5**1 #=> 4 * 1 * 5 => 20 的其他值的计算是相似的。

答案 3 :(得分:0)

一种简单的方法是用

替换方法的最后一行
factors.map(&:sort).uniq

对所有子阵列进行排序,然后消除重复。