生成一个特定数量的素数列表

时间:2010-09-24 18:41:05

标签: algorithm r primes

我正在尝试生成低于10亿的素数列表。我正在尝试这个,但这种结构非常糟糕。有什么建议吗?

a <- 1:1000000000
d <- 0
b <- for (i in a) {for (j in 1:i) {if (i %% j !=0) {d <- c(d,i)}}}

11 个答案:

答案 0 :(得分:32)

George Dontas发布的筛子是一个很好的起点。这是一个更快的版本,运行时间为1个6个素数,为0.095秒,而原始版本为30秒。

sieve <- function(n)
{
   n <- as.integer(n)
   if(n > 1e8) stop("n too large")
   primes <- rep(TRUE, n)
   primes[1] <- FALSE
   last.prime <- 2L
   fsqr <- floor(sqrt(n))
   while (last.prime <= fsqr)
   {
      primes[seq.int(2L*last.prime, n, last.prime)] <- FALSE
      sel <- which(primes[(last.prime+1):(fsqr+1)])
      if(any(sel)){
        last.prime <- last.prime + min(sel)
      }else last.prime <- fsqr+1
   }
   which(primes)
}

下面是一些替代算法,这些算法在R中尽可能快地编码。它们比筛子慢,但是比提问者原帖更快。

这是一个使用mod但是矢量化的递归函数。它几乎瞬间返回1e5,在2s内返回1e6。

primes <- function(n){
    primesR <- function(p, i = 1){
        f <- p %% p[i] == 0 & p != p[i]
        if (any(f)){
            p <- primesR(p[!f], i+1)
        }
        p
    }
    primesR(2:n)
}

下一个不是递归的,而是再次更快。在我的机器上,下面的代码在大约1.5秒内准备好最多1e6。

primest <- function(n){
    p <- 2:n
    i <- 1
    while (p[i] <= sqrt(n)) {
        p <-  p[p %% p[i] != 0 | p==p[i]]
        i <- i+1
    }
    p
}
顺便说一下,spuRs软件包有许多主要的查找功能,包括一个E筛子。没有检查过它们的速度是什么样的。

虽然我正在写一个很长的答案......如果一个值是素数,这里就是你如何检查R.

isPrime <- function(x){
    div <- 2:ceiling(sqrt(x))
    !any(x %% div == 0)
}

答案 1 :(得分:24)

这是R。

Sieve of Eratosthenes算法的实现
sieve <- function(n)
{
   n <- as.integer(n)
   if(n > 1e6) stop("n too large")
   primes <- rep(TRUE, n)
   primes[1] <- FALSE
   last.prime <- 2L
   for(i in last.prime:floor(sqrt(n)))
   {
      primes[seq.int(2L*last.prime, n, last.prime)] <- FALSE
      last.prime <- last.prime + min(which(primes[(last.prime+1):n]))
   }
   which(primes)
}

 sieve(1000000)

答案 2 :(得分:6)

我知道生成所有素数的最佳方法(没有进入疯狂的数学运算)是使用Sieve of Eratosthenes

实现非常简单,允许您在不使用除法或模数的情况下计算素数。唯一的缺点是内存密集,但可以进行各种优化以改善内存(例如忽略所有偶数)。

答案 3 :(得分:4)

这种方法应该更快更简单。

allPrime <- function(n) {
  primes <- rep(TRUE, n)
  primes[1] <- FALSE
  for (i in 1:sqrt(n)) {
    if (primes[i]) primes[seq(i^2, n, i)] <- FALSE
  }
  which(primes)
}

我的计算机上{0.1}秒n = 1e6

我在包primefactr。

中的函数AllPrimesUpTo中实现了它

答案 4 :(得分:4)

OP要求产生低于10亿的所有素数。到目前为止提供的所有答案要么无法执行此操作,要么需要很长时间才能执行,或者目前在R中不可用(请参阅@ answer来自@Charles)。包RcppAlgos(我是作者)能够在1 second之后生成请求的输出。它基于Kim Walisch的Eratosthenes分段筛。

library(RcppAlgos)
system.time(primeSieve(10^9))
  user  system elapsed 
 1.300   0.105   1.406

此外,下面是可以生成素数的包(以及@John提供的sieve函数)的摘要。

library(schoolmath)
library(primefactr)
library(sfsmisc)
library(primes)
library(numbers)
library(spuRs)
library(randtoolbox)
library(matlab)
## and 'sieve' from @John

在开始之前,我们注意到@Henrik在schoolmath中指出的问题仍然存在。观察:

## 1 is NOT a prime number
schoolmath::primes(start = 1, end = 20)
[1]  1  2  3  5  7 11 13 17 19   

## This should return 1, however it is saying that 52 "prime"
## numbers less than 10^4 are divisible by 7.... Huuuhhh????
sum(schoolmath::primes(start = 1, end = 10^4) %% 7L == 0)
[1] 52

关键是,此时不要使用schoolmath来生成素数(对作者没有冒犯......实际上,我已经向维护者提出了问题)。

让我们看看randtoolbox,因为它看起来非常有效。观察:

## the argument for get.primes is for how many prime numbers you need
## whereas most packages get all primes less than a certain number
microbenchmark(priRandtoolbox = get.primes(78498),
              priRcppAlgos = RcppAlgos::primeSieve(10^6), unit = "relative")
Unit: relative
          expr      min       lq     mean   median       uq      max neval
priRandtoolbox  1.00000  1.00000  1.00000  1.00000 1.000000 1.000000   100
  priRcppAlgos 19.37208 18.30095 9.897374 7.639231 7.249031 5.449076   100

仔细观察发现它本质上是一个查找表(在源代码的文件randtoolbox.c中找到)。

#include "primes.h"

void reconstruct_primes()
{
    int i;
    if (primeNumber[2] == 1)
        for (i = 2; i < 100000; i++)
            primeNumber[i] = primeNumber[i-1] + 2*primeNumber[i];
}

其中primes.h是一个头文件,其中包含&#34;数字与素数之差的一半&#34; 。因此,您将受到该阵列中用于生成素数(即前十万个素数)的元素数量的限制。如果您只使用较小的素数(小于1,299,709(即第100,000个素数))并且您正在处理需要nth素数的项目,randtoolbox是通往去。

现在,让我们看看其他软件包在生成不到一百万的质数时如何比较:

microbenchmark(priRcppAlgos = RcppAlgos::primeSieve(10^6),
               priNumbers = numbers::Primes(10^6),
               priSpuRs = spuRs::primesieve(c(), 2:10^6),
               priPrimes = primes::generate_primes(1, 10^6),
               priPrimefactr = primefactr::AllPrimesUpTo(10^6),
               priSfsmisc = sfsmisc::primes(10^6),
               priMatlab = matlab::primes(10^6),
               priJohnSieve = sieve(10^6),
               unit = "relative")
Unit: relative
         expr        min        lq      mean     median        uq       max neval
 priRcppAlgos   1.000000   1.00000   1.00000   1.000000   1.00000  1.000000   100
   priNumbers  21.599399  21.27664  20.17172  20.348373  20.82308 10.992780   100
     priSpuRs 223.240897 231.69938 198.16347 215.520998 202.37479 59.608137   100
    priPrimes  41.689864  38.43470  33.74977  35.329320  33.21983 17.323242   100
priPrimefactr  39.452661  39.77808  38.64081  37.887232  36.71392 14.549090   100
   priSfsmisc   9.667778  11.16356  11.58725  10.862231  11.61987  8.990612   100
    priMatlab  21.055741  21.45761  21.46455  21.053058  20.86727 15.029687   100
 priJohnSieve   9.316246  10.22913  10.52325   9.825776  10.30900  8.167797   100    

让我们测试在一定范围内生成素数的速度:

microbenchmark(priRcppAlgos = RcppAlgos::primeSieve(10^9, 10^9 + 10^6),
               priNumbers = numbers::Primes(10^9, 10^9 + 10^6),
               priPrimes = primes::generate_primes(10^9, 10^9 + 10^6),
               unit = "relative", times = 20)
Unit: relative
         expr      min       lq     mean   median       uq       max neval
 priRcppAlgos   1.0000   1.0000   1.0000   1.0000   1.0000   1.00000    20
   priNumbers 137.5877 134.2035 125.1085 133.1225 121.9784  94.93077    20
    priPrimes 911.2896 877.6692 806.9694 861.4054 783.9666 568.05759    20    20

现在,让我们删除if(n > 1e8) stop("n too large")函数中的条件sieve,以测试它可以多快地生成十亿以下的素数以及primes函数。 sfsmisc RcppAlgos包裹,因为它们是system.time(sieve(10^9)) user system elapsed 26.703 4.582 31.328 system.time(sfsmisc::primes(10^9)) user system elapsed 24.772 4.521 29.436 之后最快的。

RcppAlgos

从这一点开始,我们看到1.406随着 n 变大(20-22x(见上文)大约10x更快而得分更好。只有10^6 ## primes less than 10 billion system.time(tenBillion <- RcppAlgos::primeSieve(10^10)) user system elapsed 16.510 1.784 18.296 length(tenBillion) [1] 455052511 ## Warning!!!... Large object created tenBillionSize <- object.size(tenBillion) print(tenBillionSize, units = "Gb") 3.4 Gb 左右。

只是为了好的衡量标准:

RcppAlgos::primeSieve

带走

  1. 有许多可用于生成素数的优秀套餐
  2. 如果您一直在寻找速度,则与randtoolbox::get.primes无法匹配。
  3. 如果您正在使用小素数,请查看numbers
  4. 如果您需要某个范围内的素数,那么包primesRcppAlgos和&amp; NA是要走的路。
  5. 不能过分强调良好编程实践的重要性(例如,矢量化,使用正确的数据类型等)。 @John提供的纯碱R解决方案最恰当地证明了这一点。它简洁,清晰,高效。

答案 5 :(得分:3)

我推荐primegen,Dan Bernstein实施Atkin-Bernstein筛子。它非常快,并且可以很好地适应其他问题。你需要将数据传递给程序才能使用它,但我想有办法做到这一点吗?

答案 6 :(得分:1)

您还可以欺骗并使用primes()包中的schoolmath功能:D

答案 7 :(得分:1)

没有建议,但请允许我提供各种各样的扩展评论。我使用以下代码运行了该实验:

get_primes <- function(n_min, n_max){
  options(scipen=999)
    result = vector()
      for (x in seq(max(n_min,2), n_max)){
        has_factor <- F
        for (p in seq(2, ceiling(sqrt(x)))){
          if(x %% p == 0) has_factor <- T
          if(has_factor == T) break
          }
        if(has_factor==F) result <- c(result,x)
        }
    result
}

在将近24小时不间断的计算机操作之后,我得到了5,245,897质数的列表。 π(1,000,000,000) = 50,847,534,因此将需要10天才能完成此计算。

~ 5个百万质数中的

Here is the file

答案 8 :(得分:0)

上面发布的isPrime()函数可以使用sieve()。只需要检查是否有任何一个 素数&lt; ceiling(sqrt(x))除以x而没有余数。还需要处理1和2。

isPrime <- function(x) {
    div <- sieve(ceiling(sqrt(x)))
    (x > 1) & ((x == 2) | !any(x %% div == 0))
}

答案 9 :(得分:0)

for (i in 2:1000) {
a = (2:(i-1))
b = as.matrix(i%%a)
c = colSums(b != 0)
if (c == i-2)
 {
 print(i)
 }
 }

答案 10 :(得分:0)

检查编号(i-1)

生成的素数列表(n)检查(a)之前的每个数字(i)

感谢您的建议:

prime = function(a,n){
    n=c(2)
    i=3
    while(i <=a){
      for(j in n[n<=sqrt(i)]){
        r=0
        if (i%%j == 0){
          r=1}
        if(r==1){break}


      }
      if(r!=1){n = c(n,i)}
      i=i+2
    }
    print(n)
  }