## 10.5 Computing Modular Powers
![](https://box.kancloud.cn/485bc67a7beadb016ac9d44f235f6e03_27x27.jpg) For an element \[*m*\]*n* ∊ **Z***n* and nonnegative integer *k*, the problem of computing
![](https://box.kancloud.cn/04db6718a6bfd3237514c21e9fbd62d6_60x28.jpg)
is called the *problem of computing modular powers.* [Examples 10.43](LiB0083.html#1105) and [Examples 10.44](LiB0083.html#1106) showed the computation of some modular powers. Another example follows.
Example 10.51**![Start example](https://box.kancloud.cn/e95df9d604ab2d89febe370ae4d88fb1_1x1.gif)**![](https://box.kancloud.cn/44c6e5b0628dc8a29323fbd49f3a833e_279x26.jpg)
In [Example 10.51](#ch10ex89) we determined the power simply by taking 7 to the 11th power. Clearly, this method has exponential time complexity in terms of the input size (approximately the logarithms of the numbers). Next we develop a more efficient algorithm.
**![End example](https://box.kancloud.cn/e95df9d604ab2d89febe370ae4d88fb1_1x1.gif)**
The algorithm requires we represent *k* as a binary number. Let
![](https://box.kancloud.cn/ffd474fa55e3f1773acbea90e9f7b74c_159x22.jpg)
be the ordered set of binary digits in that representation. For example, since the binary representation of 13 is 1101, if *k* = 13,
![](https://box.kancloud.cn/625b6e6c61cd7dd10042ae8f21aecc4b_215x22.jpg)
The algorithm now follows. We say that the algorithm uses the method of ***repeated squaring.*** The reason is obvious.
Algorithm 10.4 Compute Modular Power**![Start example](https://box.kancloud.cn/e95df9d604ab2d89febe370ae4d88fb1_1x1.gif)**Problem: For an element \[*m*\]*n* ∊ **Z**n and nonnegative integer *k*, compute (\[*m*\]*n*)*k.*
**![End example](https://box.kancloud.cn/e95df9d604ab2d89febe370ae4d88fb1_1x1.gif)**
Inputs: positive integer *n*, and nonnegative integers *m* and *k.*
Outputs: (\[*m*\]*n*)*k.*
```
void compute_power (int n, int m, int k, Znmember& a)
{
index i;
a = [1]n;
{bj, bj-1, . . ., b1, b0} = ordered set of digits in binary
representation of k;
for (i = j; i >= 0; i - -)
a = a2;
if (bi = = 1)
a = a×[m]n;
}
```
Next we show an example of applying [Algorithm 10.4](#ch10ex90).
Example 10.52**![Start example](https://box.kancloud.cn/e95df9d604ab2d89febe370ae4d88fb1_1x1.gif)**Suppose
**![End example](https://box.kancloud.cn/e95df9d604ab2d89febe370ae4d88fb1_1x1.gif)**
![](https://box.kancloud.cn/a598d570a086488f2cff1142da7c2f9c_241x17.jpg)
Since the binary representation of 45 is 101101,
![](https://box.kancloud.cn/034f6946c373f38a73483f8203adb1ed_297x22.jpg)
[Table 10.2](#ch10table02) shows the state of *a* after each iteration of the **for**-*i* loop in [Algorithm 10.4](#ch10ex90) given this input. The final value of *a*, namely \[147\]257, is the value of (\[5\]257)45.
Table 10.2: The state of *a* after each iteration of the for-*i* loop in [Algorithm 10.4](#ch10ex90) when *n* = 257, *m* = 5, and *k* = 45. The third row shows the value determined by the high-order *j* - *i* + 1 bits in the binary representation of *k.*i
5
4
3
2
1
0
bi
1
0
1
1
0
1
ki
1
2
5
11
22
45
a
\[5\]257
\[25\]257
\[41\]257
\[181\]257
\[122\]257
\[147\]257
In [Table 10.2](#ch10table02), *ki* is the value determined by the high-order *j* - *i* + 1 bits in the binary representation of *k.* That is,
![](https://box.kancloud.cn/85f273eefa3ed81a43dd60cce7ef0060_159x24.jpg)
For example,
![](https://box.kancloud.cn/1e5115692d2bc56e3a6cbb1768a7f299_221x23.jpg)
Clearly, *k*0 = *k.* We will use these variables to prove the correctness of the algorithm, which we do next.
Theorem 10.26**![Start example](https://box.kancloud.cn/e95df9d604ab2d89febe370ae4d88fb1_1x1.gif)**After each iteration of the **for**-*i* loop in [Algorithm 10.4](#ch10ex90),
**![End example](https://box.kancloud.cn/e95df9d604ab2d89febe370ae4d88fb1_1x1.gif)**
![](https://box.kancloud.cn/17d72dce49ebe66b465efb5c93c4ad66_107x28.jpg)
Since *k*0 = *k*, this means the final value of *a* is (\[*m*\]*n*)*k.*
Proof: The proof is by induction.
Induction base: We assume a minimal binary representation; so the high-order bit *bj* equals 1. Therefore,
![](https://box.kancloud.cn/04c6719a03bd2a740172f556959b724a_57x22.jpg)
Before entering the **for**-*i* loop, *a* = \[1\]*n.* Since *bj* equals 1, the **if** condition is true in the first iteration, which means the instruction *a* = *a*×\[*m*\]*n* executes. Therefore, we have
![](https://box.kancloud.cn/c74a3f3eca938d48f4258fe9f8e99eb8_313x26.jpg)
after the first iteration.
Induction hypothesis: Suppose after the iteration with index value *i* that
![](https://box.kancloud.cn/9d4a835bf7eef6cf769b40e6f0c0cb33_107x27.jpg)
Induction step: We need to show that, after the iteration with index value (*i* - 1),
![](https://box.kancloud.cn/4e0cdecb192644e0e8b2bf6e18409944_124x28.jpg)
There are two cases: If *bi*-1 = 0, clearly
![](https://box.kancloud.cn/25649d453f1ca2bea7cedc6d4876c6af_90x20.jpg)
Since the condition in the **if** statement is false, only the instruction *a* = *a*2 changes the value of *a.* Since by the induction hypothesis the previous value of *a* is (\[*m*\]*n*)*ki*, we have
![](https://box.kancloud.cn/cb32893a158fb63f7f68b7030343fc00_208x27.jpg)
after this iteration.
If *bi*-1 = 1, clearly
![](https://box.kancloud.cn/f95cccb94d50c13b0ab67e14bcf62595_119x20.jpg)
Since the condition in the **if** statement is true, the instruction *a* = *a*×\[*m*\]*n* also executes. So in this case,
![](https://box.kancloud.cn/69e7b95bb444e25bfc6a4f24fad7660f_378x27.jpg)
after this iteration.
It is straightforward that, if we let *s* be the number of bits it take to encode the input, the time complexity of [Algorithm 10.4](#ch10ex90) is in *O*(*s*3).
- Table of Contents
- BackCover
- Foundations of Algorithms Using C++ Pseudocode, Third Edition
- Preface
- Chapter Contents
- Pedagogy
- Course Outlines
- Acknowledgments
- Errors
- Chapter 1: Algorithms - Efficiency, Analysis, and Order
- 1.2 The Importance of Developing Efficient Algorithms
- 1.3 Analysis of Algorithms
- 1.4 Order
- 1.5 Outline of This Book
- Exercises
- Chapter 2: Divide-and-Conquer
- 2.1 Binary Search
- 2.2 Mergesort
- 2.3 The Divide-and-Conquer Approach
- 2.4 Quicksort (Partition Exchange Sort)
- 2.5 Strassen's Matrix Multiplication Algorithm
- 2.6 Arithmetic with Large Integers
- 2.7 Determining Thresholds
- 2.8 When Not to Use Divide-and-Conquer
- Exercises
- Chapter 3: Dynamic Programming
- 3.1 The Binomial Coefficient
- 3.2 Floyd's Algorithm for Shortest Paths
- 3.3 Dynamic Programming and Optimization Problems
- 3.4 Chained Matrix Multiplication
- 3.5 Optimal Binary Search Trees
- 3.6 The Traveling Salesperson Problem
- Exercises
- Chapter 4: The Greedy Approach
- 4.1 Minimum Spanning Trees
- 4.2 Dijkstra's Algorithm for Single-Source Shortest Paths
- 4.3 Scheduling
- 4.4 Huffman Code
- 4.5 The Greedy Approach versus Dynamic Programming: The Knapsack Problem
- Exercises
- Chapter 5: Backtracking
- 5.2 The n-Queens Problem
- 5.3 Using a Monte Carlo Algorithm to Estimate the Efficiency of a Backtracking Algorithm
- 5.4 The Sum-of-Subsets Problem
- 5.5 Graph Coloring
- 5.6 The Hamiltonian Circuits Problem
- 5.7 The 0-1 Knapsack Problem
- Exercises
- Chapter 6: Branch-and-Bound
- 6.1 Illustrating Branch-and-Bound with the 0 - 1 Knapsack problem
- 6.2 The Traveling Salesperson Problem
- 6.3 Abductive Inference (Diagnosis)
- Exercises
- Chapter 7: Introduction to Computational Complexity - The Sorting Problem
- 7.2 Insertion Sort and Selection Sort
- 7.3 Lower Bounds for Algorithms that Remove at Most One Inversion per Comparison
- 7.4 Mergesort Revisited
- 7.5 Quicksort Revisited
- 7.6 Heapsort
- 7.6.1 Heaps and Basic Heap Routines
- 7.6.2 An Implementation of Heapsort
- 7.7 Comparison of Mergesort, Quicksort, and Heapsort
- 7.8 Lower Bounds for Sorting Only by Comparison of Keys
- 7.8.1 Decision Trees for Sorting Algorithms
- 7.8.2 Lower Bounds for Worst-Case Behavior
- 7.8.3 Lower Bounds for Average-Case Behavior
- 7.9 Sorting by Distribution (Radix Sort)
- Exercises
- Chapter 8: More Computational Complexity - The Searching Problem
- 8.1 Lower Bounds for Searching Only by Comparisons of Keys
- 8.2 Interpolation Search
- 8.3 Searching in Trees
- 8.4 Hashing
- 8.5 The Selection Problem: Introduction to Adversary Arguments
- Exercises
- Chapter 9: Computational Complexity and Interactability - An Introduction to the Theory of NP
- 9.2 Input Size Revisited
- 9.3 The Three General Problem Categories
- 9.4 The Theory of NP
- 9.5 Handling NP-Hard Problems
- Exercises
- Chapter 10: Number-Theoretic Algorithms
- 10.1 Number Theory Review
- 10.2 Computing the Greatest Common Divisor
- 10.3 Modular Arithmetic Review
- 10.4 Solving Modular Linear Equations
- 10.5 Computing Modular Powers
- 10.6 Finding Large Prime Numbers
- 10.7 The RSA Public-Key Cryptosystem
- Exercises
- Chapter 11: Introduction to Parallel Algorithms
- 11.1 Parallel Architectures
- 11.2 The PRAM Model
- Exercises
- Appendix A: Review of Necessary Mathematics
- A.2 Functions
- A.3 Mathematical Induction
- A.4 Theorems and Lemmas
- A.5 Logarithms
- A.6 Sets
- A.7 Permutations and Combinations
- A.8 Probability
- Exercises
- Appendix B: Solving Recurrence Equations - With Applications to Analysis of Recursive Algorithms
- B.2 Solving Recurrences Using the Characteristic Equation
- B.3 Solving Recurrences by Substitution
- B.4 Extending Results for n, a Power of a Positive Constant b, to n in General
- B.5 Proofs of Theorems
- Exercises
- Appendix C: Data Structures for Disjoint Sets
- References
- Index
- Index_B
- Index_C
- Index_D
- Index_E
- Index_F
- Index_G
- Index_H
- Index_I
- Index_J
- Index_K
- Index_L
- Index_M
- Index_N
- Index_O
- Index_P
- Index_Q
- Index_R
- Index_S
- Index_T
- Index_U
- Index_V
- Index_W-X
- Index_Y
- Index_Z
- List of Figures
- List of Tables
- List of Algorithms, Examples, and Theorems
- List of Sidebars