找出 m 和 n 之间的所有整数,其除数的平方和本身就是一个平方 [英] Find all integers between m and n whose sum of squared divisors is itself a square

查看:53
本文介绍了找出 m 和 n 之间的所有整数,其除数的平方和本身就是一个平方的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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]

解决方案的结构

让我们向后工作以提出我们的代码.首先,专注于确定对于任何给定的数字 qq 的因数的平方和本身是否是一个完全平方.假设我们构造了一个方法 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) }

返回一个包含 mn 之间满足该属性的所有数字的数组.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 

加速因子的计算

我们还可以做更多的事情来加快因子的计算.如果fn的因数,fn/f,都是n的因数.(例如,由于 342 的因数,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).round3.因此,我们只需要检查哪些数字 (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).round3. 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_mapObject#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_maps 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的每一个因子都是032的乘积's,乘以02之间的3,乘以01 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 模块的一部分,可供 includes 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 includes 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆