【问题标题】:Find all integers between m and n whose sum of squared divisors is itself a square找出 m 和 n 之间除数平方和本身是平方的所有整数
【发布时间】:2016-04-25 18:36:15
【问题描述】:

问题问题

42 的除数是:1、2、3、6、7、14、21、42。这些除数的平方是:1、4、9、36、49、196、441、1764。平方和除数是 2500 即 50 * 50,一个正方形!

给定两个整数 m, n (1

结果将是一个数组数组,每个子数组有两个元素,首先是平方除数为平方的数字,然后是平方除数之和。

代码如下

我怎样才能使这个特定的程序运行得更快?我当前的代码在 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]]

【问题讨论】:

  • 能否请您更好地格式化您的问题?
  • 对不起。我刚刚更新了我的问题。
  • 可以codereview.stackexchange.com帮忙吗?
  • 我会在那里尝试我的问题。谢谢!

标签: arrays ruby algorithm


【解决方案1】:

因子的暴力计算

你从计算开始:

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

(m..n).to_a #=> [40, 41, 42]

解决方案的结构

让我们向后工作以提出我们的代码。首先,专注于确定,对于任何给定的数字q,如果q 的因子的平方和本身是一个完美的平方。假设我们构造了一个方法magic_number?,它以q 作为唯一的参数,如果q 满足所需的属性,则返回true,否则返回false。然后我们将计算:

(m..n).select { |q| magic_number?(q) }

返回一个由mn 之间满足该属性的所有数字组成的数组。 magic_number?可以这样写:

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。我们可以使用您的代码来获取因子:

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 也是)。因此,我们只需要获得每对中较小的那个。

这条规则有一个例外。如果n是一个完美的正方形并且f == n**0.5,那么f = n/f,所以我们在n的因子中只包括f(不包括n/f)。

如果结果证明f 是这对中较小的一个,f <=(n**0.5).round3。因此,我们只需要检查(1..(n**0.5).round) 中的哪些数字是因数并包括它们的补数(除非n 是一个完美的正方形,在这种情况下我们不会重复计算(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表达式:

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_maps 块的末尾即可。)

总结

总之,我们剩下以下几点:

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]

这个计算一眨眼就完成了。

计算素数以进一步加快因子计算

最后,让我描述一种更快的方法来计算一个数的因数,使用方法Prime::prime_division。该方法将任何数字分解为其主要成分。例如,考虑n = 360

require 'prime'

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

这告诉我们:

360 == 2**3 * 3**2 * 5**1
  #=> true

它还告诉我们360 的每个因子都是03 2 之间的乘积,乘以02 3 之间的乘积01 5 的。因此:

 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/ff > n**0.5,那么n/f < n/(n**0.5) = n**0.5 < f,矛盾。

【讨论】:

  • 这种方法可能很好,但结果完全是错误的。数字应该是[1, 42, 246, 287, 728],就像它们出现在问题中一样。当检查的数字本身是平方数时,问题来自将“中间”项加倍(所以n == q/n)。
  • @tevemader,“完全错了”?不是,“你忘了……”? :-) 无论如何,感谢您指出错误。我已经修好了。
【解决方案2】:

我不了解 Ruby,但问题在于用于查找数字的除数的算法(这不是特定于所使用的语言,即本例中的 Ruby)。

r = (m..n).to_a.map { |z| (1..z).select { |x| z % x == 0} }

要找到整数n 的除数,您将n 除以所有正整数到n - 1,这意味着循环运行n - 1 次。但是,除以 sort(n) 来计算除数就足够了。在伪代码中如下所示:

for i = 1 to i <= sqrt(n)
    r = n % i
    if r == 0 then
        i is a divisor
        if n / i != i then
            n / i is another divisor

例如:

sqrt_42 = 6.48074069840786
i = 1 => 1 and 42 are two divisors
i = 2 => 2 and 21
i = 3 => 3 and 14
i = 4 => no divisor
i = 5 => no divisor
i = 6 => 6 and 7

仅此而已。

这将大大提高性能,因为现在循环只运行 sort(n) 次而不是 n - 1 次,这对于大型 n 来说是一个很大的不同。

【讨论】:

  • 此外,正如 Cary 在他的回答中指出的那样,找到一个因素实际上通常会“几乎”免费为您提供第二个因素。
  • 感谢任务!我完全忘记了使用平方根函数来简化获得除数的方法。
猜你喜欢
  • 1970-01-01
  • 2015-03-04
  • 1970-01-01
  • 1970-01-01
  • 2016-02-19
  • 2020-10-20
  • 2021-02-09
  • 1970-01-01
  • 2015-11-09
相关资源
最近更新 更多