fortran66のブログ

fortran について書きます。

エラトステネスの篩 ふたたびw

素数をn個もとめる。ガウス素数定理に基づいて必要なエラトステネスの篩の大きさを推測する。求めたおおむねn個の素数の配列から最初のn個を取りだす。

オプションとして /assume:realloc_lhs が必要。求める素数の数が大きい場合にはオプション /heap-arrays も必要となる。

実行結果

500までの素数の表

チェック
http://en.wikipedia.org/wiki/List_of_prime_numbers#The_first_500_prime_numbers

一応10**7の値でチェック おk
http://primes.utm.edu/lists/small/millions/

原始プログラム

module m_prime
    implicit none
    integer, allocatable, save :: prm(:)
  contains
    subroutine init_prime(np)  
      integer, intent(in) :: np
      integer :: n
      ! np ~ n / log(n) ! approximate number of primes less than n : Gauss   
      ! approximate np-th prime n ~ np * log(n) ~ np * log( np * log(n) ) ...  
      n = np * log( np * log( real(np) ) ) + 3 
      prm = take(np, eratos(n))
      return
    contains
      function take(n, arr) result(res) 
        integer, intent(in) :: n, arr(:)
        integer, allocatable :: res(:)
        res = arr(1:n)
        return
      end function take
    end subroutine init_prime
  
    function eratos(n) result(res)
      integer, intent(in) :: n
      integer, allocatable :: res(:)
      logical :: table(n)
      integer :: i, j
      table = .true.
      table(1) = .false.
      do i = 2, sqrt( real(n) ) ! sieve of Eratosthenes
        if (table(i)) forall(j = 2 * i:n:i) table(j) = .false.
      end do
      res = pack([(i, i = 1, n)], table) ! filter [1,2,3..] by table:[F,T,T..]
      return
    end function eratos
    
  end module m_prime
  
  program main
    use m_prime
    implicit none
  ! Body of eratos
    call init_prime(500)
    print '(20i5)', prm
    stop
  end program main

2011-11-4 if (table(i)) then ... end if を追加。
2011-11-6 if (table(i)) forall(j = 2 * i:n:i) table(j) = .false. に簡略化