【问题标题】:(Relatively) Quickly find some divisor for a number < 10 000 000(相对)快速找到小于 10 000 000 的数的除数
【发布时间】:2015-01-29 04:34:14
【问题描述】:

假设我所说的只是小于 1000 万的自然数。

我希望为 10 000 000 以下的所有数字预先生成一个最低素除数 (LPD) 列表。例如,LPD(14) == 2、LPD(15) == 3,以及任何素数的 LPD 都是它自己。

我已经预先生成了所有的素数。访问第 n 个素数是一个简单的数组查找。效率为:O(1)

我已经预先生成了一个查找表,用于确定给定数字是否为素数。访问第 n 个素数是一个简单的数组查找。效率为:O(1)

现在,我计算给定数字的 LPD 的简单算法是循环遍历所有素数,直到一个素数除以该数字。但这需要很长时间。我可以在找到所有数字的最低除数所需的一半时间内生成所有低于 1000 万的素数(使用阿特金筛,我不明白,但用伪代码实现)。

有没有更好的算法来计算最低素除数?

【问题讨论】:

  • 希望你只考虑小于sqrt(10 000 000)的素数?
  • 好点,我也希望如此。大多数人不遵循这种做法
  • 是的,我首先检查 IsPrime(n),然后检查所有素数。如果 n 不是素数,则存在一个小于 n 的素数除以 n。我循环直到找到那个素数,它总是恰好小于 sqrt(n)。

标签: c# math primes


【解决方案1】:

实际上并不确定为什么您期望在相同的问题上获得更高的性能。

筛法不是除法,而是取每个素数,将其所有倍数标记为将自身作为最低素数因子,除非已经标记。

int lpf[MAX] = {};
int primes[MAX_PRIME];

for(int i = 0; i < MAX_PRIME; ++i)
{
    int mult = primes[i];
    while(mult < MAX)
    {
       if (lpf[mult] == 0)
       {
            lpf[mult] = primes[i];
       }
       mult += primes[i];
    }
}

末尾的任何未标记的数字本身都是素数,因此这种方法与找到MAX下的所有素数所需的时间相同。

【讨论】:

  • 遗憾的是,这种方法比我原来的算法慢了大约 5%。但是,好建议!事实证明,您的方法可以很容易地进行优化,我的新代码运行速度比阿特金筛子一开始就生成素数的速度要快,所以我很满意!
  • 为了给予应得的荣誉,my method 最初应归功于 Eratosthenes
  • 这是最后的改编! stackoverflow.com/a/28207604/2028184谢谢!
  • 而不是mult *= primes[i]; 我想你想要mult += primes[i];
【解决方案2】:

根据@Keith 的回答改编,新代码运行得更快(旧速度的13%!):

    public void SieveDivisors() {
        int iNum, iPrime, i6Prime; 
        _iaFirstDivisors = new int[_iLimit];
        _iaFirstDivisors[1] = 1;
        //Start at the largest primes, then work down. This way, we never need to check if the
        // lowest prime multiple is already found, we just overwrite it
        //Also, skip any multiples of 2 or 3, because setting those is a waste of time
        for (int iPrimeIndex = _iaPrimes.Length - 1; iPrimeIndex >= 1; iPrimeIndex--) {
            iPrime = _iaPrimes[iPrimeIndex];
            i6Prime = iPrime * 6;
            for (iNum = iPrime; iNum < _iLimit; iNum += i6Prime) {
                _iaFirstDivisors[iNum] = iPrime;
            }
            for (iNum = iPrime * 5; iNum < _iLimit; iNum += i6Prime) {
                _iaFirstDivisors[iNum] = iPrime;
            }
        }
        //Then record all multiples of 2 or 3
        for (iNum = 3; iNum < _iLimit; iNum += 6) {
            _iaFirstDivisors[iNum] = 3;
        }
        for (iNum = 2; iNum < _iLimit; iNum += 2) {
            _iaFirstDivisors[iNum] = 2;
        }
    }

【讨论】:

    【解决方案3】:

    您说您正在使用阿特金筛法生成素数列表。如果您使用 Eratosthenes 筛子,您会自动获得 LPD 阵列 - 它只是您用于筛子的阵列。不是存储布尔轨道,而是存储使数字复合的第一个素数。

    这是一些伪 C 代码:

    int lpd[MAX] = {};
    int primes[MAX_PRIMES];
    int nprimes = 0;
    
    void sieve() {
      for (int p = 2; p*p < MAX; ++p) {
        if (lpd[p] == 0) {
          primes[nprimes++] = p;
          lpd[p] = p;
          for (int q = p*p; q < MAX; q += p) {
            if (lpd[q] == 0) { lpd[q] = p; }
          }
        }
      }
    }
    

    最后,数组lpd[] 将包含最小的素数除数,primes[] 将包含素数列表。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-10-01
      • 1970-01-01
      • 2021-10-10
      • 2017-02-09
      • 2020-10-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多