问题解决的时候,一切都是那么舒爽...
一直以来,写的程序都是为了在限定时间以内运行而努力。
而这次,第一次想把自己的程序速度再进一步。
嗯,回到开始的想法:去除重复运算,用memory换时间
f(n)=n(n+1)/2=n1*n2 (n1=n/2,n2=n+1或者n1=n,n2=(n+1)/2)
因为n,n+1相邻,所以n1与n2之间没有共同的质数约数
n1与n2之间没有共同的质数约数!
也就是说 f(n)的约数个数 = n1的约数个数 × n2的约数个数
而f(n)和f(n+1)都会有个共同的部分 n+1 (或(n+1)/2)
这就是说:我们可以节省这部分重复运算!
这可以节省1/2甚至更多的运算量!
coding如下代码:
class Divisor
# primes: [2, 3, 5, 7, ...]
#attr_accessor :primes, :offset, :memory
def initialize
@primes = Array.new(1, 2)
@offset = 3
@memory = Array.new
end
def n(num)
return 1 if (num == 1)
n = 2
n += 1 while (num_of_divs(n) < num)
return n
end
# add n if it is a prime
def add_prime(n)
sqrt = Math.sqrt(n).to_i
@primes.each do |prime|
break if (prime > sqrt)
return if (n % prime == 0)
end
@primes << n
end
# number of divisors of f(n) = 1 + 2 + ... + n = n(n+1)/2
def num_of_divs(n)
n1 = n
n2 = n +1
sqrt = Math.sqrt(n2).to_i
# if not added (into primes' list), try to add it,
if (sqrt >= @offset)
add_prime(sqrt)
@offset += 1 #increase prime offset
end
# f(n) = n(n+1)/2 could be as multiplication of two numbers, either (n/2) * (n+1) or n * [(n+1)/2]
if (n1 % 2 == 0)
n1 /= 2
else
n2 /= 2
end
return num_of_divs_ex(n1) * num_of_divs_ex(n2)
end
# number of divisors of n
def num_of_divs_ex(n)
return @memory[n] if @memory[n] #return if it is already stored in memory
count = 1
m = n
@primes.each do |prime|
break if (m == 1)
if @memory[m]
count *= @memory[m]
@memory[n] = count
return count
end
# check whther <prime> is one of divisor of <m>
tmp = 0
while (m % prime == 0)
tmp += 1
m /= prime
end
# update <count> if <prime> is one of divisor of <m>
count *= (tmp + 1) if (tmp > 0)
end
count *= 2 if (m != 1)
@memory[n] = count
return count
end
end
start = Time.now
div = Divisor.new
print '->' + div.n(500).to_s + '<-'
final = Time.now
cost =final - start
print 'Cost: ' + cost.to_s500个约数仅需要0.313s,速度提高了将近3倍...
No comments:
Post a Comment