// We know a sieve is pretty fast, especially only up to a million. And it can be modified easily // to allow retrieving the prime factors of a given number. So combine that with Euler's product // formula (see https://en.wikipedia.org/wiki/Euler%27s_totient_function#Euler.27s_product_formula). // That's a lot better than my first idea for using a sieve, which was getting the prime factorization // of every number below n and comparing it to n's prime factorization. public static int Solve(int n) { int[] distinctPrimeFactors = _factorizer.GetDistinctPrimeFactors(n).ToArray(); double totient = n; foreach (int primeFactor in distinctPrimeFactors) { totient *= (1 - 1 / (double)primeFactor); } return((int)Math.Round(totient)); }
public void GetDistinctPrimeFactors_AgreesWithNaiveFactorizer() { var sieveFactorizer = new SieveOfEratosthenesFactorizer(3481); var trialDivisionFactorizer = new TrialDivisionFactorizer(3481); for (int n = 0; n <= 3481; ++n) { CollectionAssert.AreEquivalent( NaivePrimeDeciderProviderFactorizer.GetDistinctPrimeFactors(n).ToArray(), sieveFactorizer.GetDistinctPrimeFactors(n).ToArray()); CollectionAssert.AreEquivalent( NaivePrimeDeciderProviderFactorizer.GetDistinctPrimeFactors(n).ToArray(), trialDivisionFactorizer.GetDistinctPrimeFactors(n).ToArray()); } }
public void GetDistinctPrimeFactors_AgreesWithKnownOutput() { var sieveFactorizer = new SieveOfEratosthenesFactorizer(1000); var trialDivisionFactorizer = new TrialDivisionFactorizer(1000); foreach (var numberPrimeFactorsPair in _numberPrimeFactorsPairs) { int number = numberPrimeFactorsPair.Item1; int[] distinctPrimeFactors = numberPrimeFactorsPair.Item2.Distinct().ToArray(); CollectionAssert.AreEquivalent(distinctPrimeFactors, sieveFactorizer.GetDistinctPrimeFactors(number).ToArray()); CollectionAssert.AreEquivalent(distinctPrimeFactors, trialDivisionFactorizer.GetDistinctPrimeFactors(number).ToArray()); CollectionAssert.AreEquivalent(distinctPrimeFactors, NaivePrimeDeciderProviderFactorizer.GetDistinctPrimeFactors(number).ToArray()); } }