我会让其他人解释为什么div 比div2 快。我想展示如何以更有效的方式计算给定自然数的因子。
每个整数都可以表示为素数集合的乘积,每个素数的幂都是一个以上。我们可以使用方法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#product 和Enumerable#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]
这显然比div 和div2 快得多,部分原因是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]