繁体   English   中英

是否有更快的算法来计算素数达到给定限制?

[英]Is there a faster algorithm to compute prime numbers up to a given limit?

据您所知,是否有更快的算法来计算素数到给定数?

我设法在我的快速桌面上在大约 18 秒内计算了多达 10 亿个素数。

我设法在不到 2.5 小时内计算了多达 4000 亿个素数,但它需要大约 26 Gigs 的可用内存。

那么,有更快的方法吗?

试试代码,看大数调试output很有趣。

西安。

$ cat Prime2.cpp 
// ----------------------------------------------------------------------
// Title: Computing primes numbers
//        this algorithm is near O(1) (linear)
//
//  Note: Using a bitmap rather than an array of "unsigned long long"
//        is a lot more memory efficient and really fast nevertheless.
//
// Author: Christian Robert
//   Date: 2021-11-01
//
// To compile: gcc -Wall -O3 -o Prime2 Prime2.cpp -lstdc++ -lm
//
// @todo: May be make it multithreads. That should be very difficult
//        and I'm not sure at all it would be faster...
// ----------------------------------------------------------------------


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <locale.h>
#include <sys/time.h>
#include <getopt.h>
#include <time.h>
#include <math.h>


typedef unsigned long long ULL;
static const ULL LEFT_ONE = (ULL) 0x8000000000000000;

static unsigned long Debug = 1;  // 0, 1 or 2

// ---------------------
// Begin of class Bitmap

class Bitmap
{
  public:
    Bitmap (ULL size);
   ~Bitmap (void);

    bool GetBit (ULL pos);
    void SetBit (ULL pos);
    void ClrBit (ULL pos);

    int bm_bits_per_ULL;
    ULL bm_bits_size;
    ULL bm_real_size;
    ULL *bm_vector;
};

Bitmap::Bitmap (ULL size)
{
  bm_bits_per_ULL = sizeof(ULL)*8;
  bm_bits_size = size;
  bm_real_size = (bm_bits_size+127)/bm_bits_per_ULL;
  bm_real_size = bm_real_size >> 1;  // Only Odd numbers, optimizing for memory

  bm_vector = new ULL[bm_real_size];

  for (ULL i=0; i<bm_real_size ; ++i)
    bm_vector[i] = 0xffffffffffffffff; // Assume every ODD number is a prime number
}

Bitmap::~Bitmap (void)
{
  if (bm_vector) delete bm_vector;
  bm_vector = NULL;
}

bool Bitmap::GetBit (ULL at)
{
  ULL pos;
  unsigned off;

  if (at == 2) return 1;        // 2 is a prime number
  if ((at & 1) == 0) return 0;  // Even numbers are not prime numbers

  at = (at >> 1) + 1;

  pos = at/bm_bits_per_ULL;
  off = at%bm_bits_per_ULL;
  return ((ULL)0) != (bm_vector[pos] & (LEFT_ONE>>off));
}

void Bitmap::SetBit(ULL at)
{
  ULL pos;
  unsigned off;

  if (at == 2) return;
  if ((at & 1) == 0) return;

  at = (at >> 1) + 1;

  pos = at/bm_bits_per_ULL;
  off = at%bm_bits_per_ULL;
  if (Debug>1) fprintf (stderr, "SetBit pos=%'llu, off=%'u\n", pos, off);

  bm_vector[pos] = bm_vector[pos] | (LEFT_ONE >> off);
}

void Bitmap::ClrBit(ULL at)
{
  ULL pos;
  unsigned off;

  if (at == 2) return;
  if ((at & 1) == 0) return;

  at = (at >> 1) + 1;

  pos = at/bm_bits_per_ULL;
  off = at%bm_bits_per_ULL;
  if (Debug>1) fprintf (stderr, "ClrBit pos=%'llu, off=%u\n", pos, off);

  bm_vector[pos] = bm_vector[pos] & ~((LEFT_ONE) >> off);
}

// End of class Bitmap
// -------------------

// ---------------------------
// Get a microsecond timestamp
// ---------------------------
const char *TS(void)
{
  #define MAX_RECURSION 8 // Not usefull in this program but may be elsewhere if reused.

  static char curtime[MAX_RECURSION][64];
  static int  curidx = 0;
  struct tm *tm;
  struct timeval tv;
  int    to_return;

  gettimeofday (&tv, NULL);

  to_return = curidx;

  tm = localtime (&tv.tv_sec);

  snprintf (curtime[curidx++], sizeof(curtime[0]),
            "%04d-%02d-%02d@%02d:%02d:%02d.%06ld:",
            tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
            tm->tm_hour, tm->tm_min, tm->tm_sec,
            tv.tv_usec);

  if (curidx >= MAX_RECURSION) curidx = 0;

  return curtime[to_return];
}

// ----------------------------------------------------
// Set a bit in the bitmap and clear all multiples bits
// ----------------------------------------------------

void SetPrime (Bitmap* bm, ULL number)
{
  if (Debug>0) fprintf (stderr, "%s New prime number %'llu, Clearing bits multiples of %'llu upto %'llu ... ",
                        TS(), number, number, bm->bm_bits_size);

  bm->SetBit (number);  // not usefull, bit is supposed to be already ON

  if (Debug > 1) fprintf(stderr, "\n");

  for (ULL i=2*number ; i <= bm->bm_bits_size ; i += number)
  {
    bm->ClrBit(i);
  }
  if (Debug>0) fprintf (stderr, "Done.\n");
}

// --------------------
// Clearing a prime bit
// --------------------

void ClrPrime (Bitmap* bm, ULL number)
{
  bm->ClrBit (number);
}

// --------------
// Usage(argv[0])
// --------------

void Usage (const char *progname)
{
  fprintf(stderr, "Usage: [\"time\"] %s [-d debug_level] { [-n upto_number] | upto_number } > outfile\n", progname);
  exit(EXIT_FAILURE);
}

// ------------
// main program
// ------------

int main (int argc, char *argv[])
{
  setlocale (LC_NUMERIC, "en_US.utf8");

  extern char *optarg;

  int opt, flag_n = 0;
  ULL number = 0; // Default

  while ((opt = getopt(argc, argv, "n:d:")) != -1)
  {
    switch (opt)
    {
      case 'n':
        number = strtoull (optarg, NULL, 10);
        flag_n = 1;
        break;

      case 'd':
        Debug = strtoul (optarg, NULL, 10);
        break;

      default: /* '?' */
        Usage(argv[0]);
    }
  }

  if (flag_n == 0)
  {
    if (optind >= argc)
    { 
      fprintf (stderr, "Required missing \"-n upto_number\" OR \"upto_number\".\n\n");
      Usage (argv[0]);
    } else {
      number = strtoull (argv[optind], NULL, 10); // Get it from command first non-optionnal args
    }
  }

  if (number < 2)
  {
    fprintf (stderr, "Your \"upto_number\" has no sence.\n");
    Usage (argv[0]);
  }

  // Allocate the Huge array (this may fail if not enough memory)

  Bitmap *bm = NULL;

  try {
    bm = new Bitmap(number);
  }
  catch (...)
  {
    fprintf (stderr, "Not enough memory to compute primes upto %'llu.\n", number);
    exit (EXIT_FAILURE);
  }

//
// Target number =  47,055,833,459 for 2 Billions primes (16m9s on my computer, 3+Gigs of ram)
//                 400,000,000,100 for ? Billions primes (135m  on my computer, 26+Gigs of ram)
//

  fprintf (stderr, "%s bm_bits_per_ULL = %u\n", TS(), bm->bm_bits_per_ULL);
  fprintf (stderr, "%s bm_bits_size = %'llu\n", TS(), bm->bm_bits_size);
  fprintf (stderr, "%s bm_real_size = %'llu (%'llu Memory Bytes)\n", TS(), bm->bm_real_size, bm->bm_real_size*8);

// Bootstrap the Bitmap

  ClrPrime (bm, 0);    // 0 is not a prime number
  ClrPrime (bm, 1);    // 1 is not a prime number
  SetPrime (bm, 2);    // 2 is a prime number

// Compute prime numbers {3 .. number}

  ULL SQRT = sqrt(bm->bm_bits_size);

  for (ULL i=3; i<=SQRT ; i+=2)
  {
    bool was = bm->GetBit(i);
    if (was) SetPrime(bm, i);
  }

// All computation done, now print the results

  fprintf (stderr, "%s Writing result to stdout...\n", TS());

  for (ULL i=2 ; i<=bm->bm_bits_size ; ++i)
  {
    if (bm->GetBit(i) == 1) printf ("%llu\n", i);
  }

  fprintf (stderr, "%s All done.\n", TS());

  delete bm;
  return 0;
}

有一个 100% 的数学测试将检查数字P是素数还是合数,称为AKS Primality Test。

这个概念很简单:给定一个数P ,如果(x-1)^P - (x^P-1)的所有系数都可以被P,那么P是素数,否则是合数。

例如,给定 P = 3,将给出多项式:

   (x-1)^3 - (x^3 - 1)
 = x^3 + 3x^2 - 3x - 1 - (x^3 - 1)
 = 3x^2 - 3x

并且系数都可以被3整除,因此这个数字是素数。

P = 4的例子,这不是一个素数会产生:

   (x-1)^4 - (x^4-1)
 = x^4 - 4x^3 + 6x^2 - 4x + 1 - (x^4 - 1)
 = -4x^3 + 6x^2 - 4x

在这里我们可以看到系数 6 不能被 4 整除,因此它不是素数。

多项式(x-1)^PP+1项,可以使用组合找到。 所以,这个测试将在O(n)运行时运行,所以我不知道这会有多大用处,因为你可以简单地将i0迭代到 p 并测试剩余部分。

Yes, this the fastest algorithm to compute the maximum number
of Primes in the minimum of time, given your computer memory.

no other algotithm can compete with this one.

the problem is that you need 1/2 bit of free memory for each
tested numbers, so, 100 Billions need 6.3 Gigs of memory...

A trillion will need over 64 Gigs of memory... and so on

Xtian.

[xtian@fedora:/home/xtian] $ ./Prime2 1000000000 -d0 | wc -l
2021-12-16@00:03:57.516219: bm_bits_per_ULL = 64
2021-12-16@00:03:57.516291: bm_bits_size = 1,000,000,000
2021-12-16@00:03:57.516297: bm_real_size = 7,812,500 (62,500,000 Memory Bytes)
2021-12-16@00:04:08.117452: Writing result to stdout...
2021-12-16@00:04:14.508408: All done.
50847534

[xtian@fedora:/home/xtian] $ ./Prime2 10000000000 -d0 | wc -l
2021-12-16@00:04:35.905754: bm_bits_per_ULL = 64
2021-12-16@00:04:35.905819: bm_bits_size = 10,000,000,000
2021-12-16@00:04:35.905824: bm_real_size = 78,125,000 (625,000,000 Memory Bytes)
2021-12-16@00:06:35.734539: Writing result to stdout...
2021-12-16@00:07:38.015008: All done.
455052511

[xtian@fedora:/home/xtian] $ ./Prime2 100000000000 -d0 | wc -l
2021-12-16@00:08:15.321612: bm_bits_per_ULL = 64
2021-12-16@00:08:15.322381: bm_bits_size = 100,000,000,000
2021-12-16@00:08:15.334243: bm_real_size = 781,250,000 (6,250,000,000 Memory Bytes)
2021-12-16@00:30:33.727373: Writing result to stdout...
2021-12-16@00:40:30.536159: All done.
4118054813

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM