根据 n 的大小(例如,大约 10,000,000),您可能会使用一个简单的 for 循环,该循环会在很短的时间内(例如大约一秒)运行。
我不知道您是否可以找到通用 T(1) 和/或通用递归的数学公式,但我猜您找不到。不过,我可以告诉你一个可以帮助你解决问题的数学属性。它被称为congruence。简而言之,语法是这样的:
a =(15)= b
表示 15 分 b - a。实际的数学符号是一个三行的=,上面写着数字,但我真的打不出来!
现在这里有几个对你有用的定理:
1.
a =(n)= b \
> => a =(n)= c
b =(n)= c /
2.
a =(n)= b => a+c =(n)= b+c
3.
a =(n)= b => a*c =(n)= b*c
4.
a =(n)= b => a^2 =(n)= b^2
他们可以很容易地通过将a和b写成:
a = k1*n+r
b = k2*n+r
并应用转换并确保最终b - a 仍可被n 整除。
也就是说,您的算法如下(假设您想要 T1 到 TN mod M 的总和):
T = 3 /* initial T1 */
TSum = T /* initial sum */
for i=1 to N
T = (T^2 % M + T + 2) % M
TSum = (TSum + T) % M
这里要注意的重要一点是T 和TSum 总是以M 为界,最大中间结果来自表达式T^2(对于非平凡的Ms)可以采取最多(M-1)^2。
因此,在您的实现中,您实际上不需要处理非常大的数字,而只需处理一个足够大的数据类型以容纳(M-1)^2。在 C 语言中,uint64_t 就可以了。请注意,对于 M=100000,(M-1)^2 不适合 32 位整数。
顺便说一下,这个算法是O(N),所以除非N真的很大或者除非它处于非常频繁的循环中,否则它应该足够快以满足您的日常需求!
编辑
这个问题实际上可以在O(M)而不是O(N)中解决。这是因为所有T(i) 都在[0, M-1) 的范围内,因此计算到T(M+1),你肯定会循环回来。由于T(n) 仅依赖于T(n-1),因此获取T(n-1) 的重复值将导致与第一次相同的值链。
所以,让我们展开 T 和 TSum 以更好地观察如何利用它。假设T 生成值A、B、...、Z,在Z 之后,它循环回到K 并且在几个循环之后它在P 上完成(因为我们达到了N):
T A B C D E ... K ... Z K ... Z K ... Z ... K ... P
TSum AS BS CS DS ES ... KS ... ZS KS2 ... ZS2 KS3 ... ZS3 ... KSt ... PSt
所以你的目标是计算PSt。这个想法是计算到KS2,将其与KS 的差值乘以t,然后将其与KS 相加得到KSt。然后将剩余的相加得到PSt。
算法如下:
Sums=[M times 0] /* initially, no sum is calculated */
Indices=[M times 0] /* Indices[i] = I means Sums[i] corresponds to T(1)+...+T(I) */
T = 3 /* initial T1 */
TSum = T /* initial sum */
Sums[T] = TSum
Indices[T] = 1
for i=2 to N
T = (T^2 % M + T + 2) % M
if Sums[T] != 0 /* a loop is detected */
break
TSum = (TSum + T) % M
Sums[T] = TSum
Indices[T] = i
if i == N
return TSum
/* compute how many cycles */
cycle_length = i - Indices[T]
t = (N - Indices[T]) / cycle_length
/* add sum of the cycles immediately */
TSum = (Sums[T] + t * (TSum - Sums[T])) % M
/* add what is left */
for i=Indices[T] + t * cycle_length+1 to N
T = (T^2 % M + T + 2) % M
TSum = (TSum + T) % M
注意:指数计算中可能存在误差。如果您打算使用此算法,请仔细检查以确保它不会遗漏任何 T(i) 或将其相加两次。