计算(m,n)的对的数量,其中GCD(m,n)= x,比如x = 1, 1 <= m <= M = 10 ^ 5且1 <= n <= N = 10 ^ 5.
M和N将被给予
我知道我们可以使用(Brute Force)2迭代器迭代M和N并检查GCD是否= = 1增加可能的对数。
但是这只适用于较小的数字,而对于较大的数字而言,它需要花费很多时间。像M = 100000和N = 100000
我们可以使用素因子来计算。
注意:我只想要可能的对数而不是对。
答案 0 :(得分:1)
让我们从您的强力算法开始:
for (n = 1; n <= N; n++) {
for (m = 1; m <= M; m++) {
if (gcd(m, n) == x) count++;
}
}
当x
大于1时,您可以加快速度,因为如果n
和m
的gdc为x
,则这些数字本身必须是x
:
for (n = x; n <= N; n += x) {
for (m = x; m <= M; m += x) {
if (gcd(m, n) == x) count++;
}
}
在这些循环中,我们可以将所有数字除以x
:
int NN = N / x;
int MM = M / x;
for (n = 1; n <= NN; n++) {
for (m = 1; m <= MM; m++) {
if (gcd(m, n) == 1) count++;
}
}
检查gdc为1是对互质对的测试。一些研究导致Wikipedia,并产生一个整洁(和奢侈的插图)算法,以生成所有互质对:
#define NN (N / X)
#define MM (M / X)
void spread(int m, int n, int *count)
{
if (n > NN) return;
if (m > MM) return;
if (n != m && m <= NN) (*count)++;
(*count)++;
spread(2*m - n, m, count);
spread(2*m + n, m, count);
spread(m + 2*n, n, count);
}
int main()
{
int count = 1;
spread(2, 1, &count);
spread(3, 1, &count);
printf("%d\n", count);
return 0;
}
计数从1开始,因为对生成器不生成(1,1),这也符合您的标准。如果M
大于或等于N
,则代码有效。如果没有,交换它们。
这种自下而上的方法比环路快得多,就像Erathostenes的筛子比对一系列数字进行天真的素性检查一样快。
我担心这是C,而不是Java。 count
作为指针传递;我猜Java成语可能是一个参考。当然,您也可以从spread
返回计数并累积。 (如果N
,NN
等不是全局的,那就太好了,但是我确定你会用Java包装一个漂亮,整洁的类。)
编辑:上面的代码是递归的,需要大量的堆栈空间。如果线性化代码并使用队列,则可以将所需空间从堆栈迁移到堆。代码看起来像这样:
int spread(m, n)
{
Queue q;
int count = 1;
q.push(m, n);
while (!q.empty()) {
int m, n;
q.pull(&m, &n);
if (n <= NN && m <= MM) {
if (n != m && m <= NN) count++;
count++;
q.push(2*m - n, m);
q.push(2*m + n, m);
q.push(m + 2*n, n);
}
}
return count;
}
int main()
{
Queue q;
int count = 1;
count += spread(2, 1);
count += spread(3, 1);
printf("%d\n", count);
return 0;
}
如果M
和N
超过100,000,则需要很长时间才能运行。但是您可以轻松地将其并行化,因为(2,1)和(3,1)情况是独立的。