/// <summary> /// The Miller primality test uses a witness sub-routine to determine if a value may be prime /// </summary> /// <param name="value">The value to be tested for primality.</param> /// <param name="powersOfTwo">The powers of 2 from the value - 1.</param> /// <param name="factor">The factor from the value - 1.</param> /// <param name="witness">The witness value.</param> /// <returns>False if value is composite. True if value *could* be prime.</returns> /// <remarks>Although this is a translation of the pseudo code from Wikipedia, for prime number 4447483681 witness 2 will exhaust the loop and claim it's a composite.</remarks> private static bool WitnessTest(long value, long powersOfTwo, long factor, long witness) { // First iteration 0 starting power modulo is (witness ^ ((2 ^ 0) * factor)) % value // i.e. just (witness ^ factor) % value var powerModulo = MathAlgorithms.PowerModulo(_base: witness, exponent: factor, modulus: value); // First condition for being identified as a composite is (witness ^ factor) % value != 1 // If that condition is not met, stop right away. if (powerModulo == 1 || powerModulo == value - 1) { return(true); } // value - 1 was decomposed in (2 ^ powersOfTwo) * factor // Iterate powersOfTwo times to compute all (witness ^ ((2 ^ iteration) * factor)) % value - let's call then f(iteration) for (var iteration = 0; iteration < powersOfTwo - 1; iteration++) { // f(iteration + 1) == (f(iteration) ^ 2) % value powerModulo = (powerModulo * powerModulo) % value; if (powerModulo == 1) { return(false); } if (powerModulo == value - 1) { return(true); } } return(false); }
/// <summary> /// Calculates the count of unique ways of making a target sum given set of coins denominations /// </summary> /// <param name="targetSum">The target sum.</param> /// <param name="coins">The coins denominations available.</param> /// <returns>The count of unique ways of making the target sum.</returns> public static int OrderedCombinationsOfCoins(int targetSum, Stack <int> coins) { // When only a single coin denomination remains, check if we can make the target sum with such coins. if (coins.Count == 1) { return((targetSum % coins.Peek()) == 0 ? 1 : 0); } // Compute a hash of the target and coins denominations to check if we've learnt this result yet. var hash = (targetSum.GetHashCode() * 769) ^ coins.ToArray().Aggregate((partialHash, coinToHash) => (partialHash * 769) ^ coinToHash); if (learntCoinCombinations.TryGetValue(hash, out int countOfOrderedCombinations)) { return(countOfOrderedCombinations); } var coin = coins.Pop(); var runningSum = 0; while (runningSum < targetSum) { countOfOrderedCombinations += MathAlgorithms.OrderedCombinationsOfCoins(targetSum - runningSum, coins); runningSum += coin; } if (runningSum == targetSum) { countOfOrderedCombinations++; } coins.Push(coin); learntCoinCombinations.Add(hash, countOfOrderedCombinations); return(countOfOrderedCombinations); }
public static bool TryFindFactor(this long value, int offset, ref long factor) { long pseudoRandomSequence = 2; long doubleSpeedPseudoRandomSequence = 2; factor = 1; do { pseudoRandomSequence = pseudoRandomSequence.NextPseudoRandomValue(offset: offset, modulus: value); doubleSpeedPseudoRandomSequence = doubleSpeedPseudoRandomSequence .NextPseudoRandomValue(offset: offset, modulus: value) .NextPseudoRandomValue(offset: offset, modulus: value); factor = MathAlgorithms.GreatestCommonDivisor(Math.Abs(pseudoRandomSequence - doubleSpeedPseudoRandomSequence), value); }while (factor == 1); return(!(factor == value)); }
/// <summary> /// Gets the greatest common divisor using the binary algorithm. /// </summary> public static long GreatestCommonDivisor(long leftValue, long rightValue) { if (leftValue < 0) { throw new ArgumentOutOfRangeException(nameof(leftValue), leftValue, "Only natural numbers are supported."); } if (rightValue < 0) { throw new ArgumentOutOfRangeException(nameof(rightValue), rightValue, "Only natural numbers are supported."); } if (leftValue == rightValue) { return(leftValue); } // Special case zeroes to ensure loop termination going forward. if (leftValue == 0) { return(rightValue); } if (rightValue == 0) { return(leftValue); } var shiftCount = 0; // While both values are even, keep binary shifting right i.e. divide by 2 as it is a common divisor. // Both values are even if their right most bit is zero. // We store the common factors of 2 so we can apply it back to the GCD found on the remaining values. // In lower level languages than C# this can be optimized with built-in trailing zero count function. for (; ((leftValue | rightValue) & 1) == 0; shiftCount++) { leftValue >>= 1; rightValue >>= 1; } // If one value is still even, then the other one must be odd, as per the exit condition of the previous iteration. // When one is even and the other odd, 2 is not a common divisor, hence the even number can be divided by 2 while the other remains the same. while ((leftValue & 1) == 0) { leftValue >>= 1; } // From here on, leftValue is always odd. // Now that we have an odd value, we are looking for the odd number which is the gcd of the remaining values do { // Remove all factors of 2 in rightValue, as we ensured current leftValue is odd, hence 2 is not a common divisor. while ((rightValue & 1) == 0) { rightValue >>= 1; } // Here both values are odd. // Swap if necessary so leftValue <= rightValue. MathAlgorithms.SwapIfGreater(ref leftValue, ref rightValue); // Setting rightValue to the difference guarantees it is even, while leftValue remains odd. // If the difference is zero, we stop as the remaining greatest common divisor is met (and in left value). // If the difference is not zero, we need to seek the gcd between it and the smallest value (in left value). rightValue = rightValue - leftValue; } while (rightValue != 0); // Re-apply the common factors of 2. return(leftValue << shiftCount); }