Project Euler #60: Prime pair sets

  • + 0 comments

    here is the C# code using System; using System.Collections.Generic; using System.Linq;

    class Program { // Cache for concatenated prime checks. static Dictionary primeCache = new Dictionary();

    static void Main()
    {
        // Read input: N and K separated by a space.
        string[] tokens = Console.ReadLine().Split();
        int N = int.Parse(tokens[0]);
        int K = int.Parse(tokens[1]);
    
        // Generate all primes less than N.
        List<int> primes = SievePrimes(N);
        int nprimes = primes.Count;
    
        // Precompute 10^(number of digits) for each prime.
        int[] pow10 = new int[nprimes];
        for (int i = 0; i < nprimes; i++)
        {
            int digits = primes[i].ToString().Length;
            pow10[i] = (int)Math.Pow(10, digits);
        }
    
        // Build compatibility lists: for each prime (index i), comp[i] holds indices j (with j > i)
        // such that both concatenations (primes[i] followed by primes[j] and vice-versa) are prime.
        List<int>[] comp = new List<int>[nprimes];
        for (int i = 0; i < nprimes; i++)
            comp[i] = new List<int>();
    
        for (int i = 0; i < nprimes; i++)
        {
            for (int j = i + 1; j < nprimes; j++)
            {
                int concat1 = primes[i] * pow10[j] + primes[j];
                int concat2 = primes[j] * pow10[i] + primes[i];
                if (IsConcatPrime(concat1) && IsConcatPrime(concat2))
                    comp[i].Add(j);
            }
        }
    
        // Build HashSet versions for fast candidate intersection.
        HashSet<int>[] compHS = new HashSet<int>[nprimes];
        for (int i = 0; i < nprimes; i++)
            compHS[i] = new HashSet<int>(comp[i]);
    
        List<int> currentSet = new List<int>();
        List<int> sums = new List<int>();
    
        // Try each prime as a starting point.
        for (int i = 0; i < nprimes; i++)
        {
            currentSet.Add(i);
            List<int> candidates = new List<int>(comp[i]);
            Search(comp, compHS, primes, currentSet, candidates, K, sums);
            currentSet.RemoveAt(currentSet.Count - 1);
        }
    
        // Sort and print the resulting sums.
        sums.Sort();
        foreach (var s in sums)
            Console.WriteLine(s);
    }
    
    // Backtracking search for sets of K primes.
    static void Search(List<int>[] comp, HashSet<int>[] compHS, List<int> primes,
                       List<int> currentSet, List<int> candidates, int K, List<int> sums)
    {
        if (currentSet.Count == K)
        {
            int sum = 0;
            foreach (var idx in currentSet)
                sum += primes[idx];
            sums.Add(sum);
            return;
        }
    
        for (int i = 0; i < candidates.Count; i++)
        {
            int candidateIndex = candidates[i];
            List<int> newCandidates = new List<int>();
            // Only consider candidates later in the list to maintain increasing order.
            for (int j = i + 1; j < candidates.Count; j++)
            {
                int nextCandidate = candidates[j];
                if (compHS[candidateIndex].Contains(nextCandidate))
                    newCandidates.Add(nextCandidate);
            }
            currentSet.Add(candidateIndex);
            Search(comp, compHS, primes, currentSet, newCandidates, K, sums);
            currentSet.RemoveAt(currentSet.Count - 1);
        }
    }
    
    // Sieve of Eratosthenes to get all primes less than N.
    static List<int> SievePrimes(int N)
    {
        bool[] isComposite = new bool[N];
        List<int> primes = new List<int>();
        for (int i = 2; i < N; i++)
        {
            if (!isComposite[i])
            {
                primes.Add(i);
                for (int j = i * 2; j < N; j += i)
                    isComposite[j] = true;
            }
        }
        return primes;
    }
    
    // Cached check for concatenated primes.
    static bool IsConcatPrime(int x)
    {
        if (primeCache.ContainsKey(x))
            return primeCache[x];
        bool result = IsPrimeMR(x);
        primeCache[x] = result;
        return result;
    }
    
    // Deterministic Miller-Rabin for 32-bit numbers using bases 2, 7, and 61.
    static bool IsPrimeMR(int n)
    {
        if (n < 2) return false;
        if (n % 2 == 0) return n == 2;
    
        // Write n-1 as d * 2^s.
        int d = n - 1;
        int s = 0;
        while ((d & 1) == 0)
        {
            d /= 2;
            s++;
        }
    
        int[] bases = { 2, 7, 61 };
        foreach (int a in bases)
        {
            if (a % n == 0) continue;
            long x = ModPow(a, d, n);
            if (x == 1 || x == n - 1)
                continue;
            bool composite = true;
            for (int r = 1; r < s; r++)
            {
                x = (x * x) % n;
                if (x == n - 1)
                {
                    composite = false;
                    break;
                }
            }
            if (composite)
                return false;
        }
        return true;
    }
    
    // Fast modular exponentiation.
    static long ModPow(long a, long d, long mod)
    {
        long result = 1;
        a %= mod;
        while (d > 0)
        {
            if ((d & 1) == 1)
                result = (result * a) % mod;
            a = (a * a) % mod;
            d >>= 1;
        }
        return result;
    }
    

    }