Phil Trelford's Array
POKE 36879, 255

C++ vs C# vs F# vs Haskell

November 29, 2009 14:36 by Phil

Since I first posted an F# solution to Left-Truncatable Primes, C# and Haskell have entered the frame, and although this is not a good problem for a serious comparison of languages, I think it is still interesting.

So lets start at the beginning, Dominic Penfold initially posed the problem and gave an efficient algorithm implemented in C++ (39 lines):

bool IsPrime(int num)
{
    int sqrt = 1 + sqrtf(num);
    for (int i=2; i<sqrt; i++)
    {
        if (num % i == 0)
            return false;
    }
    return true;
}
 
int NthLeftTruncatablePrime(int target)
{
    std::vector<int> PrimeRoots;
    PrimeRoots.push_back(2);
    PrimeRoots.push_back(3);
    PrimeRoots.push_back(5);
    PrimeRoots.push_back(7);
    int start = 0;
    int tens = 10;    
    while(true)
    {
        int end = PrimeRoots.size();
        for (int i=1; i<10; i++)
        {
            for (int pos = start; pos< end; pos++)
            {
                int newprime = tens * i + PrimeRoots[pos];
                if (IsPrime(newprime))
                {
                    PrimeRoots.push_back(newprime);
                    if (PrimeRoots.size()==target)                                      
                        return newprime;                    
                }
            }
        }
        start = end;
        tens *= 10;
    }
}

 

Performance was considerably faster when 32-bit integers were switched for 64-bit integers.

The C++ code is very easily converted into C# (35 lines):

static bool IsPrime(int num)
{
    int sqrt = 1 + (int ) System.Math.Sqrt(num);
    blue">for (int i = 2; i < sqrt; i++)
    {
        if (num % i == 0)
            return false;
    }
    return true;
}

static int NthLeftTruncatedPrime(int target)
{
    var primeRoots = new List<int>() { 2, 3, 5, 7};               
    int start = 0;
    int tens = 10;
    while (true)
    {
        int end = primeRoots.Count;
        for (int i = 1; i < 10; i++)
        {
            for (int pos = start; pos < end; pos++)
            {
                int newprime = tens * i + primeRoots[pos];
                if (IsPrime(newprime))
                {
                    primeRoots.Add(newprime);
                    if (primeRoots.Count == target)
                        return newprime;
                }
            }
        }
        start = end;
        tens *= 10;
    }
}

 

Conversion to F# is only slightly trickier as there is no equivalent return statement, but similar behavior can be accomplished using yield (30 lines):

let IsPrime n =
    if n = 1 then false
    else    
        let max = n |> float |> sqrt |> int
        let rec Test = function
            | x when x > max -> true
            | x -> if (n % x) = 0 then false else Test (x+1)
        Test 2

let NthLeftTruncatablePrime n =
    let oneDigitPrimes = [2;3;5;7]          
    seq {            
        let primes = ref oneDigitPrimes        
        for tens = 1 to 8 do
            let acc = ref []
            for i=1 to 9 do 
                let newPrime = i * pown 10 tens                
                let primes' = ref !primes
                while (!primes').Length > 0 do                            
                    let x = newPrime+(!primes').Head
                    if IsPrime x then 
                        acc := x :: !acc
                        yield x
                    primes' := (!primes').Tail                    
                done                                     
            done         
            primes := !acc |> List.rev    
        done            
    }
    |> Seq.append oneDigitPrimes
    |> Seq.nth (n-1)

 

The extra overhead of generating a sequence can be avoided using instead recursion (25 lines including IsPrime function):

let NthLeftTruncatablePrime index =
    let rec Find digit factor primes primes' count acc =
        match digit, primes' with
        | 10, _ -> 
            let primes = List.rev acc
            Find 1 (10*factor) primes primes count []
        | _, [] ->
            Find (digit+1) factor primes primes count acc
        | _, prime::tail -> 
            let k = (digit * factor) + prime
            let count, acc =
                if IsPrime k then count+1, k::acc else count, acc 
            if count = index then k
            else Find digit factor primes tail count acc
    let primes = [2;3;5;7]
    if index <= 4 then List.nth primes (index-1)
    else Find 1 10 primes primes 4 []

 

Later Matt Curran (coincidentally another ex-games developer) got in on the act with a Haskell version (17 lines including a comment – although the lines are a little dense):

isPrime :: Int -> Bool
isPrime 1 = False
isPrime x = null $ take 1 [n | n <- [2..upperBound x], 0 == mod x n]
    where
        upperBound = floor . sqrt . fromIntegral

-- list of all left truncatable primes
ltps :: [Int]
ltps = ps 1 [0]
    where
        ps _ []      = []
        ps m prevMag = thisMag ++ (ps (m*10) thisMag)
            where
                thisMag = primes [x*m | x <- [1..9]] prevMag
                    where
                        primes [] _ = []
                        primes (m:mgs) bases = 
                            (filter isPrime [m+x | x <- bases]) ++ (primes mgs bases)

 

Results Breakdown by language

Language Lines of code Performance(ms)
C++ 39 5.213
C# 35 5.332
F# sequence 30 14.075
F# recursive 25 5.412
Haskell 17 ?

Note: the time is to generate the 1000th prime, and the same computer was used for all tests, as was release mode. Haskell time coming soon.

LeftTruncatablePrime.cpp (1.45 kb)

LeftTruncatablePrime.cs (1.58 kb)

LeftTruncatablePrime_seq.fs (1.53 kb)

LeftTruncatablePrime_rec.fs (1.36 kb)


Tags:
Categories: .Net | F# | C# | C++ | Haskell
Actions: E-mail | Permalink | Comments (14) | Comment RSSRSS comment feed

Comments

November 30. 2009 04:57

pingback

Pingback from alvinashcraft.com

Dew Drop – November 30, 2009 | Alvin Ashcraft's Morning Dew

alvinashcraft.com

November 30. 2009 08:29

pete w

Very interesting indeed! I've been working in C++/C#/F#, and these comparisons are spot on with my experiences as well.
I am particularly surprised by the C#/C++ performance numbers.

pete w

November 30. 2009 09:46

pingback

Pingback from topsy.com

Twitter Trackbacks for
        
        C++ vs C# vs F# vs Haskell
        [trelford.com]
        on Topsy.com

topsy.com

November 30. 2009 10:00

Frocka

What is the Haskell time?

Frocka

November 30. 2009 21:15

Bryan O'Sullivan

Better Haskell code: hpaste.org/fastcgi/hpaste.fcgi/view?id=13424

Bryan O'Sullivan

November 30. 2009 22:10

pingback

Pingback from serpentine.com

teideal glic deisbhéalach  » Blog Archive   » Dense? Dense, you say?

serpentine.com

December 1. 2009 00:59

Nigel Findlater

Hallo,

We liked your post.

We have just been looking into using intel compilers because they optimize the machine code to use features of the Intel procesor. We measured a 100 times improvment of speed over the MS C++ compiler. The code had a lot of floating point operations that the HPO compiler could easily vectorize, so I am not sure that you will get the same kind of speed up. But it's worth a go.

have fun...

Nigel Findlater

December 1. 2009 07:51

Frocka

HELLO SIR WE ARE PATIENTLY WAITING FOR HASKELL TIME

Frocka

December 1. 2009 20:24

Don Stewart

@Frocka Please be to read :

www.serpentine.com/.../

Don Stewart

December 2. 2009 05:07

Wamiduku

I'm surprised to see that C++ and C# have the same execution time. Is that C++ compiled into native machine code, or is it managed .NET C++?

Wamiduku

December 3. 2009 01:10

Phil

Thanks everybody for all the comments.

Haskell timing: Unfortunately Matt is away this week, so I'm afraid a reliable time for Haskell won't be till next week, but don't expect it to be any faster than the other languages.

C++ timing: the C++ was compile into native code, i.e. unmanaged. Remember that the C# and F# code is Just In Time (JIT) compiled to native code before execution.

Phil

December 6. 2009 15:15

trackback

F# Discoveries This Week 12/06/2009

We have a great selection of links this week with topics including discriminated unions, equality and

Rick Minerich's Development Wonderland

February 1. 2010 15:46

Eddie Freddie

> HELLO SIR WE ARE PATIENTLY WAITING FOR HASKELL TIME

Be patient, it's still running..

Eddie Freddie

May 19. 2011 03:25

pingback

Pingback from originalbloog.com

WTS: 682 lists of PR 1-6 do follow & auto approved blogpost+free 2782 PR N/A only $20 | The Webmaster Marketplace | Blog

originalbloog.com

Add comment


(Will show your Gravatar icon)

  Country flag

biuquote
  • Comment
  • Preview
Loading