web-dev-qa-db-ja.com

特定の数までの素数のリストを生成します

10億未満の素数のリストを生成しようとしています。私はこれを試していますが、この種の構造はかなりくだらないです。助言がありますか?

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

これは、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)
24
George Dontas

ジョージドンタスによって投稿されたそのふるいは良い出発点です。これは、元のバージョンの30秒とは対照的に、0.095秒の1e6素数の実行時間を持つはるかに高速なバージョンです。

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に戻り、2秒未満で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のふるいを含む、いくつかの主要な検索機能があります。それらの速度がどのようなものかを確認していません。

そして、私が非常に長い答えを書いている間...これが1つの値が素数であるかどうかRをチェックインする方法です。

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

(狂った数学に入ることなく)すべての素数を生成するために私が知っている最良の方法は、 エラトステネスのふるい を使用することです。

実装は非常に簡単で、除算やモジュラスを使用せずに素数を計算できます。唯一の欠点は、メモリを大量に消費することですが、メモリを改善するためにさまざまな最適化を行うことができます(たとえば、すべての偶数を無視します)。

6
riwalk

Rの素数

OPは、10億未満のすべての素数を生成するように要求しました。これまでに提供されたすべての回答は、これを実行できないか、実行に時間がかかるか、現在Rでは利用できません(@Charlesによる answer を参照)。パッケージRcppAlgos(私は作成者です)は、1つのスレッドのみを使用して_1 second_のすぐ上で要求された出力を生成することができます。これは、エラトステネスのセグメント化されたふるいに基づいています Kim Walisch

RcppAlgos

_library(RcppAlgos)
system.time(primeSieve(1e9))  ## using 1 thread
  user  system elapsed 
 1.099   0.077   1.176 
_

複数のスレッドを使用する

また、最近のバージョン(つまり、_>= 2.3.0_)では、複数のスレッドを利用してさらに高速に生成できます。たとえば、今では0.5秒以内に最大10億の素数を生成できます!

_system.time(primeSieve(10^9, nThreads = 8))
  user  system elapsed 
 2.046   0.048   0.375
_

素数を生成するためのRで利用可能なパッケージの概要

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

始める前に、schoolmathで@Henrikが指摘した問題がまだ存在していることに注意してください。観察する:

_## 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!!
sum(schoolmath::primes(start = 1, end = 10^4) %% 7L == 0)
[1] 52
_

重要なのは、この時点で素数を生成するためにschoolmathを使用しないことです(作成者に不快感を与えることはありません。

randtoolboxは非常に効率的であるように見えるので、見てみましょう。観察する:

_library(microbenchmark)
## 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.000000 1.000000 1.000000 1.0000000   100
  priRcppAlgos 12.79832 12.55065 6.493295 7.355044 7.363331 0.3490306   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_は、 "素数間の差の半分"の配列を含むヘッダーファイルです。したがって、素数(つまり、最初の10万個の素数)を生成するためのその配列内の要素の数によって制限されます。小さい素数(_1,299,709_未満(つまり、100,000番目の素数))のみを使用していて、nth素数を必要とするプロジェクトで作業している場合、randtoolboxは行く方法。

以下では、残りのパッケージでベンチマークを実行します。

最大100万のプライム

_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.00000   100
    priNumbers  21.550402  23.19917  26.67230  23.140031  24.56783  53.58169   100
      priSpuRs 232.682764 223.35847 233.65760 235.924538 236.09220 212.17140   100
     priPrimes  46.591868  43.64566  40.72524  39.106107  39.60530  36.47959   100
 priPrimefactr  39.609560  40.58511  42.64926  37.835497  38.89907  65.00466   100
    priSfsmisc   9.271614  10.68997  12.38100   9.761438  11.97680  38.12275   100
     priMatlab  21.756936  24.39900  27.08800  23.433433  24.85569  49.80532   100
  priJohnSieve  10.630835  11.46217  12.55619  10.792553  13.30264  38.99460   100
_

最大1,000万のプライム

_microbenchmark(priRcppAlgos = RcppAlgos::primeSieve(10^7),
               priNumbers = numbers::Primes(10^7),
               priSpuRs = spuRs::primesieve(c(), 2:10^7),
               priPrimes = primes::generate_primes(1, 10^7),
               priPrimefactr = primefactr::AllPrimesUpTo(10^7),
               priSfsmisc = sfsmisc::primes(10^7),
               priMatlab = matlab::primes(10^7),
               priJohnSieve = sieve(10^7),
               unit = "relative", times = 20)
Unit: relative
          expr       min        lq      mean    median        uq       max neval
  priRcppAlgos   1.00000   1.00000   1.00000   1.00000   1.00000   1.00000    20
    priNumbers  30.57896  28.91780  31.26486  30.47751  29.81762  40.43611    20
      priSpuRs 533.99400 497.20484 490.39989 494.89262 473.16314 470.87654    20
     priPrimes 125.04440 114.71349 112.30075 113.54464 107.92360 103.74659    20
 priPrimefactr  52.03477  50.32676  52.28153  51.72503  52.32880  59.55558    20
    priSfsmisc  16.89114  16.44673  17.48093  16.64139  18.07987  22.88660    20
     priMatlab  30.13476  28.30881  31.70260  30.73251  32.92625  41.21350    20
  priJohnSieve  18.25245  17.95183  19.08338  17.92877  18.35414  32.57675    20
_

最大1億のプライム

次の2つのベンチマークでは、RcppAlgosnumberssfsmiscmatlab、および@Johnによるsieve関数のみを考慮します。

_microbenchmark(priRcppAlgos = RcppAlgos::primeSieve(10^8),
               priNumbers = numbers::Primes(10^8),
               priSfsmisc = sfsmisc::primes(10^8),
               priMatlab = matlab::primes(10^8),
               priJohnSieve = sieve(10^8),
               unit = "relative", times = 20)
Unit: relative
         expr      min       lq     mean   median       uq      max neval
 priRcppAlgos  1.00000  1.00000  1.00000  1.00000  1.00000  1.00000    20
   priNumbers 35.64097 33.75777 32.83526 32.25151 31.74193 31.95457    20
   priSfsmisc 21.68673 20.47128 20.01984 19.65887 19.43016 19.51961    20
    priMatlab 35.34738 33.55789 32.67803 32.21343 31.56551 31.65399    20
 priJohnSieve 23.28720 22.19674 21.64982 21.27136 20.95323 21.31737    20
_

最大10億のプライム

N.B. sieve関数の条件if(n > 1e8) stop("n too large")を削除する必要があります。

_## See top section
## system.time(primeSieve(10^9))
##  user  system elapsed 
## 1.099   0.077   1.176      ## RcppAlgos single-threaded

## gc()
system.time(matlab::primes(10^9))
   user  system elapsed 
 31.780  12.456  45.549        ## ~39x slower than RcppAlgos

## gc()
system.time(numbers::Primes(10^9))
   user  system elapsed 
 32.252   9.257  41.441        ## ~35x slower than RcppAlgos

## gc()
system.time(sieve(10^9))
  user  system elapsed 
26.266   3.906  30.201         ## ~26x slower than RcppAlgos

## gc()
system.time(sfsmisc::primes(10^9))
  user  system elapsed 
24.292   3.389  27.710         ## ~24x slower than RcppAlgos
_

これらの比較から、nが大きくなるにつれて、RcppAlgosのスケーリングが大幅に向上することがわかります。

_ _________________________________________________________
|            |   1e6   |   1e7    |   1e8     |    1e9    |
|            |---------|----------|-----------|-----------
| RcppAlgos  |   1.00  |   1.00   |    1.00   |    1.00   |
|   sfsmisc  |   9.76  |  16.64   |   19.66   |   23.56   |
| JohnSieve  |  10.79  |  17.93   |   21.27   |   25.68   |
|   numbers  |  23.14  |  30.48   |   32.25   |   34.86   |
|    matlab  |  23.43  |  30.73   |   32.21   |   38.73   |
 ---------------------------------------------------------
_

複数のスレッドを使用すると、違いはさらに劇的になります。

_microbenchmark(ser = primeSieve(1e6),
               par = primeSieve(1e6, nThreads = 8), unit = "relative")
Unit: relative
expr      min       lq     mean   median       uq      max neval
 ser 1.741342 1.492707 1.481546 1.512804 1.432601 1.275733   100
 par 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000   100

microbenchmark(ser = primeSieve(1e7),
               par = primeSieve(1e7, nThreads = 8), unit = "relative")
Unit: relative
 expr      min      lq     mean   median       uq      max neval
  ser 2.632054 2.50671 2.405262 2.418097 2.306008 2.246153   100
  par 1.000000 1.00000 1.000000 1.000000 1.000000 1.000000   100

microbenchmark(ser = primeSieve(1e8),
               par = primeSieve(1e8, nThreads = 8), unit = "relative", times = 20)
Unit: relative
 expr      min       lq     mean   median       uq      max neval
  ser 2.914836 2.850347 2.761313 2.709214 2.755683 2.438048    20
  par 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000    20

microbenchmark(ser = primeSieve(1e9),
               par = primeSieve(1e9, nThreads = 8), unit = "relative", times = 10)
Unit: relative
 expr      min       lq     mean   median       uq      max neval
  ser 3.081841 2.999521 2.980076 2.987556 2.961563 2.841023    10
  par 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000    10
_

そして、上記の表に、シリアル結果のそれぞれの中央値を掛けます。

_ _____________________________________________________________
|                |   1e6   |   1e7    |   1e8     |    1e9    |
|                |---------|----------|-----------|-----------
| RcppAlgos-Par  |   1.00  |   1.00   |    1.00   |    1.00   |
| RcppAlgos-Ser  |   1.51  |   2.42   |    2.71   |    2.99   |
|     sfsmisc    |  14.76  |  40.24   |   53.26   |   70.39   |
|   JohnSieve    |  16.32  |  43.36   |   57.62   |   76.72   |
|     numbers    |  35.01  |  73.70   |   87.37   |  104.15   |
|      matlab    |  35.44  |  74.31   |   87.26   |  115.71   |
 -------------------------------------------------------------
_

範囲を超える素数

_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.000   1.0000   1.0000   1.0000    20
   priNumbers 115.3000 112.1195 106.295 110.3327 104.9106  81.6943    20
    priPrimes 983.7902 948.4493 890.243 919.4345 867.5775 708.9603    20
_

6秒未満で最大100億をプライム

_##  primes less than 10 billion
system.time(tenBillion <- RcppAlgos::primeSieve(10^10, nThreads = 8))
  user  system elapsed 
26.077   2.063   5.602

length(tenBillion)
[1] 455052511

## Warning!!!... Large object created
tenBillionSize <- object.size(tenBillion)
print(tenBillionSize, units = "Gb")
3.4 Gb
_

非常に大きな数の範囲にわたる素数:

バージョン_2.3.0_より前は、すべての大きさの数値に同じアルゴリズムを使用していました。これは、ほとんどのふるい素数が各セグメントに少なくとも1つの倍数を持っている場合、小さい数でも問題ありません(通常、セグメントサイズは_L1 Cache ~32KiB_のサイズによって制限されます)。ただし、より大きな数を扱う場合、ふるい素数には、セグメントごとに1つ未満の倍数を持つ多くの数が含まれます。キャッシュを汚染する多くの価値のないチェックを実行しているため、この状況では多くのオーバーヘッドが発生します。したがって、数が非常に多い場合、素数の生成がはるかに遅くなります。バージョン_2.2.0_を観察します( 古いバージョンのRパッケージのインストール を参照):

_## Install version 2.2.0
## packageurl <- "http://cran.r-project.org/src/contrib/Archive/RcppAlgos/RcppAlgos_2.2.0.tar.gz"
## install.packages(packageurl, repos=NULL, type="source")

system.time(old <- RcppAlgos::primeSieve(1e15, 1e15 + 1e9))
 user  system elapsed 
7.932   0.134   8.067
_

そして、元々 TomásOliveira によって開発されたキャッシュフレンドリーな改善を使用すると、劇的な改善が見られます。

_## Reinstall current version from CRAN
## install.packages("RcppAlgos"); library(RcppAlgos)
system.time(cacheFriendly <- primeSieve(1e15, 1e15 + 1e9))
 user  system elapsed 
2.258   0.166   2.424   ## Over 3x faster than older versions

system.time(primeSieve(1e15, 1e15 + 1e9, nThreads = 8))
 user  system elapsed 
4.852   0.780   0.911   ##  Over 8x faster using multiple threads
_

取り除く

  1. 素数を生成するために利用できる多くの素晴らしいパッケージがあります
  2. 一般的に速度を求めている場合、特に大きな数値の場合、_RcppAlgos::primeSieve_に一致するものはありません。
  3. 小さな素数で作業している場合は、_randtoolbox::get.primes_以上を探す必要はありません。
  4. 範囲内の素数が必要な場合は、パッケージnumbersprimes、およびRcppAlgosが最適です。
  5. 優れたプログラミング手法の重要性は強調しすぎることはありません(たとえば、ベクトル化、正しいデータ型の使用など)。これは、@ Johnが提供する純粋なベースRソリューションによって最も適切に示されます。それは簡潔で、明確で、非常に効率的です。
5
Joseph Wood

この方法は、より速く、より簡単でなければなりません。

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.12秒n = 1e6

これをパッケージprimefactrの関数AllPrimesUpToに実装しました。

4
F. Privé

primegen 、DanBernsteinによるAtkin-Bernsteinふるいの実装をお勧めします。それは非常に高速で、他の問題にうまく対応できます。それを使用するには、プログラムにデータを渡す必要がありますが、それを行う方法はあると思いますか?

3
Charles

schoolmathパッケージのprimes()関数をごまかして使用することもできます:D

1
nico

上記のisPrime()関数はsieve()を使用できます。素数<ceiling(sqrt(x))のいずれかがxを余りなしで除算するかどうかを確認するだけで済みます。 1と2も処理する必要があります。

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

(a)の前のすべての数(i)は、数(i-1)をチェックすることによって生成された素数(n)のリストと照合されます。

提案をありがとう:

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)
  }
0
Gaurav_Dakliya
for (i in 2:1000) {
a = (2:(i-1))
b = as.matrix(i%%a)
c = colSums(b != 0)
if (c == i-2)
 {
 print(i)
 }
 }
0
Hadi Atashi