【问题标题】:Why method "div" is faster thant "div2"?为什么方法“div”比“div 2”快?
【发布时间】:2022-07-06 05:53:14
【问题描述】:

我试图破译为什么div 方法比div2 方法快,但我找不到原因。

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)

【问题讨论】:

  • 手动尝试这两种方法以获得更小的数字,例如 15。div2 将循环 15 次,div 将循环直到 5,看到它已经找到 5(当它除以 3 时)并中断.
  • 最好循环到 num 的 integer square root

标签: ruby time-complexity


【解决方案1】:

div2 方法创建一个从1num 的列表,然后迭代其中的所有元素。

div 方法可以提前中断,因此不必重复多次。

【讨论】:

    【解决方案2】:

    我会让其他人解释为什么divdiv2 快。我想展示如何以更有效的方式计算给定自然数的因子。

    每个整数都可以表示为素数集合的乘积,每个素数的幂都是一个以上。我们可以使用方法Prime::prime_division 来获得那些素数和幂。例如,

    require 'prime'
    
    arr = Prime.prime_division(58463982)
      #=> [[2, 1], [3, 2], [53, 1], [61283, 1]]
    

    这意味着:

    (2**1) * (3**2) * (53**1) * (61283**1)
      #=> 58463982
    

    58463982 的主要除数之一等于,例如:

    (2**1) * (3**2) * (53**0) * (61283**1)
      #=> 2 * 9 * 1 * 61283
      #=> 1103094 
    

    确认:

    58463982 % 1103094
      #=> 0
    

    另一个是

    (2**0) * (3**1) * (53**1) * (61283**0)
      #=> 1 * 3 * 53 * 1
      #=> 159
    

    我们发现给定数字的所有因子都可以使用Array#productEnumerable#reduce(又名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
    

    根据要求,可能不需要末尾的.sort


    我们可能会测试:

    all_factors(58463982)
      #=> [1, 2, 3, 6, 9, 18, 53, 106, 159, 318, 477, 954, 61283, 122566,
      #    183849, 367698, 551547, 1103094, 3247999, 6495998, 9743997,
      #    19487994, 29231991, 58463982]
    

    这显然比divdiv2 快得多,部分原因是Prime::prime_division 是用优化的C 代码编写的。


    我们可以通过示例来查看正在执行的计算。

    n = 58463982
    

    然后

    primes, exponents = Prime.prime_division(n).transpose
      #=> [[2, 3, 53, 61283], [1, 2, 1, 1]]
    

    所以

    primes
      #=> [2, 3, 53, 61283]
    exponents
      #=> [1, 2, 1, 1]
    

    那么,

    first_exp_range, *rest_exp_range = exponents.map { |e| [*0..e] }
      #=> [[0, 1], [0, 1, 2], [0, 1], [0, 1]]
    

    所以

    first_exp_range
      #=> [0, 1]
    rest_exp_range
      #=> [0, 1, 2], [0, 1], [0, 1]
    

    然后

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

    那么,

     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]
    

    查看排序后的结果,

    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]
    

    【讨论】:

      猜你喜欢
      • 2017-12-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多