【问题标题】:Number theory algorithms. Most divisors on the segment数论算法。细分市场上的大多数除数
【发布时间】:2015-07-28 12:35:48
【问题描述】:

我正在寻找一种有效的算法来解决以下问题。让d(n) 表示n 的正除数的数量,其中n 是正整数。我们得到了一些1 <= a <= b <= 10^18,任务是在[a..b] 段上找到d 的最大值,并且(可能我们需要为此部分更复杂的算法)找到使d 的值最大化的数字。

前段时间在免费访问中发现如下代码:http://ideone.com/qvxPj

unsigned long long n, res;
int p, primes[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 51, 53, 59, 61, 67, 71};

unsigned long long mul(unsigned long long a, unsigned long long b){
    unsigned long long res = 0;

    while (b){
        if (b & 1LL) res = (res + a);
        if (res >= n) return 0;
        a = (a << 1LL);
        b >>= 1LL;
    }

    return res;
}

void backtrack(int i, int lim, unsigned long long val, unsigned long long r){
    if (r > res) res = r;
    if (i == p) return;

    int d;
    unsigned long long x = val;

    for (d = 1; d <= lim; d++){
        x = mul(x, primes[i]);
        if (x == 0) return;
        backtrack(i + 1, d, x, r * (d + 1));
    }
}

int main(){    
    p = sizeof(primes) / sizeof(int);

    while (scanf("%llu", &n) != EOF){
        res = 0;
        backtrack(0, 100, 1, 1);
        printf("Maximum number of divisors of any number less than %llu = %llu\n", n, res);
    }
    return 0;
}

如果有人向我解释它是如何工作的,我会非常高兴,因为(就我而言)这个程序运行得非常快。

提前感谢您的帮助。

【问题讨论】:

  • 请修复代码环境:在mul函数前添加一个空行。
  • 你是否考虑不同的除数?除数需要简单吗?例如,d(12) 是什么?
  • 我考虑所有除数,所以 d(1) = 1, d(prime) = 2, d(12) = 6

标签: c++ algorithm numbers number-theory


【解决方案1】:

它像这样遍历所有数字:

num = P1^D1 * P2^D2 * P3^D3 * ... * Ps^Ds
constraints:
  Pi <= 71
  1 <= Di <= 100
  sequence (Pi) is a sorted list of first s primes
  sequence (Di) is nonincreasing
  num <= n

让我们检查第一个约束。假设最小最优数有素因子q > 71。如果在这个数中没有使用任何素数 p ,那么我们可以将 q 替换为具有相同幂的 p。显然,除数的数量将保持不变,但数量会减少 -> 矛盾。那么没有小于 71 的未使用素数。但是直到 71 的所有素数的乘积已经非常大,以至于我们考虑的数字必须大于 64 位 n。这是不可能的。

现在让我们解释第二个和第三个约束。假设我们的最小最优数在其因式分解中有一个素数 q,但没有某个素数 p,其中 p 。然后我们可以将 q 替换为 p 以相同的顺序,该数字将具有相同数量的除数,但它会变得更少 -> 矛盾。这意味着寻找最优(最小)数的因式分解中的所有素数必须恰好是前 s 个素数。使用的素数集合中不能有孔。 顺便说一句,Di 很明显,因为即使 2^100 也不适合 64 位整数。

现在我们要解释第四个约束。假设对于某些iD[i] 。然后我们可以将 P[i]^D[i] * P[i+1]^D[i+1] 替换为 P[i]^D[i+1] * P[i+1]^D[i],数字会变小。例如,将 5^2 * 7^3 替换为 5^3 * 7^2:除数相同,但结果更小。显然,如果我们搜索最小最优数,我们也可以安全地假设这个条件。

现在让我们考虑一下代码。 mul 是一个计算 ab 乘积的小函数。它是通过一个有趣的二进制程序计算出来的。这个过程的主要原因是:如果乘积大于n,则函数返回0。这个过程只是防止溢出,否则可能发生。

最后,我们到了backtrack。这是一个通常的递归搜索。 val 是当前数字,r 是它的除数,i 显示我们现在要添加的素数的索引,lim 将每个素数的幂限制为 100。一开始你会看到更新当前最优答案(存储在res)和硬停止条件(使用所有素数)。

然后有一个循环检查当前素数的每个幂。它从 1 次方开始,因为禁止使用 0 次方。它保持x 中的当前数字,并在每次迭代时将其乘以 Pi 以增加功率。如果x 大于n,它会立即停止。最后,它调用自己以搜索下一个素数。

【讨论】:

    【解决方案2】:

    作为对@stgatilov 答案的补充,我将证明选择将质数限制为小于或等于 71 的质数是合理的。

    我使用稍微修改过的代码版本来记录具有最大除数的最大数字。对于 1000000000000000000 或 999999999999999999,我得到:

    897612484786617600 = 28 * 34 * 52 * 72 * 11 * 13 * 17 * 19 * 23 * 29 * 31 * 37

    共有 103680 个除数。

    这意味着对于所有 18 位十进制数字,不涉及大于 37 的素数来寻找除数最多的整数。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-12-14
      • 1970-01-01
      • 2015-08-23
      • 2019-10-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多