计算一个数字的质因数

Calculating the prime factors of a number

本文关键字:数字 质因数 一个 计算      更新时间:2023-10-16

我有以下要求:

2 < n < 999999 查找给定数n的素因数(不带其指数)。

我有以下算法可以解决问题,但似乎有一些性能问题:

bool is_prime(const unsigned long int &number) {
    for (unsigned long int i = 2; i <= sqrt(number); i++) {
        if (number%i == 0) return false;
    }
    return true;
}
unsigned long int next_prime(unsigned long int current) {
    current++;
    while (!is_prime(current)) {
        current++;
    }
    return current;
}
// THE PROBLEM SOLVER
vector<unsigned long int> find_divisors(const unsigned long int &n) {
    vector<unsigned long int> divisors;
    for (unsigned long int i = 2; i <= n; i = next_prime(i)) {
        if (n%i == 0) divisors.push_back(i);
    }
    return divisors;
}

问题:对于大数字,算法花费的时间太多(对于允许的最大数字,大约需要 1.5 秒)。

示例(正确):

  • n = 1620输出{2, 3, 5}
  • n = 2048输出{2}
  • n = 4096输出{2}

正如你所暗示的那样,你提出的算法效率非常低。但是对于规定的范围,对于具有 32 位整数算术的计算机来说,这是一个非常简单的问题。这是如何做到的:

for (int p = 2 ; p * p <= n ; p = (p == 2) ? 3 : (p + 2)) { // p = 2,3,5,7,9,...until p > sqrt(n)
  if (n % p) continue ;
  divisors.push_back (p) ;       // p divides n, so save it
  do n /= p ; while (!(n % p)) ; // Remove all divisors p from n
}
if (n > 1) divisors.push_back (n) ;

我不打算解释这一点:通过自己弄清楚,你会学到更多。通过各种微优化,它可以做得更快一点,但它基本上是你规定的范围的最优选择,除非你使用素数表。这样的表格可能会让它更快一点,但在大多数情况下,这是不值得的。

主要优化:你不需要搜索到n,你可以搜索到sqrt(n)。剩下的任何东西都是首要的。二次优化:划分您找到的素数以减少您搜索的边界。

可以做更多的事情,但这已经快了大约一千倍。

vector<unsigned long int> find_divisors(const unsigned long int &m) {
  unsigned long int n = m;
  vector<unsigned long int> divisors;
  if (n%2 == 0) {
    divisors.push_back(2);
    while (n%2==0) n/=2;
  }
  for (unsigned long int i = 3; i*i <= n; i += 2) {
      if (n%i) continue;
      divisors.push_back(i);
      while (n%i==0) n/=i;
  }
  if (n > 1) divisors.push_back(n);
  return divisors;
}

对于如此有限的数字范围,硬编码最多 1000 的素数表是完全可以接受的。

int P[169]= {
    2,   3,   5,   7,  11,  13,  17,  19,  23,  29,  31,  37,  41,
   43,  47,  53,  59,  61,  67,  71,  73,  79,  83,  89,  97, 101,
  103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167,
  173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239,
  241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313,
  317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397,
  401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467,
  479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569,
  571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643,
  647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733,
  739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823,
  827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911,
  919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, 1009          
};
int i= 0;
while (i < 169 && P[i] < Number)
{
    if (Number % P[i] != 0)
        i++;
    else
        Number/= P[i]; // P[i] is a factor
}
// Number is the last factor

您不缓存任何结果:相反,您应该考虑使用

map<int,vector<int>> vPrimes

此映射将包含给定数字的所有素因数。例如,对于 50,它将包含 2,5 (2*5*5=50)。然后假设您尝试计算 300,然后进行测试 (300%3=0),然后将 3 添加到列表中,结果为 (2,3,5)。

希望有帮助,

来自维基百科:

大整数的因式分解被认为是计算上的 非常困难的问题,以及许多现代密码学的安全性 系统基于其不可行性。[注11]

最好的方法是通过埃拉托色尼的筛子或波拉德-罗算法来暴力破解素因数。因为数字不是那么大(最多 100000),所以你可以在现代硬件上非常快速地预先计算所有素因数。干杯