从中国剩余定理谈POJ 1006

POJ 1006 – Biorhythms

试题链接:POJ 1006

一、费马小定理

p p p为素数,且 a , p a, p a,p互素,则
a p − 1 ≡ 1 ( m o d p ) a^{p-1} \equiv 1 \pmod{p} ap11(modp)

二、欧拉定理

n , a n, a n,a为整数,且 n , a n, a n,a互素,则
a ϕ ( n ) ≡ 1 ( m o d n ) a^{\phi (n)} \equiv 1 \pmod{n} aϕ(n)1(modn)
其中, ϕ ( n ) \phi (n) ϕ(n)为欧拉函数,表示小于 n n n的正整数中与 n n n互质的数的数目。欧拉函数的通式为
ϕ ( n ) = n ∏ i = 1 k ( 1 − 1 p i ) \phi (n) = n \prod_{i=1}^{k}{\left( 1-\frac{1}{p_i} \right)} ϕ(n)=ni=1k(1pi1)
其中, p 1 , p 2 , ⋯   , p k p_1, p_2, \cdots, p_k p1,p2,,pk n n n的所有素因数。

三、逆元

1、定义


a x ≡ 1 ( m o d m ) ax \equiv 1 \pmod{m} ax1(modm)
则称 x = a − 1 x = a^{-1} x=a1 a a a在模 m m m意义下的逆元(数论倒数)。

2、求法

求解数论逆元的方法有很多,下面先介绍根据费马小定理来推出计算公式。

(1)根据费马小定理(当模为素数)

根据费马小定理
a p − 1 ≡ 1 ( m o d p ) a^{p-1} \equiv 1 \pmod{p} ap11(modp)
则有
a a p − 2 ≡ 1 ( m o d p ) a a^{p-2} \equiv 1 \pmod{p} aap21(modp)
所以, a a a在模 p p p p p p为素数)意义下的逆元
a − 1 ≡ a p − 2 ( m o d p ) a^{-1} \equiv a^{p-2} \pmod{p} a1ap2(modp)
因而,我们可以使用快速幂取模来计算逆元。

(2)根据欧拉定理(对于任意模)

根据欧拉定理,不难推出
a ϕ ( n ) − 1 a ≡ 1 ( m o d n ) a^{\phi(n)-1} a \equiv 1 \pmod{n} aϕ(n)1a1(modn)
所以, a a a在模 n n n意义下的逆元
a − 1 ≡ a ϕ ( n ) − 1 ( m o d n ) a^{-1} \equiv a^{\phi(n)-1} \pmod{n} a1aϕ(n)1(modn)
同样可以使用快速幂取模求得。

四、快速幂取模

1、原理

对于一个十进制正整数 n = b k − 1 b k − 2 ⋯ b 1 b 0 ‾ n = \overline{b_{k-1} b_{k-2}\cdots b_{1} b_{0}} n=bk1bk2b1b0可以表示为 k k k位二进制数,即
n = b k − 1 2 k − 1 + b k − 2 2 k − 2 + ⋯ + b 1 2 1 + b 0 2 0 n = b_{k-1}2^{k-1} + b_{k-2}2^{k-2} + \cdots + b_{1}2^{1} + b_{0}2^{0} n=bk12k1+bk22k2++b121+b020
其中, b i ∈ { 0 , 1 } ( i = 0 , 1 , ⋯   , k − 1 ) b_i \in \{0, 1\} (i = 0, 1, \cdots, k-1) bi{0,1}(i=0,1,,k1)

那么对于指数
x n = x b k − 1 2 k − 1 + b k − 2 2 k − 2 + ⋯ + b 1 2 1 + b 0 2 0 = x b k − 1 2 k − 1 ⋅ x b k − 2 2 k − 2 ⋯ x b 1 2 1 ⋅ x b 0 2 0 \begin{aligned} x^{n} &= x^{b_{k-1}2^{k-1} + b_{k-2}2^{k-2} + \cdots + b_{1}2^{1} + b_{0}2^{0}} \\ &= x^{b_{k-1}2^{k-1}} \cdot x^{b_{k-2}2^{k-2}} \cdots x^{b_{1}2^{1}} \cdot x^{b_{0}2^{0}} \end{aligned} xn=xbk12k1+bk22k2++b121+b020=xbk12k1xbk22k2xb121xb020

其中,有一些 b i b_i bi为0,即对应的指数项为1,不会影响最后的结果。如
x 11 = x 1011 ‾ 2 = x 8 ⋅ x 2 ⋅ x 1 x^{11} = x^{\overline{1011}_2} = x^8 \cdot x^2 \cdot x^1 x11=x10112=x8x2x1
x 4 x^4 x4没有出现在最后的乘积中,因为 b 2 = 0 b_2 = 0 b2=0


x 2 i = x 2 i − 1 ⋅ 2 = x 2 i − 1 + 2 i − 1 = x 2 i − 1 ⋅ x 2 i − 1 = ( x 2 i − 1 ) 2 x^{2^{i}} = x^{2^{i-1} \cdot 2} = x^{2^{i-1} + 2^{i-1}} = x^{2^{i-1}} \cdot x^{2^{i-1}} = \left(x^{2^{i-1}}\right)^2 x2i=x2i12=x2i1+2i1=x2i1x2i1=(x2i1)2

所以,就可以使用迭代来计算指数,时间复杂度为 O ( n ) O(n) O(n)

2、伪代码

// calculate (x^n) % M
int exp(int x, int n, int M)
{
    int res = 1;            // the answer
    
    while (n)               // for each bit of n, start from b_0
    {
        if (n & 1)          // if b_i = 1
            res = (res * x) % M;    // multiply into residue
        x = (x * x) % M;    // x^{2^i} = (x^{2^{i-1}})^2
        n >>= 1;            // move to next bit b_{i+1}
    }
    
    return res;
}

五、中国剩余定理(孙子定理)

假设整数 m 1 , m 2 , ⋯   , m n m_1, m_2, \cdots, m_n m1,m2,,mn两两互素,则对于任意的整数 a 1 , a 2 , ⋯   , a n a_1, a_2, \cdots, a_n a1,a2,,an,同余方程组
{ x ≡ a 1 ( m o d m 1 ) x ≡ a 2 ( m o d m 2 ) ⋮ x ≡ a n ( m o d m n ) \left \{ \begin{aligned} x &\equiv a_1 \pmod{m_1} \\ x &\equiv a_2 \pmod{m_2} \\ &\vdots \\ x &\equiv a_n \pmod{m_n} \end{aligned} \right. xxxa1(modm1)a2(modm2)an(modmn)
有解。


M = m 1 × m 2 × ⋯ × m n = ∏ i = 1 n m i M = m_1 \times m_2 \times \cdots \times m_n = \prod_{i=1}^{n}{m_i} M=m1×m2××mn=i=1nmi
是整数 m 1 , m 2 , ⋯   , m n m_1, m_2, \cdots, m_n m1,m2,,mn的乘积,并设
M i = M m i M_i = \frac{M}{m_i} Mi=miM
是除 m i m_i mi以为其他 n − 1 n-1 n1个模的乘积,其中 i = 1 , 2 , ⋯   , n i = 1, 2, \cdots, n i=1,2,,n。且记 M i − 1 M_{i}^{-1} Mi1 M i M_i Mi m i m_i mi的逆元,即
M i M i − 1 ≡ 1 ( m o d m i ) M_i M_i^{-1} \equiv 1 \pmod{m_i} MiMi11(modmi)
则解为
x ≡ a 1 M 1 − 1 M 1 + a 2 M 2 − 1 M 2 + ⋯ + a n M n − 1 M n ( m o d M ) x \equiv a_1 M_1^{-1} M_1 + a_2 M_2^{-1} M_2 + \cdots + a_n M_n^{-1} M_n \pmod{M} xa1M11M1+a2M21M2++anMn1Mn(modM)

六、本题解答

1、推导

根据题意,设 x x x为从当前年第一天开始到三个高峰同时到来之间的天数,我们可以得到同余方程组
{ x ≡ p ( m o d 23 ) x ≡ e ( m o d 28 ) x ≡ i ( m o d 33 ) \left \{ \begin{aligned} x &\equiv p \pmod{23} \\ x &\equiv e \pmod{28} \\ x &\equiv i \pmod{33} \end{aligned} \right. xxxp(mod23)e(mod28)i(mod33)
于是题目要求计算的从当前年给定天 d d d到三个高峰同时到来之间的天数 s s s
s = ( x − d )  mod  21252 s = (x - d) \text{ mod } 21252 s=(xd) mod 21252

由于上述方程组形式已定,所以可以通过中国剩余定理人工求出解的公式,然后将特定的 p , e , i p, e, i p,e,i值代入即可。

本题中, M = 23 × 28 × 33 = 21252 M = 23 \times 28 \times 33 = 21252 M=23×28×33=21252 M 1 = 28 × 33 = 924 M_1 = 28 \times 33 = 924 M1=28×33=924 M 2 = 23 × 33 = 759 M_2 = 23 \times 33 = 759 M2=23×33=759 M 3 = 23 × 28 = 644 M_3 = 23 \times 28 = 644 M3=23×28=644

由于 m 1 = 23 m_1 = 23 m1=23为素数,通过快速幂取模求出 M 1 M_1 M1的逆元,即
M 1 − 1 ≡ M 1 m 1 − 2 ( m o d m 1 ) M_1^{-1} \equiv M_1^{m_1-2} \pmod{m_1} M11M1m12(modm1)
得到 M 1 − 1 ≡ 6 ( m o d 23 ) M_1^{-1} \equiv 6 \pmod{23} M116(mod23)

m 2 = 28 = 2 2 × 7 m_2 = 28 = 2^2 \times 7 m2=28=22×7,其素因数为2和7,则 ϕ ( m 2 ) = 28 × ( 1 − 1 2 ) × ( 1 − 1 7 ) = 12 \phi(m_2) = 28 \times \left( 1 - \frac{1}{2} \right) \times \left( 1 - \frac{1}{7} \right) = 12 ϕ(m2)=28×(121)×(171)=12,于是 M 2 − 1 ≡ 19 ( m o d 28 ) M_2^{-1} \equiv 19 \pmod{28} M2119(mod28)

同理 m 3 = 33 = 3 × 11 m_3 = 33 = 3 \times 11 m3=33=3×11,其素因数为3和11,则 ϕ ( m 3 ) = 33 × ( 1 − 1 3 ) × ( 1 − 1 11 ) = 20 \phi(m_3) = 33 \times \left( 1 - \frac{1}{3} \right) \times \left( 1 - \frac{1}{11} \right) = 20 ϕ(m3)=33×(131)×(1111)=20,于是 M 3 − 1 ≡ 2 ( m o d 33 ) M_3^{-1} \equiv 2 \pmod{33} M312(mod33)

所以,方程组的解为
x ≡ p × M 1 × M 1 − 1 + e × M 2 × M 2 − 1 + m × M 3 × M 3 − 1 ( m o d M ) ≡ 5544 p + 14421 e + 1288 i ( m o d 21252 ) \begin{aligned} x &\equiv p \times M_1 \times M_1^{-1} + e \times M_2 \times M_2^{-1} + m \times M_3 \times M_3^{-1} \pmod{M} \\ &\equiv 5544p + 14421e + 1288i \pmod{21252} \end{aligned} xp×M1×M11+e×M2×M21+m×M3×M31(modM)5544p+14421e+1288i(mod21252)

2、代码

#include <stdio.h>

int main()
{
    int p, e, i, d, x, s;
    int cnt = 0;

    while (scanf("%d %d %d %d", &p, &e, &i, &d) && p >= 0)
    {
        ++cnt;
        x = (5544 * p + 14421 * e + 1288 * i) % 21252;
        s = (21252 + x - d) % 21252;
        if (s == 0)
            s = 21252;
        printf("Case %d: the next triple peak occurs in %d days.\n", cnt, s);
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值