decimal_power_mod.hpp¶
指数以超长十进制字符串给出时计算模幂,不要求底数与模数互质。
\[
\displaystyle a^e\equiv r\;(\bmod m)
\]
Complexity: Time: \(O(d)\) modular multiplications for \(d\) decimal exponent digits. Space: \(O(1)\) beyond the exponent string.
Implementation¶
当前头文件,省略 include guard;依赖见 #include。
/// @complexity Time: O(d) modular multiplications for d decimal exponent digits.
/// Space: O(1) beyond the exponent string.
#include <array>
#include <cassert>
#include <cstdint>
#include <string_view>
namespace noya {
/// @brief Compute bas^exp modulo an arbitrary positive 64-bit modulus
/// from a decimal exponent of any length, without coprimality assumptions.
inline std::uint64_t decimal_power_mod(std::uint64_t bas, std::string_view exp,
std::uint64_t mod) {
assert(mod >= 1);
assert(!exp.empty());
for ([[maybe_unused]] char dig : exp) {
assert('0' <= dig && dig <= '9');
}
using u128 = unsigned __int128;
auto mul = [&](std::uint64_t a, std::uint64_t b) {
return std::uint64_t(u128(a) * b % mod);
};
bas %= mod;
auto first = exp.find_first_not_of('0');
// Preserve 0^0 = 1 (mod mod), including an all-zero decimal string.
if (first == std::string_view::npos) {
return 1 % mod;
}
exp.remove_prefix(first);
if (bas <= 1) {
return bas;
}
if (bas == mod - 1) {
return ((exp.back() - '0') & 1) ? bas : 1;
}
// Every decimal string of at most 19 digits fits uint64_t.
if (exp.size() <= 19) {
std::uint64_t exponent = 0;
for (char dig : exp) {
exponent = exponent * 10 + dig - '0';
}
std::uint64_t res = 1;
while (exponent > 0) {
if (exponent & 1) {
res = mul(res, bas);
}
exponent >>= 1;
if (exponent) {
bas = mul(bas, bas);
}
}
return res;
}
std::array<std::uint64_t, 10> powers{1 % mod, bas};
for (int i = 2; i < 10; i++) {
powers[i] = mul(powers[i - 1], bas);
}
std::uint64_t res = powers[exp.front() - '0'];
exp.remove_prefix(1);
if (res == 0) {
return 0;
}
for (char dig : exp) {
// Append digit d: a^(10*e+d) = (a^e)^10 * a^d.
auto x2 = mul(res, res);
auto x4 = mul(x2, x2);
auto x8 = mul(x4, x4);
res = mul(mul(x8, x2), powers[dig - '0']);
// Further decimal digits keep a zero residue zero.
if (res == 0) {
return 0;
}
}
return res;
}
} // namespace noya
// NOYA_DECIMAL_POWER_MOD_HPP
#ifndef NOYA_DECIMAL_POWER_MOD_HPP
#define NOYA_DECIMAL_POWER_MOD_HPP 1
/// @complexity Time: O(d) modular multiplications for d decimal exponent digits.
/// Space: O(1) beyond the exponent string.
#include <array>
#include <cassert>
#include <cstdint>
#include <string_view>
namespace noya {
/// @brief Compute bas^exp modulo an arbitrary positive 64-bit modulus
/// from a decimal exponent of any length, without coprimality assumptions.
inline std::uint64_t decimal_power_mod(std::uint64_t bas, std::string_view exp,
std::uint64_t mod) {
assert(mod >= 1);
assert(!exp.empty());
for ([[maybe_unused]] char dig : exp) {
assert('0' <= dig && dig <= '9');
}
using u128 = unsigned __int128;
auto mul = [&](std::uint64_t a, std::uint64_t b) {
return std::uint64_t(u128(a) * b % mod);
};
bas %= mod;
auto first = exp.find_first_not_of('0');
// Preserve 0^0 = 1 (mod mod), including an all-zero decimal string.
if (first == std::string_view::npos) {
return 1 % mod;
}
exp.remove_prefix(first);
if (bas <= 1) {
return bas;
}
if (bas == mod - 1) {
return ((exp.back() - '0') & 1) ? bas : 1;
}
// Every decimal string of at most 19 digits fits uint64_t.
if (exp.size() <= 19) {
std::uint64_t exponent = 0;
for (char dig : exp) {
exponent = exponent * 10 + dig - '0';
}
std::uint64_t res = 1;
while (exponent > 0) {
if (exponent & 1) {
res = mul(res, bas);
}
exponent >>= 1;
if (exponent) {
bas = mul(bas, bas);
}
}
return res;
}
std::array<std::uint64_t, 10> powers{1 % mod, bas};
for (int i = 2; i < 10; i++) {
powers[i] = mul(powers[i - 1], bas);
}
std::uint64_t res = powers[exp.front() - '0'];
exp.remove_prefix(1);
if (res == 0) {
return 0;
}
for (char dig : exp) {
// Append digit d: a^(10*e+d) = (a^e)^10 * a^d.
auto x2 = mul(res, res);
auto x4 = mul(x2, x2);
auto x8 = mul(x4, x4);
res = mul(mul(x8, x2), powers[dig - '0']);
// Further decimal digits keep a zero residue zero.
if (res == 0) {
return 0;
}
}
return res;
}
} // namespace noya
#endif // NOYA_DECIMAL_POWER_MOD_HPP
/// @complexity Time: O(d) modular multiplications for d decimal exponent digits.
/// Space: O(1) beyond the exponent string.
#include <array>
#include <cassert>
#include <cstdint>
#include <string_view>
namespace noya {
/// @brief Compute bas^exp modulo an arbitrary positive 64-bit modulus
/// from a decimal exponent of any length, without coprimality assumptions.
inline std::uint64_t decimal_power_mod(std::uint64_t bas, std::string_view exp,
std::uint64_t mod) {
assert(mod >= 1);
assert(!exp.empty());
for ([[maybe_unused]] char dig : exp) {
assert('0' <= dig && dig <= '9');
}
using u128 = unsigned __int128;
auto mul = [&](std::uint64_t a, std::uint64_t b) {
return std::uint64_t(u128(a) * b % mod);
};
bas %= mod;
auto first = exp.find_first_not_of('0');
// Preserve 0^0 = 1 (mod mod), including an all-zero decimal string.
if (first == std::string_view::npos) {
return 1 % mod;
}
exp.remove_prefix(first);
if (bas <= 1) {
return bas;
}
if (bas == mod - 1) {
return ((exp.back() - '0') & 1) ? bas : 1;
}
// Every decimal string of at most 19 digits fits uint64_t.
if (exp.size() <= 19) {
std::uint64_t exponent = 0;
for (char dig : exp) {
exponent = exponent * 10 + dig - '0';
}
std::uint64_t res = 1;
while (exponent > 0) {
if (exponent & 1) {
res = mul(res, bas);
}
exponent >>= 1;
if (exponent) {
bas = mul(bas, bas);
}
}
return res;
}
std::array<std::uint64_t, 10> powers{1 % mod, bas};
for (int i = 2; i < 10; i++) {
powers[i] = mul(powers[i - 1], bas);
}
std::uint64_t res = powers[exp.front() - '0'];
exp.remove_prefix(1);
if (res == 0) {
return 0;
}
for (char dig : exp) {
// Append digit d: a^(10*e+d) = (a^e)^10 * a^d.
auto x2 = mul(res, res);
auto x4 = mul(x2, x2);
auto x8 = mul(x4, x4);
res = mul(mul(x8, x2), powers[dig - '0']);
// Further decimal digits keep a zero residue zero.
if (res == 0) {
return 0;
}
}
return res;
}
} // namespace noya
// NOYA_DECIMAL_POWER_MOD_HPP