Deprecated: Return type of Dotenv\Repository\AbstractRepository::offsetExists($offset) should either be compatible with ArrayAccess::offsetExists(mixed $offset): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /home/forthrig/public_html/wp-content/plugins/anycomment/vendor/vlucas/phpdotenv/src/Repository/AbstractRepository.php on line 147

Deprecated: Return type of Dotenv\Repository\AbstractRepository::offsetGet($offset) should either be compatible with ArrayAccess::offsetGet(mixed $offset): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /home/forthrig/public_html/wp-content/plugins/anycomment/vendor/vlucas/phpdotenv/src/Repository/AbstractRepository.php on line 155

Deprecated: Return type of Dotenv\Repository\AbstractRepository::offsetSet($offset, $value) should either be compatible with ArrayAccess::offsetSet(mixed $offset, mixed $value): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /home/forthrig/public_html/wp-content/plugins/anycomment/vendor/vlucas/phpdotenv/src/Repository/AbstractRepository.php on line 163

Deprecated: Return type of Dotenv\Repository\AbstractRepository::offsetUnset($offset) should either be compatible with ArrayAccess::offsetUnset(mixed $offset): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /home/forthrig/public_html/wp-content/plugins/anycomment/vendor/vlucas/phpdotenv/src/Repository/AbstractRepository.php on line 171

Deprecated: Return type of PhpOption\Some::getIterator() should either be compatible with IteratorAggregate::getIterator(): Traversable, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /home/forthrig/public_html/wp-content/plugins/anycomment/vendor/phpoption/phpoption/src/PhpOption/Some.php on line 151

Deprecated: Return type of PhpOption\None::getIterator() should either be compatible with IteratorAggregate::getIterator(): Traversable, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /home/forthrig/public_html/wp-content/plugins/anycomment/vendor/phpoption/phpoption/src/PhpOption/None.php on line 118
forthright48 | Page 10 of 17 | learning never ends

Modular Exponentiation

Problem

Given three positive integers $B, P$ and $M$, find the value of $B^P \mod M$.

For example, $B=2$, $P=5$ and $M=7$, then $B^P \mod M = 2^5 \mod 7 = 32 \mod 7 = 4$.

This problem is known as [ref1] Modular Exponentiation. In order to solve this problem, we need to have basic knowledge about [ref2] Modular Arithmetic.

Brute Force – Linear Solution $O(P)$

As usual, we first start off with a naive solution. We can calculate the value of $B^P \mod M$ in $O(P)$ time by running a loop from $1$ to $P$ and multiplying $B$ to result.

int bigmodLinear ( int b, int p, int m ) {
    int res = 1 % m;
    b = b % m;
    for ( int i = 1; i <= p; i++ ) {
        res = ( res * b ) % m;
    }
    return res;
}

A simple solution which will work as long as the value of $P$ is small enough. But for large values of $P$, for example, $P > 10^9$, this code will time out. Also note that in line $2$, I wrote $res = 1 \mod m$. Some people tend to write $res = 1$. This will produce a wrong result when the value of $P$ is $0$ and value of $M$ is $1$.

Divide and Conquer Approach - $O(log_2(P) )$

According to [ref3] Wiki,

A divide and conquer algorithm works by recursively breaking down a problem into two or more sub-problems of the same (or related) type (divide), until these become simple enough to be solved directly (conquer).

That is, instead of solving the big problem $B^P \mod M$, we will solve smaller problems of similar kind and merge them to get the answer to the big problem.

So how do we apply this D&C (Divide and Conquer) idea?

Suppose we are trying to find the value of $B^P$. Then three thing can happen.

  1. Value of P is 0 ( Base Case ): $B^0$ is $1$. This will be the base case of our recursion function. Once we reach this state, we no longer divide the problem into smaller parts.
  2. Value of P is Even: Since $P$ is even, we can say that $B^P = B^{\frac{P}{2}} \times B^{\frac{P}{2}}$. For example, $2^{32} = 2^{16} \times 2^{16}$, $3^6 = 3^3 \times 3^3$. Therefore, instead of calculating the value of $x = B^P$, if we find the value of $y = B^{\frac{P}{2}}$, then we can get the value of $x$ as $x = y \times y$.
  3. Value of P is Odd: In this case we can simply say that $B^P = B \times B^{P-1}$.

Using these three states, we are can formulate a D&C code.

int bigmodRecur ( int b, int p, int m ) {
    if ( p == 0 ) return 1%m; // Base Case

    if ( p % 2 == 0 ) { // p is even
        int y = bigmodRecur ( b, p / 2, m );
        return ( y * y ) % m; // b^p = y * y
    }
    else {
        // b^p = b * b^(p-1)
        return ( b * bigmodRecur ( b, p - 1, m ) ) % m;
    }
}

In line $2$, we have the base case. We return $1 \mod m$ in case value of $m$ is $1$. Next on line $4$ we check if $p$ is even or not. If it is even, we calculate $A^{\frac{P}{2}}$ and return after squaring it. In line $8$, we handle the case when $P$ is odd.

At each step we either divide $P$ by $2$ or subtract $1$ from $P$ and then divide it by $2$. Therefore, there can be at most $2 \times log_2(P)$ steps in D&C approach. Hence complexity is $O(log_2(P) )$.

A Better Solution

A better solution to this problem exists. By using the Repeated Squaring algorithm, we can find the Modular Exponentiation faster than D&C. Repeated Squaring Algorithm has the same complexity as D&C, but it is iterative, thus does not suffer from recursion overhead.

We need to know about bitwise operations before we learn Repeated Squaring algorithm. Since we did not cover this topic yet, I won't write about this approach now. Hopefully, once we cover bitwise operations I will write another post

Resource

  1. Wikipedia - Modular Exponentiation
  2. forthright48 - Introduction to Modular Arithmetic
  3. Wikipedia - Divide and conquer algorithms
  4. forthright48 - Introduction to Number Systems

Leading Digits of Factorial

Problem

Given an integer $N$, find the first $K$ leading digits of $N!$.

For example, for $N=10$ and $K=3$, then first $3$ leading digits of $10! = 3628800$ is $362$.

Finding leading digits uses concepts similar to [ref1] Number of Trailing Zeroes of Factorial.

Brute Force Solution

Finding the value of $N!$ and then printing the first $K$ digits is a simple but slow solution. Using long long we can calculate value $N!$ up to $N \leq 20$ and using Big Integer we can calculate arbitrary $N!$ but with complexity worse than $O(N^2)$.

Solution Using Logarithm

In [ref1], we say that a logarithm of value $x$ is $y$ such that $x = 10^y$. For now let us find out leading digits of a value $x$ instead of $N!$. We will extend it to cover factorials later.

So, we know that $log_{10}(x) = y$, where $y$ will be some fraction. Let us separate $y$ into its integer and decimal part and call them $p,q$. For example, if $y = 123.456$, then $p=123$ and $q=0.456$.

Therefore, we can say that $log_{10}(x) = p + q$. Which means, $x = 10^y = 10^{p+q}=10^p \times 10^q$.

Now expand the values of $10^p$ and $10^q$. If $A=10^p$, then $A$ will simply be a power of $10$ since $p$ is an integer. To be more exact, $A$ will be $1$ with $p$ trailing zeroes. For example, $A=10^3 = 1000$. What about $B=10^q$?

Since $q$ is a fraction which is $0 \leq q < 1$, value of $B$ will be between $10^0 \leq B < 10^1$, that is, $1 \leq B < 10$.

Okay, we got the value of $A$ and $B$, what now? We know that if we multiply $A$ and $B$ we will get $x$. But don’t multiply them just yet. Think for a bit what will happen when we multiply a decimal number with $10$. If it is integer, it will get a trailing zero, e.g, $3 \times 10 = 30$. But if it is a fraction, its decimal point will shift to right, e.g $23.65 \times 10 = 236.5$. Actually, decimal points shifts for integer numbers too, since integer numbers are real numbers with $0$ as fraction, e.g $3 = 3.00$. So in either case multiplying $10$ shifts decimal point to the right.

So what happens if we multiply, $A$, which is just $10^p$ to $B$? Since $A$ has $10$ in it $p$ times, the decimal point of $B$ will shift to right $p$ times. That is all $A$ does to $B$ is change its decimal point. It does not change the digits of $B$ in any way. Thus, $B$ contains all the leading digits of $x$.

For example,
$log_{10}(5420) = 3.7339993 = 3 + 0.7339993$.
$\therefore B = 10^0.7339993 = 5.4200$.

So, if we need first $K$ leading digits of $x$, we just need to multiply $B$ with $10^{K-1}$ and then throw away the fraction part. That is $res = \lfloor B \times 10^{K-1} \rfloor$. Why $10^{K-1}$ not just $10^K$? That’s because we already have $1$ leading digit present in $10^q$ before shifting it.

Extending to Factorial

It is easy to extend the idea above to $N!$. First we need to find out the value of $y=log_{10}(N!)$.

$y= log_{10}(N!)$
$y= log_{10}(N \times (N-1) \times (N-2) \times… \times 3 \times 2 \times 1 )$
$y= log_{10}(N) + log_{10}(N-1) + log_{10}(N-2) + … + log_{10}(2) + log_{10}(1)$

So we can simply find out the value of $y$ by running a loop from $1$ to $N$ and taking its log value.

After that we decompose $y$ into $p$, integer part and $q$, fraction part. The answer will be $\lfloor 10^q \times 10^{K-1}\rfloor$.

Code

const double eps = 1e-9;

// Find the first K digits of N!
int leadingDigitFact ( int n, int k ) {
    double fact = 0;

    // Find log(N!)
    for ( int i = 1; i <= n; i++ ) {
        fact += log10 ( i );
    }

    // Find the value of q
    double q = fact - floor ( fact+eps );

    double B = pow ( 10, q );

    // Shift decimal point k-1 \times
    for ( int i = 0; i < k - 1; i++ ) {
        B *= 10;
    }

    // Don't forget to floor it
    return floor(B+eps);
}

The code does exactly what we discussed before. But note the $eps$ that we added when flooring value in line $12$ and $22$. This due to precision error when dealing with real numbers in C++. For example, due to precision error sometimes a value which is supposed to be $1$, becomes $0.9999999999999$. The difference between these two values is very small, but if we floor them both, the first one becomes $1$ whereas the second one becomes $0$. So in order to avoid this error, when flooring a positive value we add a small number ( epsilon = $0.000000001$ ) to the number.

Summary

We need to execute the following steps to find the first $K$ leading digits of a number $x$ ( in our problem $x=N!$ ):

  1. Find the log value of the number whose leading digits we are seeking. $y=log_{10}(x)$.
  2. Decompose $y$ into two parts. Integer part $p$ and fraction part $q$.
  3. The answer is $\lfloor 10^q \times 10^{K-1} \rfloor$.

Resource

  1. forthright48 - Number of Trailing Zeroes of Factorial

Number of Trailing Zeroes of Factorial

Problem

Given a positive integer $N$, find the number of trailing zero $N!$ has.

For example, $5! = 120$ has $1$ trailing zero and $10! = 3628800$ has $2$.

Brute Force Solution

Brute Force solution for this problem involves calculating the value of $N!$, which is overkill in this situation. Read the section on Brute Force on this article before you continue – [ref1] Number of Digits of Factorial.

The brute force solution for this problem faces the same limitation as [ref1]. We cannot calculate $N!$ for $N > 20$ using a long long variable and using Big Integer is too slow, $O(N^2)$, if we assume the number of digits in $N!$ increase by $1$ in each step.

Solution Using Factorization

Suppose a there is a number $x$, with $y$ trailing zeroes at its end, then can’t we write that number as $x = z \times 10^y$?

For example, $x = 12300$ and it has $2$ trailing zeroes. Then we can write $x = 123 \times 10^2$.

Therefore, for every factor of $10$, $x$ gets a trailing zero. If we can find out the number of $10$ in $N!$ then we can easily count its number of trailing zeroes.

How do we form a $10$? We form a $10$ by multiplying $2$ and $5$. So we need to find out how many $2$ and $5$ $N!$ has. This can be found using the idea for factorizing $N!$. The idea is discussed in [ref2] Prime Factorization of Factorial. We will be using $\text{factorialPrimePower}()$ function from that post.

So all we need to do is call $x = \text{factorialPrimePower}(2)$ and $y = \text{factorialPrimePower}(5)$ to find out the frequency of those primes. For every pair of $(2,5)$ we get one $10$ factor. So how many pairs can we make if we have $x$ number of $2$’s and $y$ number of $5$’s? We can make $MIN(x,y)$ pairs.

For example, for $10!$ we have $x = \frac{10}{2} + \frac{10}{4} + \frac{10}{8}= 5 + 2 + 1 = 8$ and $y = \frac{10}{5} = 2$. Therefore number of trailing zero is $MIN(x,y) = MIN(8,2) = 2$.

Trailing Zeroes in Different Base

We solved the problem for the decimal base. Now, what if we want to know how many trailing zero does $N!$ has if we convert $N!$ to base $B$?

For example, how many trailing zeroes does $10!$ has in base $16$? In order to solve this, we need to know how the number system works. Read the post [ref3] Introduction to Number System to learn more.

Previously we said that for decimal numbers, multiplying them with $10$ increase their trailing zeroes. Can something similar be said for the base $16$? Yes. Look at the following values:

$(1)_{16} = 1 \times 16^0$
$(10)_{16} = 1 \times 16^1$
$(100)_{16} = 1 \times 16^2$

Every time we multiply a number with its base, all its digits shift a place to left and at the end, a $0$ is added. So for a number in base $B$, if we multiply it by $B$ it gets a trailing zero at its end.

So all we need to do is find out how many $B$ does $N!$ has in it. For base $16$, we need to find out how many \times $16 = 2^4$ occurs in $N!$. For that we find out how many \times $2$ occurs using $x = \text{factorialPrimePower}(2)$ and since we get $16$ for each $2^4$, we conclude that $N!$ has $\frac{x}{4}$ trailing zeroes.

This is extended to any base. Factorize the base $B$ and find out the occurrence of each prime. Then figure out how many combinations we can make.

For example if base is $60$, then $60 = 2^2 \times 3 \times 5$. So we find out $x = \text{factorialPrimePower}(2)$, $y = \text{factorialPrimePower}(3)$ and $z = \text{factorialPrimePower}(5)$. But then, we see that we need $2^2$ in $60$, so we can’t use all the $x$ we calculated, we need to pair them. So it becomes $x = \frac{x}{2}$. Now, using $x,y,z$ we can make $60$ only $MIN(x,y,z)$ times. So this will be number of trailing zero.

Summary

We find number of trailing zero using the following steps:

  1. Factorize the base $B$
  2. If $B = p_1^{a_1} \times p_2^{a_2}… \times p_k^{a_k}$, then find out occurance of $x_i = \text{factorialPrimePower} ( p_i)$.
  3. But we can’t use $x_i$ directly. In order to create $B$ we will need to combine each $p_i$ into $p_i^{a_i}$. So we divide each $x_i$ by $a_i$.
  4. Number of trailing zero is $MIN(x_1,x_2,…,x_k)$.

Resources

  1. forthright48 – Number of Digits of Factorial
  2. forthright48 – Prime Factorization of Factorial
  3. forthright48 – Introduction to Number System
  4. Wikipedia – Trailing Zero