[ci skip] Modular exponentiation, modular multiplicative inverse, extended Euclidean algorithm, discrete logarithm.

This commit is contained in:
Nick Thompson
2018-01-28 14:47:14 -06:00
parent 919c5277c1
commit fc4d657201
14 changed files with 768 additions and 39 deletions

View File

@ -0,0 +1,97 @@
[section:discrete_log Discrete Log]
[section Introduction]
The discrete log is the inverse of modular exponentiation.
To wit, if /a/[sup /x/] = /b/ mod /p/, then we write /x/ = log[sub a](/b/).
Fast algorithms for modular exponentiation exists, but currently there are no polynomial time algorithms known for the discrete logarithm,
a fact which is the basis for the security of Diffie-Hellman key exchange.
Despite having exponential complexity in the number of bits, the algorithms for discrete logarithm provided by Boost are still useful,
for there are many uses of the discrete logarithm outside of cryptography which do not require massive inputs.
The algorithms provided by Boost should be acceptable up to roughly 64 bits.
[endsect]
[section Synopsis]
#include <boost/integer/discrete_log.hpp>
namespace boost { namespace integer {
template<class Z>
boost::optional<Z> trial_multiplication_discrete_log(Z base, Z arg, Z p);
template<class Z>
class baby_step_giant_step_discrete_log
{
public:
baby_step_giant_step_discrete_log(Z base, Z p);
Z operator()(Z arg) const;
};
}}
[endsect]
[section Usage]
Boost provides two algorithms for the discrete log: Trial multiplication and the "baby-step giant step" algorithm.
Basic usage is shown below:
auto logarithm = trial_multiplication_discrete_log(2, 3, 5);
if (logarithm)
{
std::cout << "log_2(3) mod 5 = " << l.value() << std::endl;
}
auto bsgs = baby_step_giant_step_discrete_log(2, 5);
int log = bsgs(3);
std::cout << "log_2(3) mod 5 = " << log << std::endl;
Of these, trial multiplication is more general, requires O(/p/) time and O(1) storage.
The baby-step giant step algorithm requires O([radic] p) time and O([radic] p) storage, and is slightly less general as the generator must be coprime to the the modulus.
Let's illustrate this with a few examples: Suppose we wish to compute log[sub 2](3) mod 4.
Since 2[sup x] = 3 mod 4 has no solution, the result is undefined.
boost::optional<int> l = trial_multiplication_discrete_log(2, 3, 4);
if (!l)
{
std::cout << "log_2(3) mod 4 is undefined!\n";
}
The baby-step giant-step algorithm is less polite when the base and the modulus are not coprime:
try
{
auto bsgs = baby_step_giant_step_discrete_log(2, 4);
}
catch(std::exception const & e)
{
// e.what() is gonna tell you 2 and 4 are not coprime:
std::cout << e.what() << std::endl;
}
The baby-step giant-step discrete log will *never* compute a logarithm when the generator and modulus are not coprime, because it relies on the existence of modular multiplicative inverses.
However, discrete logarithms can exist even when the generator and modulus share a common divisor greater than 1.
For example, since 2[sup 1] = 2 mod 4, log[sub 2](2) = 1.
Trial multiplication successfully recovers this value, and `baby_step_giant_step_discrete_log` blows up.
[endsect]
[section References]
Wagstaff, Samuel S., ['The Joy of Factoring], Vol. 68. American Mathematical Soc., 2013.
[endsect]
[endsect]
[/
Copyright 2018 Nick Thompson.
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or copy at
http://www.boost.org/LICENSE_1_0.txt).
]

View File

@ -0,0 +1,47 @@
[section:extended_euclidean Extended Euclidean Algorithm]
[section Introduction]
The extended Euclidean algorithm solves the integer relation /mx + ny/ = gcd(/m/, /n/) for /x/ and /y/.
[endsect]
[section Synopsis]
#include <boost/integer/extended_euclidean.hpp>
namespace boost { namespace integer {
template<class Z>
std::tuple<Z, Z, Z> extended_euclidean(Z m, Z n);
}}
[endsect]
[section Usage]
The tuple returned by the extended Euclidean algorithm contains, the greatest common divisor, /x/, and /y/, in that order:
int m = 12;
int n = 15;
auto tup = extended_euclidean(m, n);
int gcd = std::get<0>(tup);
int x = std::get<1>(tup);
int y = std::get<2>(tup);
// mx + ny = gcd(m,n)
[endsect]
[section References]
Wagstaff, Samuel S., ['The Joy of Factoring], Vol. 68. American Mathematical Soc., 2013.
[endsect]
[endsect]
[/
Copyright 2018 Nick Thompson.
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or copy at
http://www.boost.org/LICENSE_1_0.txt).
]

View File

@ -0,0 +1,51 @@
[section:modular_multiplicative_inverse Modular Multiplicative Inverse]
[section Introduction]
The modular multiplicative inverse of a number /a/ is that number /x/ which satisfied /ax/ = 1 mod /p/.
A fast algorithm for computing modular multiplicative inverses based on the extended Euclidean algorithm exists and is provided by Boost.
[endsect]
[section Synopsis]
#include <boost/integer/modular_multiplicative_inverse.hpp>
namespace boost { namespace integer {
template<class Z>
boost::optional<Z> modular_multiplicative_inverse(Z a, Z p);
}}
[endsect]
[section Usage]
Multiplicative modular inverses exist if and only if /a/ and /p/ are coprime.
So for example
auto x = modular_multiplicative_inverse(2, 5);
if (x)
{
int should_be_three = x.value();
}
auto y = modular_multiplicative_inverse(2, 4);
if (!y)
{
std::cout << "There is no inverse of 2 mod 4\n";
}
[endsect]
[section References]
Wagstaff, Samuel S., ['The Joy of Factoring], Vol. 68. American Mathematical Soc., 2013.
[endsect]
[endsect]
[/
Copyright 2018 Nick Thompson.
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or copy at
http://www.boost.org/LICENSE_1_0.txt).
]