Can the execution time of this prime number generator be improved?

后端 未结 5 2166
情书的邮戳
情书的邮戳 2020-12-30 09:22

My initial goal when writing this was to leave the smallest footprint possible. I can say with confidence that this goal has been met. Unfortunately, this leaves me with a r

5条回答
  •  灰色年华
    2020-12-30 10:18

    Just for funsies, let's take a look at this page.

    pi(x) is the prime counting function, it returns the number of primes below x. You can approximate pi(x) using the following inequalities:

    (x/log x)(1 + 0.992/log x) < pi(x) < (x/log x)(1 + 1.2762/log x) 
    // The upper bound holds for all x > 1
    

    p(x) is the nth's prime function, which can be approximated using:

    n ln n + n ln ln n - n < p(n) < n ln n + n ln ln n
    // for n >= 6
    

    With that in mind, here is a very fast generator which computes the first n primes, where each element at index i equal p(i). So, if we want to cap our array at primes below 2,000,000, we use the upperbound inequality for the prime counting function:

    let rec is_prime (primes : int[]) i testPrime maxFactor =
        if primes.[i] > maxFactor then true
        else
            if testPrime % primes.[i] = 0 then false
            else is_prime primes (i + 1) testPrime maxFactor
    
    let rec prime_n (primes : int[]) primeCount testPrime tog =
        if primeCount < primes.Length then
            let primeCount' =
                if is_prime primes 2 testPrime (float testPrime |> sqrt |> int) then
                    primes.[primeCount] <- testPrime
                    primeCount + 1
                else
                    primeCount
            prime_n primes primeCount' (testPrime + tog) (6 - tog)
    
    let getPrimes upTo =
        let x = float upTo
        let arraySize = int <| (x / log x) * (1.0 + 1.2762 / log x)
        let primes = Array.zeroCreate (max arraySize 3)
        primes.[0] <- 2
        primes.[1] <- 3
        primes.[2] <- 5
    
        prime_n primes 3 7 4
        primes
    

    Cool! So how fast is it? On my 3.2ghz quad-core, I get the following in fsi:

    > let primes = getPrimes 2000000;;
    Real: 00:00:00.534, CPU: 00:00:00.546, GC gen0: 0, gen1: 0, gen2: 0
    
    val primes : int [] =
      [|2; 3; 5; 7; 11; 13; 17; 19; 23; 29; 31; 37; 41; 43; 47; 53; 59; 61; 67; 71;
        73; 79; 83; 89; 97; 101; 103; 107; 109; 113; 127; 131; 137; 139; 149; 151;
        157; 163; 167; 173; 179; 181; 191; 193; 197; 199; 211; 223; 227; 229; 233;
        239; 241; 251; 257; 263; 269; 271; 277; 281; 283; 293; 307; 311; 313; 317;
        331; 337; 347; 349; 353; 359; 367; 373; 379; 383; 389; 397; 401; 409; 419;
        421; 431; 433; 439; 443; 449; 457; 461; 463; 467; 479; 487; 491; 499; 503;
        509; 521; 523; 541; ...|]
    
    > printfn "total primes: %i. Last prime: %i" (primes.Length - 1) primes.[primes.Length - 1];;
    total primes: 149973. Last prime: 2014853
    

    So that's all the primes around 2 million in less than half a second :)

提交回复
热议问题