同余模方程学习小记 [FINISHED]

什么是同余模呢?比如21%6=3,21%8=5,21%11=10,就是给出(6,3)(8,5)(11,10)这样的数对来求一个最小的正整数X,满足:

X%6=3;
X%8=5;
X%11=10。
怎么解决这个问题呢?首先我们将这样的数对表示为(m,r),设有两个(m1,r1),(m2,r2),设答案为X,那么显然有X=m1*k1+r1,X=m2*k2+r2 ,因此得到:m1*k1+r1=m2*k2+r2,即:m1*k1-m2*k2=r2-r1。设x=k1,y=-k2,c=r2-r1,上式即为:m1*x+m2*y=c。我们可以用扩展欧几里得求出x和y以及m1和m2的最大公约数d,如果c不能整除d则显然这样的x和y是不存在的。下面的讨论我们假设c能够整除d.我们知道我们求出x和y满足:m1*x+m2*y=d。那么我们可以求出x0和y0满足m1*x0+m2*y0=c,显然x0=c/d*x,y0=c/d*y,并且我们知道,X、Y的通项公式为:
X=x0+(m2/d)*k
Y=y0-(m1/d)*k
因此为求出最小的正整数X,我们总能用m2/d来调节x0使X最小。那么,若给出的是超过两组的(m,r)呢?这样解决:将r0=X(X就是用前两组求出的X),m0=m1*m2/d(即m0是m1和m2的最小公倍数),那么若有超过两组的(m,r),最后的答案ans必满足:ans%m0=r0。因为ans=m0*k+r0,因此ans%m1=(m0*k+r0)%m1=r0%m1=X%m1=r1,同理对(m2,r2)也是。这样我们就将前两组合并成新的一组(m0,r0)。这样依次合并,

#include <vector>

template <typename INT_TYPE = long long>
class CongruenceSolver {
public:
  // The input are {(6, 3), (8, 5), (11, 10)}
  INT_TYPE Solve(const std::vector<std::pair<INT_TYPE, INT_TYPE>> &pi) {
    INT_TYPE a = pi.front().second;
    INT_TYPE m = pi.front().first;

    for (decltype(pi.size()) i = 1; i < pi.size(); ++i) {
      INT_TYPE x, y;
      INT_TYPE d = EXGcd(m, pi[i].first, x, y);
      INT_TYPE c = pi[i].second - a;
      if (c % d != 0) {
        return -1;
      }
      INT_TYPE t = pi[i].first / d;
      x = (c / d * x % t + t) % t;
      a = m * x + a;
      m = m * pi[i].first / d;
    }
    return a;
  }

private:
  INT_TYPE EXGcd(INT_TYPE a, INT_TYPE b, INT_TYPE &x, INT_TYPE &y) {
    if (0 == b) {
      x = 1;
      y = 0;
      return a;
    }
    INT_TYPE d = EXGcd(b, a % b, x, y);
    INT_TYPE t = x;
    x = y;
    y = t - a / b * y;
    return d;
  }
};

 

  
 
posted @ 2013-11-27 22:51  朝拜明天19891101  阅读(476)  评论(0编辑  收藏  举报