C++ Library for Competitive Programming
#include "emthrm/math/is_primitive_root.hpp"
$n \in \mathbb{N}^+,\ g \in \mathbb{Z}$ に対して $\mathrm{ord}_n(g) = \varphi(n)$ が成り立つとき、$g \bmod n$ を「$n$ を法とする原始根」と呼ぶ。
$n = 2, 4, p^k, 2p^k$ ($p \in \mathbb{P} \setminus \lbrace 2 \rbrace,\ k \in \mathbb{N}^+$) のときのみ $\varphi(\varphi(n))$ 個原始根が存在する。
$a \perp n$ を満たす $a \in \mathbb{Z},\ n \in \mathbb{N}^+$ に対して $a^k \equiv 1 \pmod{n}$ を満たす最小の $k \in \mathbb{N}^+$ である。
$n$ を法とする原始根を $g$ とすると、任意の $a \in \mathbb{Z}$ に対して $g^e \equiv a \pmod{n}$ を満たす $0 \leq e < \varphi(n)$ がただ一つ存在する。この $e$ を「$g$ を底とする $a$ の指数」と呼び、$\mathrm{Ind}_g(a)$ と表す。
時間計算量 | |
---|---|
原始根判定 | $O(\sqrt{M})$ |
名前 | 戻り値 |
---|---|
bool is_primitive_root(long long root, const int m); |
$\mathrm{root}$ は $m$ を法とする原始根であるか。 |
原始根判定
#ifndef EMTHRM_MATH_IS_PRIMITIVE_ROOT_HPP_
#define EMTHRM_MATH_IS_PRIMITIVE_ROOT_HPP_
#include <algorithm>
#include <map>
#include <numeric>
#include <ranges>
#include <utility>
#include <vector>
#include "emthrm/math/euler_phi.hpp"
#include "emthrm/math/mod_pow.hpp"
#include "emthrm/math/prime_factorization.hpp"
namespace emthrm {
bool is_primitive_root(long long root, const int m) {
if ((root %= m) < 0) root += m;
if (std::gcd(root, m) > 1) return false;
static std::map<int, int> phi;
if (!phi.contains(m)) phi[m] = euler_phi(m);
const int phi_m = phi[m];
static std::map<int, std::vector<int>> primes;
if (!primes.contains(phi_m)) {
// GCC 12 adopted P2415.
const std::vector<std::pair<int, int>> pf = prime_factorization(phi_m);
const auto ev = pf | std::views::keys;
// const auto ev = prime_factorization(phi_m) | std::views::keys;
primes[phi_m] = std::vector<int>(ev.begin(), ev.end());
}
return std::none_of(primes[phi_m].begin(), primes[phi_m].end(),
[root, phi_m, m](const int p) -> bool {
return mod_pow(root, phi_m / p, m) == 1;
});
}
} // namespace emthrm
#endif // EMTHRM_MATH_IS_PRIMITIVE_ROOT_HPP_
#line 1 "include/emthrm/math/is_primitive_root.hpp"
#include <algorithm>
#include <map>
#include <numeric>
#include <ranges>
#include <utility>
#include <vector>
#line 1 "include/emthrm/math/euler_phi.hpp"
#include <cassert>
namespace emthrm {
long long euler_phi(long long n) {
assert(n >= 1);
long long res = n;
for (long long i = 2; i * i <= n; ++i) {
if (n % i == 0) [[unlikely]] {
res -= res / i;
while (n % i == 0) n /= i;
}
}
return n > 1 ? res - res / n : res;
}
} // namespace emthrm
#line 1 "include/emthrm/math/mod_pow.hpp"
namespace emthrm {
long long mod_pow(long long x, long long n, const int m) {
if ((x %= m) < 0) x += m;
long long res = 1;
for (; n > 0; n >>= 1) {
if (n & 1) res = (res * x) % m;
x = (x * x) % m;
}
return res;
}
} // namespace emthrm
#line 1 "include/emthrm/math/prime_factorization.hpp"
#line 6 "include/emthrm/math/prime_factorization.hpp"
namespace emthrm {
template <typename T>
std::vector<std::pair<T, int>> prime_factorization(T n) {
std::vector<std::pair<T, int>> res;
for (T i = 2; i * i <= n; ++i) {
if (n % i == 0) [[unlikely]] {
int exponent = 0;
for (; n % i == 0; n /= i) {
++exponent;
}
res.emplace_back(i, exponent);
}
}
if (n > 1) res.emplace_back(n, 1);
return res;
}
} // namespace emthrm
#line 14 "include/emthrm/math/is_primitive_root.hpp"
namespace emthrm {
bool is_primitive_root(long long root, const int m) {
if ((root %= m) < 0) root += m;
if (std::gcd(root, m) > 1) return false;
static std::map<int, int> phi;
if (!phi.contains(m)) phi[m] = euler_phi(m);
const int phi_m = phi[m];
static std::map<int, std::vector<int>> primes;
if (!primes.contains(phi_m)) {
// GCC 12 adopted P2415.
const std::vector<std::pair<int, int>> pf = prime_factorization(phi_m);
const auto ev = pf | std::views::keys;
// const auto ev = prime_factorization(phi_m) | std::views::keys;
primes[phi_m] = std::vector<int>(ev.begin(), ev.end());
}
return std::none_of(primes[phi_m].begin(), primes[phi_m].end(),
[root, phi_m, m](const int p) -> bool {
return mod_pow(root, phi_m / p, m) == 1;
});
}
} // namespace emthrm