F系列定义为
F(0) = 1
F(1) = 1
F(i) = i * F(i - 1) * F(i - 2) for i > 1
任务是找到F(i)
的不同除数的数量这个问题来自Timus。我尝试了以下Python,但肯定会超出时间限制。这种强力方法不适用于大输入,因为它也会导致整数溢出。
#!/usr/bin/env python
from math import sqrt
n = int(raw_input())
def f(n):
global arr
if n == 0:
return 1
if n == 1:
return 1
a = 1
b = 1
for i in xrange(2, n + 1):
k = i * a * b
a = b
b = k
return b
x = f(n)
cnt = 0
for i in xrange(1, int(sqrt(x)) + 1):
if x % i == 0:
if x / i == i:
cnt += 1
else:
cnt += 2
print cnt
任何优化?
修改 我已经尝试了这个建议,并重写了解决方案:(不直接存储F(n)值,而是一系列因子)
#!/usr/bin/env python
#from math import sqrt
T = 10000
primes = range(T)
primes[0] = False
primes[1] = False
primes[2] = True
primes[3] = True
for i in xrange(T):
if primes[i]:
j = i + i
while j < T:
primes[j] = False
j += i
p = []
for i in xrange(T):
if primes[i]:
p.append(i)
n = int(raw_input())
def f(n):
global p
if n == 1:
return 1
a = dict()
b = dict()
for i in xrange(2, n + 1):
c = a.copy()
for y in b.iterkeys():
if c.has_key(y):
c[y] += b[y]
else:
c[y] = b[y]
k = i
for y in p:
d = 0
if k % y == 0:
while k % y == 0:
k /= y
d += 1
if c.has_key(y):
c[y] += d
else:
c[y] = d
if k < y: break
a = b
b = c
k = 1
for i in b.iterkeys():
k = k * (b[i] + 1) % (1000000007)
return k
print f(n)
它仍然给TL5,不够快,但这解决了值F(n)的溢出问题。
答案 0 :(得分:4)
首先看到这个wikipedia article on the divisor function。简而言之,如果你有一个数字并且你知道它的主要因素,你可以很容易地计算除数的数量(得到除数的TeX数学):
$n = \prod_{i=1}^r p_i^{a_i}$
$\sigma_x(n) = \prod_{i=1}^{r} \frac{p_{i}^{(a_{i}+1)x}-1}{p_{i}^x-1}$
无论如何,这是一个简单的功能。
现在,要解决您的问题,请将其保留为一组素数因子和指数大小,而不是将F(n)
作为数字本身。然后,计算F(n)
的函数只需要F(n-1)
和F(n-2)
的两个集合,对两个集合中相同素数因子的指数求和(对于不存在的集合假设为零)并另外添加数字i
的素数因子和指数大小的集合。这意味着您需要另一个简单的 1 函数来查找i
的素因子。
以这种方式计算F(n)
,你只需要将上面的公式(取自维基百科)应用到集合中,这就是你的价值。另请注意,F(n)
可能很快变得非常大。这个解决方案也避免使用大数字库(因为没有素数因子也没有指数可能超过40亿 2 )。
1 当然,对于任意大的i
来说,这并不是那么简单,否则我们现在就不会有任何形式的安全性,但对于您的应用程序来说,它应该足够简单。
2 好吧。如果您碰巧找出了一个简单的公式来回答任何n
给出的问题,那么在测试用例中也可以使用大n
个,这个算法很可能会超出时间限制
答案 1 :(得分:3)
这是一个有趣的问题。
F(n)
增长极快。自F(n) <= F(n+1)
n
以来,我们有
F(n+2) > F(n)²
适用于所有n
,因此
F(n) > 2^(2^(n/2-1))
代表n > 2
。粗略估计已经表明,除了最小的n
之外,任何人都无法存储这些数字。由此F(100)
需要超过(2^49)
位存储,而128 GB仅需2^40
位。实际上,F(100)
的主要因子分解是
*Fiborial> fiborials !! 100
[(2,464855623252387472061),(3,184754360086075580988),(5,56806012190322167100)
,(7,20444417903078359662),(11,2894612619136622614),(13,1102203323977318975)
,(17,160545601976374531),(19,61312348893415199),(23,8944533909832252),(29,498454445374078)
,(31,190392553955142),(37,10610210054141),(41,1548008760101),(43,591286730489)
,(47,86267571285),(53,4807526976),(59,267914296),(61,102334155),(67,5702887),(71,832040)
,(73,317811),(79,17711),(83,2584),(89,144),(97,3)]
这将需要大约9.6 * 10^20
(大约2^70
)位 - 稍微不到一半的位是尾随零,但即使存储数字àla浮点数与有效数和指数没有足够的存储空间。
因此,不是自己存储数字,而是可以考虑素数因子分解。这也可以更容易地计算除数的数量,因为
k k
divisors(n) = ∏ (e_i + 1) if n = ∏ p_i^e_i
i=1 i=1
现在,让我们稍微调查F(n)
的主要因素。我们从
引理:当且仅当p
时,F(n)
p <= n
分为F(0) = F(1) = 1
。
通过归纳很容易证明:<= 1
不能被任何素数整除,并且没有素数n > 1
。
现在假设A(k) = The prime factors of F(k) are exactly the primes <= k
和
k < n
适用于F(n) = n * F(n-1) * F(n-2)
。然后,因为
F(n)
n
的集素数因子是F(n-1)
,F(n-2)
和F(k)
的素数因子集的并集。
通过归纳假设,P(k) = { p | 1 < p <= k, p prime }
的素数因子集是
k < n
代表n
。现在,如果n
是复合的,则n
的所有素数因子都比F(n)
小,因此P(n-1)
的素因子集是n
,但是P(n) = P(n-1)
不是素数,n
。另一方面,如果F(n)
是素数,那么P(n-1) ∪ {n} = P(n)
的素数因子是
F(n)
有了这个,让我们看看一次跟踪n
的素数因子化,并更新每个n
的列表/字典是多少工作(我忽略了找到因子分解的问题) n
的{{1}},对于所涉及的小p
而言并不需要很长时间。
n = p
的条目n
的条目首先显示在N - p + 1
,然后针对每个F(N)
进行更新,总共为 ∑ (N + 1 - p) = π(N)*(N+1) - ∑ p ≈ N²/(2*log N)
p <= N p <= N
创建/更新N = 10^6
次1}}。因此有
3.6 * 10^10
总计更新。对于p
,大约p
次更新,这比允许的时间(0.5秒)更多。
所以我们需要一种不同的方法。让我们单独查看一个素数F(n)
,并按照v_p(k)
中p
的指数进行操作。
让k
成为v_p(F(n)) = v_p(n) + v_p(F(n-1)) + v_p(F(n-2))
的素数因子分解中v_p(F(k)) = 0
的指数。然后我们有
k < p
我们知道p
的{{1}}。所以(假设v_p(F(n)) = v_p(n) + v_p(F(n-1)) + v_p(F(n-2))
v_p(F(p)) = 1 + 0 + 0 = 1
v_p(F(p+1)) = 0 + 1 + 0 = 1
v_p(F(p+2)) = 0 + 1 + 1 = 2
v_p(F(p+3)) = 0 + 2 + 1 = 3
v_p(F(p+4)) = 0 + 3 + 2 = 5
v_p(F(p+5)) = 0 + 5 + 3 = 8
并不太小,无法理解发生了什么):
v_p(F(p+k)) = Fib(k+1)
所以我们得到指数的斐波那契数,p
- 暂时,因为后来p
的倍数会进一步注入v_p(F(2*p-1)) = 0 + Fib(p-1) + Fib(p-2) = Fib(p)
v_p(F(2*p)) = 1 + Fib(p) + Fib(p-1) = 1 + Fib(p+1)
v_p(F(2*p+1)) = 0 + (1 + Fib(p+1)) + Fib(p) = 1 + Fib(p+2)
v_p(F(2*p+2)) = 0 + (1 + Fib(p+2)) + (1 + Fib(p+1)) = 2 + Fib(p+3)
v_p(F(2*p+3)) = 0 + (2 + Fib(p+3)) + (1 + Fib(p+2)) = 3 + Fib(p+4)
,
2*p
但来自v_p(F(2*p+k)) = Fib(p+k+1) + Fib(k+1)
的额外权力也遵循一个不错的斐波那契模式,我们0 <= k < p
为p
。
对于 n/p
v_p(F(n)) = ∑ Fib(n + 1 - k*p)
k=1
的进一步倍数,我们在指数中得到另一个斐波纳契加数,所以
n >= p²
- 直到p²
,因为p³
的倍数对指数贡献两个,相应的加数必须乘以2;对于p
的倍数,乘以3等。
还可以分割p
的倍数的倍数的贡献,因此它会得到一个斐波那契加数,因为它是p²
的倍数,一个是p³
的倍数。 1}},一个是 n/p n/p² n/p³
v_p(F(n)) = ∑ Fib(n + 1 - k*p) + ∑ Fib(n + 1 - k*p²) + ∑ Fib(n + 1 - k*p³) + ...
k=1 k=1 k=1
等的倍数,产生
0 < a <= s
现在,特别是对于较小的素数,这些总和有很多术语,以这种方式计算它们会很慢。幸运的是,对于 m
∑ Fib(a + k*s) = (Fib(a + (m+1)*s) - (-1)^s * Fib(a + m*s) - (-1)^a * Fib(s - a) - Fib(a)) / D(s)
k=0
D(s) = Luc(s) - 1 - (-1)^s
,其中
Luc(k)
和k
是Luc(k) = Fib(k+1) + Fib(k-1)
- 卢卡斯数字10^9 + 7
。
出于我们的目的,我们只需要模数为D(s)
的斐波那契数,然后必须用F(n)
的模逆,乘以除法。
使用这些事实,10^9+7
模n <= 10^6
的除数的数量可以在{{1}}的允许时间内计算(在我的旧32位框上约为0.06秒),尽管使用Python,在测试机器上,可能需要进一步优化。