How to do modulo multiplication for 64‑bit values without using BigInteger in C#

1 Answer

0 votes
using System;

/*
    ================================================================
    Modulo Multiplication (Slow and Fast Versions)
    ================================================================

    Purpose:
        Compute (a * b) % mod safely for large 64‑bit values
        without using BigInteger.

    Versions:
        1. Slow version:
            - Adds b to result a times.
            - Always correct.
            - Very slow for large numbers.
            - Useful as a correctness reference.

        2. Fast version:
            - Uses the classic "double‑and‑add" technique.
            - Runs in O(log b).
            - Avoids overflow by never multiplying large numbers.
            - Produces the same result as the slow version.

    Notes:
        C# long = 64‑bit signed integer.
        All intermediate values are kept safe using modulo.
        The fast version is the recommended one for real use.
*/

class Program
{
    // ---------------------------------------------------------------
    // SLOW VERSION (simple, correct, but extremely slow)
    // ---------------------------------------------------------------
    static long MulModSlow(long a, long b, long mod)
    {
        /*
            Computes:
                (b + b + b + ... a times) % mod

            This avoids overflow because:
                - result stays below mod
                - b fits in long
                - addition is safe

            But it is O(a), which is too slow for large inputs.
        */

        if (b < a) {
            long tmp = a;
            a = b;
            b = tmp;
        }

        long result = 0;

        for (long i = 0; i < a; i++) {
            result = (result + b) % mod;
        }

        return result;
    }


    // ---------------------------------------------------------------
    // FAST VERSION (efficient and safe)
    // ---------------------------------------------------------------
    static long MulModFast(long a, long b, long mod)
    {
        /*
            Uses the "double‑and‑add" method:

                - If the lowest bit of b is set, add a to result.
                - Double a each step.
                - Shift b right each step.

            This avoids overflow because:
                - We never compute a * b directly.
                - Doubling a is safe because we reduce modulo each step.

            This makes the algorithm:
                - Fast
                - Safe
                - Exact
        */

        long result = 0;
        a %= mod;

        while (b > 0)
        {
            if ((b & 1) == 1) {
                result = (result + a) % mod;
            }

            a = (a << 1) % mod;
            b >>= 1;
        }

        return result;
    }


    // ---------------------------------------------------------------
    // MAIN PROGRAM
    // ---------------------------------------------------------------
    static void Main()
    {
        long x   = 798_345L;
        long y   = 20_289_473_612_815L;
        long mod = 100_000_000_000_003L;

        long slowResult = MulModSlow(x, y, mod);
        long fastResult = MulModFast(x, y, mod);

        Console.WriteLine("Slow result: " + slowResult);
        Console.WriteLine("Fast result: " + fastResult);
    }
}


/*
run:

Slow result: 99811422305238
Fast result: 99811422305238

*/

 



answered Aug 16 by avibootz

Related questions

...