Ruby 寻找完美平方算法的优化
我正在做的是: 找出给定特定范围的平方因子之和是完美平方。 因此,如果范围是(1..10),你将得到每个数字的因子(1的所有因子,2的所有因子,3的所有因子等)平方这些因子,然后将它们相加。最后检查这个和是否是一个完美的平方 我被困在重构/优化上,因为我的解决方案太慢了 以下是我的想法:Ruby 寻找完美平方算法的优化,ruby,algorithm,optimization,refactoring,Ruby,Algorithm,Optimization,Refactoring,我正在做的是: 找出给定特定范围的平方因子之和是完美平方。 因此,如果范围是(1..10),你将得到每个数字的因子(1的所有因子,2的所有因子,3的所有因子等)平方这些因子,然后将它们相加。最后检查这个和是否是一个完美的平方 我被困在重构/优化上,因为我的解决方案太慢了 以下是我的想法: def list_squared(m, n) ans = [] range = (m..n) range.each do |i| factors = (1..i).select { |j|
def list_squared(m, n)
ans = []
range = (m..n)
range.each do |i|
factors = (1..i).select { |j| i % j == 0 }
squares = factors.map { |k| k ** 2 }
sum = squares.inject { |sum,x| sum + x }
if sum == Math.sqrt(sum).floor ** 2
all = []
all += [i, sum]
ans << all
end
end
ans
end
然后期望的输出将是一个数组数组,每个数组包含一个数,其平方因子的和是一个完美的平方,以及这些平方因子的和:
[[1, 1], [42, 2500], [246, 84100]]
首先,我将介绍一些辅助方法(
factors
和square?
)来提高代码的可读性
此外,我将减少范围和数组的数量以提高内存使用率
require 'prime'
def factors(number)
[1].tap do |factors|
primes = number.prime_division.flat_map { |p, e| Array.new(e, p) }
(1..primes.size).each do |i|
primes.combination(i).each do |combination|
factor = combination.inject(:*)
factors << factor unless factors.include?(factor)
end
end
end
end
def square?(number)
square = Math.sqrt(number)
square == square.floor
end
def list_squared(m, n)
(m..n).map do |number|
sum = factors(number).inject { |sum, x| sum + x ** 2 }
[number, sum] if square?(sum)
end.compact
end
list_squared(1, 250)
但是范围更广的基准测试(高达10000
)显示出比原始实现更好的性能:
require 'benchmark'
n = 10
Benchmark.bmbm(15) do |x|
x.report("original_list_squared :") { n.times do; original_list_squared(1, 10000); end }
x.report("improved_list_squared :") { n.times do; improved_list_squared(1, 10000); end }
end
# Rehearsal -----------------------------------------------------------
# original_list_squared : 36.400000 0.160000 36.560000 ( 36.860889)
# improved_list_squared : 2.530000 0.000000 2.530000 ( 2.540743)
# ------------------------------------------------- total: 39.090000sec
# user system total real
# original_list_squared : 36.370000 0.120000 36.490000 ( 36.594130)
# improved_list_squared : 2.560000 0.010000 2.570000 ( 2.581622)
tl;dr:N
N越大,我的代码与最初的实现相比执行得越好……我首先介绍一些辅助方法(因子和平方?
)来提高代码的可读性
此外,我将减少范围和数组的数量以提高内存使用率
require 'prime'
def factors(number)
[1].tap do |factors|
primes = number.prime_division.flat_map { |p, e| Array.new(e, p) }
(1..primes.size).each do |i|
primes.combination(i).each do |combination|
factor = combination.inject(:*)
factors << factor unless factors.include?(factor)
end
end
end
end
def square?(number)
square = Math.sqrt(number)
square == square.floor
end
def list_squared(m, n)
(m..n).map do |number|
sum = factors(number).inject { |sum, x| sum + x ** 2 }
[number, sum] if square?(sum)
end.compact
end
list_squared(1, 250)
但是范围更广的基准测试(高达10000
)显示出比原始实现更好的性能:
require 'benchmark'
n = 10
Benchmark.bmbm(15) do |x|
x.report("original_list_squared :") { n.times do; original_list_squared(1, 10000); end }
x.report("improved_list_squared :") { n.times do; improved_list_squared(1, 10000); end }
end
# Rehearsal -----------------------------------------------------------
# original_list_squared : 36.400000 0.160000 36.560000 ( 36.860889)
# improved_list_squared : 2.530000 0.000000 2.530000 ( 2.540743)
# ------------------------------------------------- total: 39.090000sec
# user system total real
# original_list_squared : 36.370000 0.120000 36.490000 ( 36.594130)
# improved_list_squared : 2.560000 0.010000 2.570000 ( 2.581622)
tl;dr:NN
越大,与原始实现相比,我的代码执行得越好……提高效率的一种方法是使用Ruby的内置方法
对于任何数字n
,如果prime\u division
返回包含单个元素的数组,则该元素将[n,1]
,并且n
将显示为prime。素数有因子n
和1
,因此必须与非素数的数字区别对待
require 'prime'
def list_squared(range)
range.each_with_object({}) do |i,h|
facs = Prime.prime_division(i)
ssq =
case facs.size
when 1 then facs.first.first**2 + 1
else facs.inject(0) { |tot,(a,b)| tot + b*(a**2) }
end
h[i] = facs if (Math.sqrt(ssq).to_i)**2 == ssq
end
end
list_squared(1..10_000)
#=> { 1=>[], 48=>[[2, 4], [3, 1]], 320=>[[2, 6], [5, 1]], 351=>[[3, 3], [13, 1]],
# 486=>[[2, 1], [3, 5]], 1080=>[[2, 3], [3, 3], [5, 1]],
# 1260=>[[2, 2], [3, 2], [5, 1], [7, 1]], 1350=>[[2, 1], [3, 3], [5, 2]],
# 1375=>[[5, 3], [11, 1]], 1792=>[[2, 8], [7, 1]], 1836=>[[2, 2], [3, 3], [17, 1]],
# 2070=>[[2, 1], [3, 2], [5, 1], [23, 1]], 2145=>[[3, 1], [5, 1], [11, 1], [13, 1]],
# 2175=>[[3, 1], [5, 2], [29, 1]], 2730=>[[2, 1], [3, 1], [5, 1], [7, 1], [13, 1]],
# 2772=>[[2, 2], [3, 2], [7, 1], [11, 1]], 3072=>[[2, 10], [3, 1]],
# 3150=>[[2, 1], [3, 2], [5, 2], [7, 1]], 3510=>[[2, 1], [3, 3], [5, 1], [13, 1]],
# 4104=>[[2, 3], [3, 3], [19, 1]], 4305=>[[3, 1], [5, 1], [7, 1], [41, 1]],
# 4625=>[[5, 3], [37, 1]], 4650=>[[2, 1], [3, 1], [5, 2], [31, 1]],
# 4655=>[[5, 1], [7, 2], [19, 1]], 4998=>[[2, 1], [3, 1], [7, 2], [17, 1]],
# 5880=>[[2, 3], [3, 1], [5, 1], [7, 2]], 6000=>[[2, 4], [3, 1], [5, 3]],
# 6174=>[[2, 1], [3, 2], [7, 3]], 6545=>[[5, 1], [7, 1], [11, 1], [17, 1]],
# 7098=>[[2, 1], [3, 1], [7, 1], [13, 2]], 7128=>[[2, 3], [3, 4], [11, 1]],
# 7182=>[[2, 1], [3, 3], [7, 1], [19, 1]], 7650=>[[2, 1], [3, 2], [5, 2], [17, 1]],
# 7791=>[[3, 1], [7, 2], [53, 1]], 7889=>[[7, 3], [23, 1]],
# 7956=>[[2, 2], [3, 2], [13, 1], [17, 1]],
# 9030=>[[2, 1], [3, 1], [5, 1], [7, 1], [43, 1]],
# 9108=>[[2, 2], [3, 2], [11, 1], [23, 1]], 9295=>[[5, 1], [11, 1], [13, 2]],
# 9324=>[[2, 2], [3, 2], [7, 1], [37, 1]]}
此计算大约花费了0.15秒
对于i=6174
(2**1) * (3**2) * (7**3) #=> 6174
及
提高效率的一种方法是使用Ruby的内置方法
对于任何数字n
,如果prime\u division
返回包含单个元素的数组,则该元素将[n,1]
,并且n
将显示为prime。素数有因子n
和1
,因此必须与非素数的数字区别对待
require 'prime'
def list_squared(range)
range.each_with_object({}) do |i,h|
facs = Prime.prime_division(i)
ssq =
case facs.size
when 1 then facs.first.first**2 + 1
else facs.inject(0) { |tot,(a,b)| tot + b*(a**2) }
end
h[i] = facs if (Math.sqrt(ssq).to_i)**2 == ssq
end
end
list_squared(1..10_000)
#=> { 1=>[], 48=>[[2, 4], [3, 1]], 320=>[[2, 6], [5, 1]], 351=>[[3, 3], [13, 1]],
# 486=>[[2, 1], [3, 5]], 1080=>[[2, 3], [3, 3], [5, 1]],
# 1260=>[[2, 2], [3, 2], [5, 1], [7, 1]], 1350=>[[2, 1], [3, 3], [5, 2]],
# 1375=>[[5, 3], [11, 1]], 1792=>[[2, 8], [7, 1]], 1836=>[[2, 2], [3, 3], [17, 1]],
# 2070=>[[2, 1], [3, 2], [5, 1], [23, 1]], 2145=>[[3, 1], [5, 1], [11, 1], [13, 1]],
# 2175=>[[3, 1], [5, 2], [29, 1]], 2730=>[[2, 1], [3, 1], [5, 1], [7, 1], [13, 1]],
# 2772=>[[2, 2], [3, 2], [7, 1], [11, 1]], 3072=>[[2, 10], [3, 1]],
# 3150=>[[2, 1], [3, 2], [5, 2], [7, 1]], 3510=>[[2, 1], [3, 3], [5, 1], [13, 1]],
# 4104=>[[2, 3], [3, 3], [19, 1]], 4305=>[[3, 1], [5, 1], [7, 1], [41, 1]],
# 4625=>[[5, 3], [37, 1]], 4650=>[[2, 1], [3, 1], [5, 2], [31, 1]],
# 4655=>[[5, 1], [7, 2], [19, 1]], 4998=>[[2, 1], [3, 1], [7, 2], [17, 1]],
# 5880=>[[2, 3], [3, 1], [5, 1], [7, 2]], 6000=>[[2, 4], [3, 1], [5, 3]],
# 6174=>[[2, 1], [3, 2], [7, 3]], 6545=>[[5, 1], [7, 1], [11, 1], [17, 1]],
# 7098=>[[2, 1], [3, 1], [7, 1], [13, 2]], 7128=>[[2, 3], [3, 4], [11, 1]],
# 7182=>[[2, 1], [3, 3], [7, 1], [19, 1]], 7650=>[[2, 1], [3, 2], [5, 2], [17, 1]],
# 7791=>[[3, 1], [7, 2], [53, 1]], 7889=>[[7, 3], [23, 1]],
# 7956=>[[2, 2], [3, 2], [13, 1], [17, 1]],
# 9030=>[[2, 1], [3, 1], [5, 1], [7, 1], [43, 1]],
# 9108=>[[2, 2], [3, 2], [11, 1], [23, 1]], 9295=>[[5, 1], [11, 1], [13, 2]],
# 9324=>[[2, 2], [3, 2], [7, 1], [37, 1]]}
此计算大约花费了0.15秒
对于i=6174
(2**1) * (3**2) * (7**3) #=> 6174
及
经常解决此类问题的诀窍是从审判庭转到审判庭。在Python中(抱歉):
def列表的平方(m,n):
因子_平方_和={i:0,对于范围(m,n+1)内的i}
对于范围内的系数(1,n+1):
i=n-n%因子#小于或等于n的因子的最大倍数
当i>=m时:
因子平方和[i]+=因子**2
i-=系数
如果isqrt(fss)**2==fss},则返回{i(i,fss)在因子_平方_sum.items()中的i
def isqrt(n):
#从http://stackoverflow.com/a/15391420
x=n
y=(x+1)//2
当y
下一个优化是只对isqrt(n)
执行因子
,成对添加因子平方(例如,2
和i//2
)。经常解决此类问题的诀窍是从试用除法切换到a除法。在Python中(抱歉):
def列表的平方(m,n):
因子_平方_和={i:0,对于范围(m,n+1)内的i}
对于范围内的系数(1,n+1):
i=n-n%因子#小于或等于n的因子的最大倍数
当i>=m时:
因子平方和[i]+=因子**2
i-=系数
如果isqrt(fss)**2==fss},则返回{i(i,fss)在因子_平方_sum.items()中的i
def isqrt(n):
#从http://stackoverflow.com/a/15391420
x=n
y=(x+1)//2
当y
下一个优化是只对isqrt(n)
执行因子
,成对添加因子平方(例如,2
和i//2
)。优化的目的是什么?演出内存使用情况?可读性?如果你的代码运行,然后考虑移动你的问题。你在优化什么?演出内存使用情况?可读性?如果你的代码运行,然后考虑移动你的问题。第二个有很多相关的链接,可以更深入地探究原因。在这个网站上阅读知识的深度和广度是很有趣的。第二个有很多相关的链接,可以更深入地探究原因。在这个网站上阅读知识的深度和广度是很有趣的。