Skip to the content.

:warning: Garner のアルゴリズム (オンライン)
(math/garner-online.hpp)

$x\bmod m_1=r_1,x\bmod m_2=r_2,\dots,x\bmod m_n=r_n$ を満たす最小の非負整数 $x$ を求める.ただし $m_1,m_2,\dots,m_n$ のどの 2 つも互いに素.

解の mod をとらないとき

まず $n=2$ のときを考える.

$x=r_1+m_1y$ とおくと $y\equiv(r_2-r_1)m_1^{-1}\pmod{m_2}$ である.

従って $x=r_1+m_1((r_2-r_1)m_1^{-1}\bmod{m_2})$ として $x$ が求められる.

複数 mod のときも繰り返し用いればよい.

解の mod をとるとき

まず$n=2$ のときを考える.

$m_0$ を任意の mod とする($m_1$ および $m_2$ と互いに素でなくとも構わない).

$(x\bmod m_1)\bmod m_i$ の値がわかっているときに $(x\bmod m_1m_2)\bmod m_i$ の値を求める.

そのためには $(r_2-r_1)m_1^{-1}\bmod{m_2}$ の値がわかればよく,これは $(x\bmod m_1)\bmod m_2,m_1\bmod m_2$ の値から求められる.

その値($y$ とする)が求まれば,計算済みの値にそれぞれ $m_1y$ を適宜 mod をとって加えればよい.

一般の $n$ に対しては,以下のように定める.

\[\begin{align*} x_i&=\left(\sum_{j=1}^{i-1}y_j\left(\prod_{l=1}^{j-1}m_l\right)\right)\bmod{m_i}\quad (1\leq i\leq n)\\ y_i&=(r_i-x_i)\left(\prod_{l=1}^{i-1}m_l\right)^{-1}\bmod{m_i}\quad(1\leq i\leq n). \end{align*}\]

このとき実際の値は $y_1+m_1y_2+\cdots+m_1\cdots m_{i-1}y_i$ であるから,これを適宜 mod を取り計算すればよい.

$x$ の値が非常に大きいような場合であっても,四則演算が各 mod の範囲でさえ行えれば計算できる.

オンライン化

解の mod をとらないとき,単に直近の mod と解を持てばよい.

解の mod をとるとき,$m_i,y_i$ の値を管理しておけばよい.

Depends on

Code

#pragma once
#include "math/util.hpp"

template <class T>
struct GarnerOnline {
  vector<T> ms, y;
  GarnerOnline() { init(); }
  void init() { ms.clear(), y.clear(); }
  void push(T r, T m) {
    T x = 0, p = 1;
    for (int j = 0; j < ms.size(); j++) {
      x = (x + y[j] * p) % ms[i];
      p = p * ms[j] % m;
    }
    ms.push_back(m);
    y.push_back((r - x + m) * Math::inv_mod(p, m) % m);
  }
  template <class mint>
  void push(mint x) { push(mint::get_mod(), x.val()); }
  void pop() {
    assert(!ms.empty());
    ms.pop_back(), y.pop_back();
  }
  T get() {
    T res = 0;
    for (int i = (int)ms.size() - 1; i >= 0; i--) res = res * m[i] + y[i];
    return res;
  }
  T get(T mod) {
    T res = 0;
    for (int i = (int)ms.size() - 1; i >= 0; i--) res = (res * m[i] + y[i]) % mod;
    return res;
  }
};

/**
 * @brief Garner のアルゴリズム (オンライン)
 * @docs docs/math/garner.md
 */
#line 2 "math/util.hpp"

namespace Math {
template <class T>
T safe_mod(T a, T b) {
  assert(b != 0);
  if (b < 0) a = -a, b = -b;
  a %= b;
  return a >= 0 ? a : a + b;
}
template <class T>
T floor(T a, T b) {
  assert(b != 0);
  if (b < 0) a = -a, b = -b;
  return a >= 0 ? a / b : (a + 1) / b - 1;
}
template <class T>
T ceil(T a, T b) {
  assert(b != 0);
  if (b < 0) a = -a, b = -b;
  return a > 0 ? (a - 1) / b + 1 : a / b;
}
long long isqrt(long long n) {
  if (n <= 0) return 0;
  long long x = sqrt(n);
  while ((x + 1) * (x + 1) <= n) x++;
  while (x * x > n) x--;
  return x;
}
// return g=gcd(a,b)
// a*x+b*y=g
// - b!=0 -> 0<=x<|b|/g
// - b=0  -> ax=g
template <class T>
T ext_gcd(T a, T b, T& x, T& y) {
  T a0 = a, b0 = b;
  bool sgn_a = a < 0, sgn_b = b < 0;
  if (sgn_a) a = -a;
  if (sgn_b) b = -b;
  if (b == 0) {
    x = sgn_a ? -1 : 1;
    y = 0;
    return a;
  }
  T x00 = 1, x01 = 0, x10 = 0, x11 = 1;
  while (b != 0) {
    T q = a / b, r = a - b * q;
    x00 -= q * x01;
    x10 -= q * x11;
    swap(x00, x01);
    swap(x10, x11);
    a = b, b = r;
  }
  x = x00, y = x10;
  if (sgn_a) x = -x;
  if (sgn_b) y = -y;
  if (b0 != 0) {
    a0 /= a, b0 /= a;
    if (b0 < 0) a0 = -a0, b0 = -b0;
    T q = x >= 0 ? x / b0 : (x + 1) / b0 - 1;
    x -= b0 * q;
    y += a0 * q;
  }
  return a;
}
template <class T>
T inv_mod(T x, T m) {
  x %= m;
  if (x < 0) x += m;
  T a = m, b = x;
  T y0 = 0, y1 = 1;
  while (b > 0) {
    T q = a / b;
    swap(a -= q * b, b);
    swap(y0 -= q * y1, y1);
  }
  if (y0 < 0) y0 += m / a;
  return y0;
}
template <class T>
T pow_mod(T x, T n, T m) {
  x = (x % m + m) % m;
  T y = 1;
  while (n) {
    if (n & 1) y = y * x % m;
    x = x * x % m;
    n >>= 1;
  }
  return y;
}
constexpr long long pow_mod_constexpr(long long x, long long n, int m) {
  if (m == 1) return 0;
  unsigned int _m = (unsigned int)(m);
  unsigned long long r = 1;
  unsigned long long y = x % m;
  if (y >= m) y += m;
  while (n) {
    if (n & 1) r = (r * y) % _m;
    y = (y * y) % _m;
    n >>= 1;
  }
  return r;
}
};  // namespace Math
#line 3 "math/garner-online.hpp"

template <class T>
struct GarnerOnline {
  vector<T> ms, y;
  GarnerOnline() { init(); }
  void init() { ms.clear(), y.clear(); }
  void push(T r, T m) {
    T x = 0, p = 1;
    for (int j = 0; j < ms.size(); j++) {
      x = (x + y[j] * p) % ms[i];
      p = p * ms[j] % m;
    }
    ms.push_back(m);
    y.push_back((r - x + m) * Math::inv_mod(p, m) % m);
  }
  template <class mint>
  void push(mint x) { push(mint::get_mod(), x.val()); }
  void pop() {
    assert(!ms.empty());
    ms.pop_back(), y.pop_back();
  }
  T get() {
    T res = 0;
    for (int i = (int)ms.size() - 1; i >= 0; i--) res = res * m[i] + y[i];
    return res;
  }
  T get(T mod) {
    T res = 0;
    for (int i = (int)ms.size() - 1; i >= 0; i--) res = (res * m[i] + y[i]) % mod;
    return res;
  }
};

/**
 * @brief Garner のアルゴリズム (オンライン)
 * @docs docs/math/garner.md
 */
Back to top page