以清晰易懂的方式重新发布它,而不会出现任何无法正常显示的复杂MathJax:
我探索了一些计算机科学/数论挑战网站,他们很有趣地提出了以下问题:
让P(n) = sum{1<=k<=n} φ(k)
找到P(10^16)
我搜索了很长时间,并尝试了不同的方法:
使用φ(n)= n * product{1<=i<=k} (Pi-1)/Pi
的公式,我尝试计算范围内的φ(n)
,但是对于大的n
来说效率很低。通过这种方法,我可以达到10^7
的程度。除此之外,它变得太慢了。
我尝试了另一种方法,更加直接。维基百科和Wolfram Alpha建议使用类似公式直接计算P(n)
:
P(n) = sum {1<=k<=n} φ(k)= 0.5⋅(1+∑{1<=k<=n} μ(k)⋅⌊n/k⌋^2)
这个公式似乎更有希望。我尝试了一下,设法比10^7
走得更远,但距离目标还很远。通过预先计算Moebius函数的筛选,我可以比10^9
少一点。我的内存不足,无法再通过筛子计算值。即使可以,它仍然要花很长时间,而且距离10^16
还是很远。
这是我用Java编写的第二种方法所用的代码的一部分:
public static BigInteger PhiSummatoryFunction (long limit)
{
BigInteger sum = BigInteger.ZERO;
int [] m = MoebiusSieve(limit);
for (int i=1;i<m.length;i++)
sum=sum.add(BigInteger.valueOf((long) (m[i]*Math.floor(limit/i)*Math.floor(limit/i))));
return sum.add(BigInteger.ONE).divide(BigInteger.ONE.add(BigInteger.ONE));
}
其中MoebiusSieve是一个函数,它使用类似于橡皮擦的方法在筛子中计算Moebius函数的值,直到某个极限。
P(n)= n(n + 1)/ 2-∑ {2 <= i <=√n} P(⌊n/i⌋)-∑ {1 <= j <=√n} P( j)⋅(⌊n/ j⌋−⌊n /(j + 1)⌋)
我最多可以计算P(10^11)
的值,并且在最大内存分配的情况下,尽可能多地预先计算φ(n),因此我可以进行记忆的所有P(n)
都可以计算{ {1}}仅需20多分钟。一项重大改进,但与P(10^12)
仍有一段距离。可以花更长的时间来确定,但是我担心P(10^16)
将花费成倍的时间,这取决于P(10^16)
和P(10^11)
之间的计算时间“跳跃”。我的记忆使我可以“保存”最多P(10^12)
,或。也许有一种方法可以使用μ(k)值而不是φ(n)?
我所有的计算都表明并表明我的递归是主要的时间消耗者。这是显而易见的,但是我敢肯定它会花更长的时间。我在递归代码下面发布了一些文档。在我看来,这是进行此计算的正确方法,但我的实现并非最佳。
350,000,000 φ(n) values
除了数组之外,建议我将字典用于大的700,000,000 μ(k) values
值,但我对此一无所知。是否可以进行其他改进以使时间范围一天左右?
答案 0 :(得分:0)
如果您想知道单个数字 n 的总和,找到它的最佳方法是将 n 分解为因子,并将乘积减去每个因子1。 ;例如,30 = 2 * 3 * 5,然后从每个因子中减去1,然后相乘,得出的总和为1 * 2 * 4 =8。但是,如果要查找所有小于给定 n ,比将每个因子都分解的更好的方法是筛分。这个想法很简单:从0到 n 设置一个数组 X ,将 i 存储在每个 X_i 中,然后从0开始遍历数组,每当 X_i = i 遍历 i 的倍数时,将它们乘以( i -1)/ i 。您可以在末尾计算总和,也可以随时累加。由于筛子很大,因此您需要对其进行分段。
以下是我的博客中一些有用的页面:Sieving For Totients和Segmented Sieve of Eratosthenes。如果您在附近闲逛,可能还会发现其他有趣的东西。
答案 1 :(得分:0)
对于低阶订单:
public class Solution {
static Map<Long,Long> X2 = new HashMap<>();
static long F2(long N){
return N*(N-1)/2;
}
static long R2(long N){
if(N==1) return 0;
if(X2.containsKey(N)) return X2.get(N);
long sum = F2(N);
long m=2;
while(true){
long x = N/m;
long nxt = N/x;
if(nxt >= N) {
X2.put(N, sum - (N-m+1)*R2(N/m));
return X2.get(N);
}
sum -= (nxt-m+1) * R2(N/m);
m = nxt+1;
}
}
public long odradi(int N){
return R2(N)+1;
}
public static void main(String[] args) {
System.out.println(R2(100000)+1);
}
}