找出 m 和 n 之间的所有整数,其除数的平方和本身就是一个平方 [英] Find all integers between m and n whose sum of squared divisors is itself a square
问题描述
42 的除数是:1, 2, 3, 6, 7, 14, 21, 42.这些除数的平方是:1, 4, 9, 36, 49, 196, 441, 1764.平方和除数是 2500,也就是 50 * 50,一个正方形!
Divisors of 42 are : 1, 2, 3, 6, 7, 14, 21, 42. These divisors squared are: 1, 4, 9, 36, 49, 196, 441, 1764. The sum of the squared divisors is 2500 which is 50 * 50, a square!
给定两个整数 m, n (1 <= m <= n) 我们想要找到 m 和 n 之间的所有整数,其除数的平方和本身就是一个平方.42就是这样一个数字.
Given two integers m, n (1 <= m <= n) we want to find all integers between m and n whose sum of squared divisors is itself a square. 42 is such a number.
结果将是一个数组数组,每个子数组有两个元素,首先是除数平方为平方的数,然后是除数平方的和.
The result will be an array of arrays, each subarray having two elements, first the number whose squared divisors is a square and then the sum of the squared divisors.
我怎样才能让这个特定的程序运行得更快?我当前的代码在 n > 后超时9999.
How can I make this specific program run faster? My current code times out after n > 9999.
#returns the divisors of each number in an array of arrays
r = (m..n).to_a.map { |z| (1..z).select { |x| z % x == 0} }
#this finds all integers between m and n whose sum of squared divisors is itself a square
squarenumbers = r.map { |x| x.map { |c| c**2 }.inject(:+) }.select { |x| Math.sqrt(x) % 1 == 0 }
#returns an array of booleans.
booleans = r.map { |x| x.map { |c| c**2 }.inject(:+) }.map { |x| Math.sqrt(x) % 1 == 0 }
#returns the index of each of the true values in booleans as an array
indexer = booleans.map.with_index{|x, i| i if x == true }.compact
#returns the numbers whose squared divisors is a square in an array
unsqr = indexer.map { |x| (m..n).to_a[x] }
#merges the two arrays together, element for element and creates an array of arrays
unsqr.zip(squarenumbers)
# for m = 1 and n = 1000 the result would be
# [[1, 1], [42, 2500], [246, 84100], [287, 84100], [728, 722500]]
推荐答案
因子的蛮力计算
你从计算开始:
m, n = 40, 42
r = (m..n).to_a.map { |z| (1..z).select { |x| z % x == 0} }
#=> [[1, 2, 4, 5, 8, 10, 20, 40], [1, 41], [1, 2, 3, 6, 7, 14, 21, 42]]
没关系,但你不需要.to_a
:
r = (m..n).map { |z| (1..z).select { |x| z % x == 0} }
#=> [[1, 2, 4, 5, 8, 10, 20, 40], [1, 41], [1, 2, 3, 6, 7, 14, 21, 42]]
这避免了额外的步骤,即创建临时数组1,2:
This avoids an extra step, which is the creation of the temporary array1,2:
(m..n).to_a #=> [40, 41, 42]
解决方案的结构
让我们向后工作以提出我们的代码.首先,专注于确定对于任何给定的数字 q
,q
的因数的平方和本身是否是一个完全平方.假设我们构造了一个方法 magic_number?
,它以 q
作为唯一参数,如果 q
满足要求,则返回 true
属性和 false
否则.然后我们将计算:
Let's work backwards to come up with our code. First, concentrate on determining, for any given number q
, if the sum of squares of the factors of q
is itself a perfect square. Suppose we construct a method magic_number?
which takes q
as its only argument and returns true
if q
satisfies the required property and false
otherwise. Then we will compute:
(m..n).select { |q| magic_number?(q) }
返回一个包含 m
和 n
之间满足该属性的所有数字的数组.magic_number?
可以这样写:
to return an array of all numbers between m
and n
that satisfy the property. magic_number?
can be written like this:
def magic_number?(q)
return true if q == 1
s = sum_of_squared_factors(q)
s == Math.sqrt(s).round**2
end
计算平方和
所以现在我们需要编写方法 sum_of_squared_factors
.我们可以使用您的代码来获取因子:
So now we are left with writing the method sum_of_squared_factors
. We can use your code to obtain the factors:
def factors(q)
(1..q).select { |x| q % x == 0 }
end
factors(40) #=> [1, 2, 4, 5, 8, 10, 20, 40]
factors(41) #=> [1, 41]
factors(42) #=> [1, 2, 3, 6, 7, 14, 21, 42]
然后写:
def sum_of_squared_factors(q)
factors(q).reduce(0) { |t,i| t + i*i }
end
sum_of_squared_factors(40) #=> 2210
sum_of_squared_factors(41) #=> 1682
sum_of_squared_factors(42) #=> 2500
加速因子的计算
我们还可以做更多的事情来加快因子的计算.如果f
是n
的因数,f
和n/f
,都是n的因数代码>.(例如,由于
3
是 42
的因数,42/3 #=>14
也是如此).因此,我们只需要获得每对中较小的.
There's something more we can do to speed up the calculation of factors. If f
is a factor of n
, f
and n/f
, are both factors of n
. (For example, since 3
is a factor of 42
, so is 42/3 #=> 14
). We therefore need only obtain the smaller of each pair.
此规则有一个例外.如果n
是一个完全平方数且f == n**0.5
,那么f = n/f
,所以我们只包括n
的因子中的 f(也不是 n/f
).
There is one exception to this rule. If n
is a perfect square and f == n**0.5
, then f = n/f
, so we only include f
among the factors of n
(not n/f
as well).
如果结果证明 f
是对中较小的一个,则 f <=(n**0.5).round
3.因此,我们只需要检查哪些数字 (1..(n**0.5).round)
是因数并包括它们的补码(除非 n
是完美的正方形,在这种情况下我们不会重复计算 (n**0.5).round
):
If turns out that if f
is the smaller of the pair, f <=(n**0.5).round
3. We therefore need only check to see which of the numbers (1..(n**0.5).round)
are factors and include their complements (unless n
is a perfect square, in which case we do not double-count (n**0.5).round
):
q = 42
arr = (1..Math.sqrt(q).round).select { |x| q % x == 0 }
#=> [1, 2, 3, 6]
arr = arr.flat_map { |n| [n, q/n] }
#=> [1, 42, 2, 21, 3, 14, 6, 7]
arr.pop if a[-2] == a[-1]
arr
#=> [1, 42, 2, 21, 3, 14, 6, 7]
q = 36
arr = (1..Math.sqrt(q).round).select { |x| q % x == 0 }
#=> [1, 2, 3, 4, 6]
arr = arr.flat_map { |n| [n, q/n] }
#=> [1, 36, 2, 18, 3, 12, 4, 9, 6, 6]
arr.pop if a[-2] == a[-1]
#=> 6
arr
#=> [1, 36, 2, 18, 3, 12, 4, 9, 6]
所以我们可以写:
def factors(q)
arr = (1..Math.sqrt(q)).select { |x| q % x == 0 }
arr = arr.flat_map { |n| [n, q/n] }
arr.pop if arr[-2] == arr[-1]
arr
end
代入arr
(链接"表达式),我们得到一个典型的Ruby表达式:
Substituting out arr
("chaining" expressions), we obtain a typical Ruby expression:
def factors(q)
(1..Math.sqrt(q)).select { |x| q % x == 0 }.
flat_map { |n| [n, q/n] }.
tap { |a| a.pop if a[-2] == a[-1] }
end
factors(42)
#=> [1, 42, 2, 21, 3, 14, 6, 7]
factors(36)
#=> [1, 36, 2, 18, 3, 12, 4, 9, 6]
参见 Enumerable#flat_map 和Object#tap.(不需要对这个数组进行排序.在需要对其进行排序的应用程序中,只需将 .sort
附加到 flat_map
块的末尾即可.)
See Enumerable#flat_map and Object#tap. (There's no need for this array to be sorted. In applications where it needs to be sorted, just tack .sort
onto the end of flat_map
s block.)
总结
总而言之,我们剩下以下几点:
In sum, we are left with the following:
def magic_number?(q)
return true if q == 1
s = sum_of_squared_factors(q)
s == Math.sqrt(s).round**2
end
def sum_of_squared_factors(q)
factors(q).reduce(0) { |t,i| t + i*i }
end
def factors(q)
(1..Math.sqrt(q)).select { |x| q % x == 0 }.
flat_map { |n| [n, q/n] }.
tap { |a| a.pop if a[-2] == a[-1] }
end
m, n = 1, 1000
(m..n).select { |q| magic_number?(q) }
#=> `[1, 42, 246, 287, 728]
这个计算眨眼间就完成了.
This calculation was completed in a blink of an eye.
计算素数以进一步加快因子的计算
最后,让我描述一种更快的方法来计算一个数的因数,使用方法 Prime::prime_division.该方法将任何数字分解为其质数成分.例如,考虑 n = 360
.
Lastly, let me describe an even faster way to compute the factors of a number, using the method Prime::prime_division. That method decomposes any number into its prime components. Consider, for example, n = 360
.
require 'prime'
Prime.prime_division(360)
#=> [[2, 3], [3, 2], [5, 1]]
这告诉我们:
360 == 2**3 * 3**2 * 5**1
#=> true
它还告诉我们360
的每一个因子都是0
和3
2
的乘积's,乘以0
和2
之间的3
,乘以0
或1
5
的.因此:
It also tells us that every factor of 360
is the product of between 0
and 3
2
's, multiplied by between 0
and 2
3
's, multiplied by 0
or 1
5
's. Therefore:
def factors(n)
Prime.prime_division(n).reduce([1]) do |a,(prime,pow)|
a.product((0..pow).map { |po| prime**po }).map { |x,y| x*y }
end
end
a = factors(360).sort
#=> [ 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 18,
# 20, 24, 30, 36, 40, 45, 60, 72, 90, 120, 180, 360]
我们可以检查:
a == (1..360).select { |n| (360 % n).zero? }
#=> true
另一张支票:
factors(40).sort
#=> [1, 2, 4, 5, 8, 10, 20, 40]
1.你可以改为写 [*m..n] #=>[40, 41, 42]
.2.为什么不需要将范围转换为数组?Enumerable#map,是一个实例方法Enumerable
模块的一部分,可供 include
s Enumerable
的每个类使用.Array
是一个,但是 (m..n).class #=>Range
是另一个.(请参阅 Range 中的第二段).3.假设 f
小于 n/f
并且 f >n**0.5
,然后 n/f
1. You could instead write that [*m..n] #=> [40, 41, 42]
.
2. Why is it not necessary to convert the range to an array? Enumerable#map, being an instance method of the module Enumerable
, is available for use by every class that include
s Enumerable
. Array
is one, but (m..n).class #=> Range
is another. (See the second paragraph at Range).
3. Suppose f
is smaller than n/f
and f > n**0.5
, then n/f < n/(n**0.5) = n**0.5 < f
, a contradiction.
这篇关于找出 m 和 n 之间的所有整数,其除数的平方和本身就是一个平方的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!