【问题标题】:needed explanation about implementing the 6n-+1 algorithm for primes?需要解释为素数实现 6n-+1 算法吗?
【发布时间】:2021-09-20 08:03:03
【问题描述】:

我在 c 中探索素数算法,直到我偶然发现了这个,哦,天哪,算法真的很快,知道它在 2 之后实现素数的 6n-+1 属性以找到下一个最近的素数,但是代码的某些部分我仍然没有得到,首先为什么考虑到他从 2,3 开始,如果它们分别出现则返回 true,下一个条件 if (nb % 2 == 0 || nb % 3 == 0) return FALSE; 的用途是什么?,每个其他素数都不可整除除了 2 和 3 之外的 2 和 3 并且应该同时进行检查,那么 if 条件有什么用?其次,我所说的 while 循环应该实现 6n-+1 属性,但我真的没有看到循环中的数学表达式和测试表达式之间的相关性?最后,while 中的两个条件似乎是可爱的和数学的,但我没有那么多使用它们,请你澄清一下。非常感谢。

#include <stdio.h>
#define TRUE 1
#define FALSE 0
int is_prime(int nb)
{
    int divisor;

    if (nb == 2 || nb == 3)
        return TRUE;
    if (nb % 2 == 0 || nb % 3 == 0)
        return FALSE;
    divisor = 6;
    while (divisor * divisor - 2 * divisor + 1 <= nb)
    {
        if (nb % (divisor - 1) == 0)
            return FALSE;
        if (nb % (divisor + 1) == 0)
            return FALSE;
        divisor += 6;
    }
    return (1);
}

int find_next_prime(int nb)
{
    while (!is_prime(++nb))
    {};
    return nb;
}
int main()
{
    printf("%i", find_next_prime(78314683));
    return (0);
}

【问题讨论】:

  • 那个属性是用来skip 2和3的倍数尽可能divisors,但是函数还是需要知道这个数是否经过这样的倍数。
  • @Bob_ 确实如此,请大声说为什么它会跳过 3 和 2 的倍数?
  • 试试铅笔和纸,写下所有的数字,检查所有 2 和 3 的倍数,看看剩下的数字在哪里。现在进入代码并计算divisor 的所有值。我会从 5 开始,然后添加 2,然后添加 4,然后添加 2,依此类推,但这就是我。

标签: c algorithm performance primes


【解决方案1】:

首先,如果数字是 2 和 3,则代码返回 true,因为它们是质数。 if (nb % 2 == 0 || nb % 3 == 0) 用于丢弃所有可以除以 2 或 3 的数字(除了 2 和 3 之前已被接受为素数),因为它们显然不是素数。此条件对于正确测试较小的nb 值是必需的。实际上,while 循环以 divisor 设置为 6 开始,因此 divisor * divisor - 2 * divisor + 1 = 25 这意味着 nb 必须大于或等于 25,以便执行循环的一次迭代。请注意,之所以选择 25,是因为所有不是素数的较小数字都可以除以 2 或 3,并且前面的条件可以捕获所有这些数字。

关于主循环,想法是当数字大于 6 时,只有数字 nb=6k+1nb=6k-1 可以(但不是必需的)素数。事实上,nb=6k+0 可以被 6 简单地整除,nb=6k+2 可以被 2 整除,nb=6k+4nb=6k+3 可以被 3 整除。剩下的是nb=6k+1nb=6k+5。后者可以用不同的k 重写为nb=6k-1

此算法是对迭代 2 到 sqrt(nb) 之间的所有可能数字以找到除数的算法的简单优化。这种优化是可能的,因为不需要检查所有除数,而只需检查素数。

【讨论】:

    【解决方案2】:

    我真的不认为有关于这个算法的证明,但它在某种程度上是独一无二的和棘手的。如果你检查素数序列,你会发现两个连续的最大差异是6,这可能是divisor = 6divisor += 6的想法。但关键是,没有关于素数序列的经过验证的规定。所以也许它不能在大量数据上正常工作。他使用的另一个无法再次证明的可爱技巧是 6k+1 和 6k-1 中至少有一个是素数。对于 Sieve 来说,这是一个很好但不太强大的优化。显然,这不是你能找到的最快的素数算法,但也许你会发现它的技巧对另一个问题很有用。

    【讨论】:

    • 只是在大量测试后失败了,我尝试过并得到错误的结果
    • 首先我建议您检查用于大数字的数据类型。尝试将 1e10 或更大的数字保存为整数,这让我很受伤。不过,正如我所提到的,没有关于素数的规定。如果有,你可以检查一个数字是否在 O(1) 中是素数!
    • 关于证明:primes.utm.edu/notes/faq/six.html 另外,不,6 不是最大差异:en.wikipedia.org/wiki/Prime_gap ;)
    【解决方案3】:

    让我们从一些理论开始。

    首先,n % prime == (n % (prime * anything)) % prime

    这意味着这些都是正确的:

    n % prime1 == (n % (prime1 * prime2)) % prime1
    n % prime2 == (n % (prime1 * prime2)) % prime2
    
    temp = n % (prime1 * prime2)
    n % prime1 == temp % prime1
    n % prime2 == temp % prime2
    

    让我们选择素数 2 和 3:

    temp = n % 6
    n % 2 == temp % 2
    n % 3 == temp % 3
    

    temp 只有 6 个可能的值:

    0, divisible by both 2 and 3
    1, not divisible by either 2 or 3
    2, divisible by 2
    3, divisible by 3
    4, divisible by 2
    5, not divisible by either 2 or 3
    

    这是“6n-+1 属性”。请注意,2 可被 2 整除,3 可被 3 整除,因此对于这两种情况,我们需要额外检查以确定“可被 2 或 3 整除”是否实际上意味着“是质数 2 或 3”(或者它是否意思是“不能是素数”)。

    但是……为什么要停在那里?

    有了 3 个素数,扩展起来很简单:

    temp = n % (prime1 * prime2 * prime3)
    n % prime1 == temp % prime1
    n % prime2 == temp % prime2
    n % prime3 == temp % prime3
    

    让我们选择素数 2、3 和 5:

    temp = n % 30
    n % 2 == temp % 2
    n % 3 == temp % 3
    n % 5 == temp % 5
    

    现在temp 有 30 个可能的值:

    0, divisible by 2, 3 and 5
    1, not divisible by either 2, 3 or 5
    2, divisible by 2
    3, divisible by 3
    4, divisible by 2
    5, divisible by 5
    6, divisible by both 2 and 3
    7, not divisible by either 2, 3 or 5
    8, divisible by 2
    9, divisible by 3
    10, divisible by 2 and 5
    11, not divisible by either 2, 3 or 5
    12, divisible by both 2 and 3
    13, not divisible by either 2, 3 or 5
    14, divisible by 2
    15, divisible by 3 and 5
    16, divisible by 2
    17, not divisible by either 2, 3 or 5
    18, divisible by both 2 and 3
    19, not divisible by either 2, 3 or 5
    20, divisible by 2 and 5
    21, divisible by 3
    22, divisible by 2
    23, not divisible by either 2, 3 or 5
    24, divisible by both 2, 3 and 5
    25, divisible by 5
    26, divisible by 2
    27, divisible by 3
    28, divisible by 2
    29, not divisible by either 2, 3 or 5
    

    我们可以将 30 种可能性打包成 30 位,得到常数 0x208A2882;然后使用if( (0x208A2882ULL &amp; (1 &lt;&lt; n%30)) != 0) /* Not divisible by 2, 3 or 5 */

    但是……为什么要停在那里?

    如果您选择 2、3 和 7,则需要 42 位(n%(2*3*7) 的 42 种可能性)并且可以使用 uint64_t 常量;如果您选择 5 和 11,则需要 55 位并且可以使用不同的 uint64_t 常量;因此,使用 2 个常量,您可以确定“可被 2、3、5、7 或 11 整除”。

    但是……为什么要停在那里?

    没有理由不能选择 2、3、5、7、11 和 13;并做temp = n % (2*3*5*7*11*13) 并有一个大的(30030 位)位域而不是一个整数常量。

    但是……为什么要停在那里?

    对于很多情况(您的find_next_prime(int nb)),单个“是/不可分割”标志很好,但是“到下一个不可分割的整数的偏移量”会好很多,因为你可以一次完全跳过很多数字。您可以执行以下操作:

    int find_next_prime(int nb)
    {
        do {
            if(nb > 13) {
                nb += table[nb % 30030];
                /* nb is not divisible by 2, 3, 5, 7, 11 or 13 */
    

    【讨论】:

    • 详细信息:anythinganything &gt;= 1 时有效,所以不完全是任何东西
    【解决方案4】:

    is_prime() 在功能上并非对所有int 都正确。

    过于关注速度往往无法提供正确的功能。

    最好先获得正确的功能,然后再担心速度。


    小值

    is_prime() 报告 1 是素数。

    代码无法处理小nb。简单修复:

    // if (nb == 2 || nb == 3)
    //    return TRUE;
    if (nb <= 3) {
      return nb == 2 || nb == 3;
    }
    

    大值

    is_prime(primes near INT_MAX)(例如 2147483647 可能还有其他)在为素数时报告为非素数。这是由于divisor * divisor 的整数溢出(未定义行为)。

    一个潜在的修复:

      divisor = 6 - 1;
      while (divisor <= nb / divisor) {
        if (nb % (divisor - 1 + 1) == 0)
          return FALSE;
        if (nb % (divisor + 1 + 1) == 0)
          return FALSE;
        divisor += 6;
      }
    

    div() 用于第一个nb / divisornb % (divisor - 1 + 1) 计算。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-10-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多