【C/C++无聊练手(三)】计算1/1000000007的循环节(长度为1000000006)

C++代码的空间复杂度优化

在文章【C/C++无聊练手(一)】从「计算1/N的循环节」到「计算M/N的循环节」的理论推导&代码实现中,我们轻而易举地计算了千万量级输入的分数循环节。

诚然,上面的代码的确能用,时间复杂度是 O ( N ) \mathcal{O}\left(N\right) O(N) 。然而空间复杂度近似于 5 N 5N 5N ,常数 5 5 5 十分大。这使得我们在计算 1000000007 这种十亿级别的输入时,内存会被直接吃完。

让我们反思一下,空间复杂度其实分为 N + 4 N N+4N N+4N ,那 4 N 4N 4N 是因为我们用了 int 类型的 N N N 长度数组来计算循环节长度。如果我们通过质因数分解+欧拉定理的方式简化计算过程,那么 4 N 4N 4N 这部分空间复杂度将会被优化为 O ( log ⁡ N ) \mathcal{O}\left( \log N\right) O(logN)

因为这一部分需要用到质因数分解,所以我单独为质因数分解写了一串代码,放到上一篇文章【C/C++无聊练手(二)】将质因数分解封装为类+快速幂求幂模函数+代码正确性单元测试(本文作为下文的基础类)

因为这部分涉及到了一些近世代数的理论知识,所以我没有在第一篇文章进行介绍,而是在本篇文章与上一篇文章中进行

近世代数基本知识+理论推导

定义1:设 a a a n n n 为互素的整数, a ≠ 0 a\neq 0 a=0 N ≠ 1 N\neq1 N=1,使得 a x ≡ 1 ( m o d   n ) a^x\equiv 1\left(\mathrm{mod}\ n\right) ax1(mod n)最小正整数 x x x 称为 a a a n n n 的阶,记为 o r d n a \mathrm{ord}_na ordna

我们考虑最简分式 M / N M/N M/N ,并设 0 < M < N 0<M<N 0<M<N ,那么它的循环节长度和 1 / N 1/N 1/N 的循环节长度相同。

我们设 N = 2 s 1 5 s 2 t N=2^{s_1}5^{s_2}t N=2s15s2t ,其中正整数 t t t 的因子中不含 2 2 2 5 5 5 。那么有限小数部分 p = max ⁡ { s 1 ,   s 2 } p=\max\left\{s_1,\ s_2\right\} p=max{s1, s2} ,循环节长度为 o r d t 10 \mathrm{ord}_{t}10 ordt10也就是说, ( p ,   p + o r d t 10 ] \left(p,\ p+\mathrm{ord}_{t}10\right] (p, p+ordt10] 为最小循环节部分

显然, t t t p p p 都很好求,现在问题在于如何求 o r d t 10 \mathrm{ord}_{t}10 ordt10 。根据欧拉公式 a ϕ ( n ) ≡ 1 ( m o d   n ) a^{\phi\left(n\right)}\equiv 1\left(\mathrm{mod}\ n\right) aϕ(n)1(mod n) ,得到 o r d r 10 \mathrm{ord}_{r}10 ordr10 ϕ ( t ) \phi\left(t\right) ϕ(t) 的因子,即 o r d t 10 ∣ ϕ ( t ) \mathrm{ord}_{t}10|\phi\left(t\right) ordt10ϕ(t)此时 o r d t 10 \mathrm{ord}_{t}10 ordt10 的搜索范围成功缩小到了 ϕ ( t ) \phi\left(t\right) ϕ(t) 的全体因子

举个例子,假设 ϕ ( t ) = 2 a 3 b 5 c \phi\left(t\right)=2^a3^b5^c ϕ(t)=2a3b5c ,它的全体因子个数为 ( a + 1 ) ( b + 1 ) ( c + 1 ) \left(a+1\right)\left(b+1\right)\left(c+1\right) (a+1)(b+1)(c+1) ,分别为 ( 2 0 , 2 1 , ⋯ 2 a ) × ( 3 0 , 3 1 , ⋯ 3 b ) × ( 5 0 , 5 1 , ⋯ 5 c ) \left(2^0,2^1,\cdots 2^a\right)\times\left(3^0,3^1,\cdots 3^b\right)\times\left(5^0,5^1,\cdots 5^c\right) (20,21,2a)×(30,31,3b)×(50,51,5c) ,其中符号 × \times × 代表直和。

具体而言, ϕ ( 63 ) = 36 = 2 2 3 2 \phi\left(63\right)=36=2^23^2 ϕ(63)=36=2232 ,此时恰好相当于 a = 2 , b = 2 , c = 0 a=2,b=2,c=0 a=2,b=2,c=0 ,相当于 ϕ ( t ) = 36 \phi\left(t\right)=36 ϕ(t)=36 ,那么
( 2 0 , ⋯   , 2 a ) × ( 3 0 , ⋯   , 3 b ) = ( 2 0 , 2 1 , 2 2 ) × ( 3 0 , 3 1 , 3 2 ) = ( 2 0 3 0 , 2 0 3 1 , 2 0 3 2 , 2 1 3 0 , 2 1 3 1 , 2 1 3 2 , 2 2 3 0 , 2 2 3 1 , 2 2 3 2 ) (1.1) \begin{aligned} &\left( 2^0,\cdots ,2^a \right) \times \left( 3^0,\cdots ,3^b \right) \\ =&\left( 2^0,2^1,2^2 \right) \times \left( 3^0,3^1,3^2 \right)\\ =&\left( 2^03^0,2^03^1,2^03^2,2^13^0,2^13^1,2^13^2,2^23^0,2^23^1,2^23^2 \right)\\ \end{aligned} \tag{1.1} ==(20,,2a)×(30,,3b)(20,21,22)×(30,31,32)(2030,2031,2032,2130,2131,2132,2230,2231,2232)(1.1)

其中 φ ( 63 ) \varphi\left(63\right) φ(63) 共有 9 9 9 个因子,分别是 2 0 3 0 , 2 0 3 1 , 2 0 3 2 , 2 1 3 0 , 2 1 3 1 , 2 1 3 2 , 2 2 3 0 , 2 2 3 1 , 2 2 3 2 2^03^0,2^03^1,2^03^2,2^13^0,2^13^1,2^13^2,2^23^0,2^23^1,2^23^2 2030,2031,2032,2130,2131,2132,2230,2231,2232 。对于每一个因子 s = 2 a 3 b s=2^a3^b s=2a3b ,此时,我们只需要计算一下 1 0 s ( m o d   63 ) 10^{s}\left(\mathrm{mod}\ 63\right) 10s(mod 63) ,看谁算出来是 1 1 1

比如这里,最小的一组是 a = 1 , b = 1 a=1,b=1 a=1,b=1 ,即 1 0 2 1 3 1 ≡ 1 ( m o d   63 ) 10^{2^13^1}\equiv 1\left(\mathrm{mod}\ 63\right) 1021311(mod 63) ,那么循环节长度就是 2 1 3 1 = 6 2^13^1=6 2131=6

事实上,这里不需要搜索所有的因子,也就是说复杂度不是 O ( ( a + 1 ) ⋅ ( b + 1 ) ⋅ ( c + 1 ) ) \mathcal{O}\left(\left(a+1\right)\cdot\left(b+1\right)\cdot\left(c+1\right)\right) O((a+1)(b+1)(c+1)) ,而是 O ( ( a + 1 ) + ( b + 1 ) + ( c + 1 ) ) \mathcal{O}\left(\left(a+1\right)+\left(b+1\right)+\left(c+1\right)\right) O((a+1)+(b+1)+(c+1)) ,具体算法将在后面对应部分讲到。

等下,好像欧拉函数 ϕ ( t ) \phi\left(t\right) ϕ(t) 还不知道怎么算呢。别急,这里其实和质因数分解是一体的。

假设有质因数分解 t = p 1 s 1 p 2 s 2 ⋯ p n s n t=p_1^{s_1}p_2^{s_2}\cdots p_n^{s_n} t=p1s1p2s2pnsn ,那么 ϕ ( t ) = t × ( p 1 − 1 p 1 ) ( p 2 − 1 p 2 ) ⋯ ( p n − 1 p n ) \phi\left(t\right)=t\times \left(\frac{p_1-1}{p_1}\right)\left(\frac{p_2-1}{p_2}\right)\cdots \left(\frac{p_n-1}{p_n}\right) ϕ(t)=t×(p1p11)(p2p21)(pnpn1)

大家应该看出来了,这里面最复杂的内容在于质因数分解类,因此,我将这部分内容单独放到了一篇文章中进行——【C/C++无聊练手(二)】将质因数分解封装为类+快速幂求幂模函数+代码正确性单元测试(本文作为下文的基础类),其中顺便实现了快速幂模函数。

简要分析一下复杂度——

  • 质因数分解我就不打算用Pollard-Rho算法这么麻烦的东西了,反正 N N N 不大,直接暴力试除法已经够用了。时间复杂度 O ( N ) \mathcal{O}\left(\sqrt{N}\right) O(N ) ,空间复杂度 O ( log ⁡ N ) \mathcal{O}\left(\log N\right) O(logN)
  • 按照因子搜索复杂度,时间复杂度 O ( log ⁡ N ) \mathcal{O}\left(\log N\right) O(logN) ,空间复杂度 O ( log ⁡ N ) \mathcal{O}\left(\log N\right) O(logN)
  • 纯粹计算 1 0 x ( m o d   n ) 10^x\left(\mathrm{mod}\ n\right) 10x(mod n) 也可以用快速幂实现,我们可以为每个因子提前算好,时间复杂度 O ( log ⁡ N ) \mathcal{O}\left(\log N\right) O(logN) ,空间复杂度 O ( 1 ) \mathcal{O}\left(1\right) O(1)

然后总结一下循环节长度的计算步骤

  • 对于一个数 N = 2 s 1 5 s 2 t N=2^{s_1}5^{s_2}t N=2s15s2t ,其有限小数部分 p = max ⁡ { s 1 ,   s 2 } p=\max\left\{s_1,\ s_2\right\} p=max{s1, s2}
  • 对于 t t t ,计算 ϕ ( t ) \phi\left(t\right) ϕ(t)
  • ϕ ( t ) \phi\left(t\right) ϕ(t) 质因数分解为 ϕ ( t ) = p 1 s 1 p 2 s 2 ⋯ p n s n \phi\left(t\right)=p_1^{s_1}p_2^{s_2}\cdots p_n^{s_n} ϕ(t)=p1s1p2s2pnsn ,令 s = p 1 a 1 p 2 a 2 ⋯ p n a n s=p_1^{a_1}p_2^{a_2}\cdots p_n^{a_n} s=p1a1p2a2pnan ,其中 a 1 ⩽ s 1 , ⋯   , a n ⩽ s n a_1\leqslant s_1, \cdots ,a_n\leqslant s_n a1s1,,ansn 。找出满足 1 0 s ≡ 1 ( m o d   t ) 10^{s}\equiv 1\left(\mathrm{mod}\ t\right) 10s1(mod t) 最小的一组 ( a 1 , ⋯   , a n ) \left(a_1,\cdots ,a_n\right) (a1,,an) ,此时的 s s s 记为所求的 o r d t 10 \mathrm{ord}_{t}10 ordt10

除掉因子2、5

回忆一下,我们要算的数是 1 / N 1/N 1/N ,但实际上是先进行了分解 N = 2 s 1 5 s 2 t N=2^{s_1}5^{s_2}t N=2s15s2t ,得到有限小数部分 p = max ⁡ { s 1 ,   s 2 } p=\max\left\{s_1,\ s_2\right\} p=max{s1, s2} ,真正计算的是 t t t 。所以我们先编写一个函数,输入整数 N N N ,返回 Factor 类的 t t t ,以及小数部分 p p p

// main.cpp(part 1)
/**
 * @brief 设N=(2^s1)(5^s2)t
 *        除掉所有因子2^s1和5^s2,返回引用p=max{s1, s2}
 *        返回Factor t
 */
Factor Mod2and5 (uint32 N, uint32& p) {
    Factor      tmpT(N);
    tmpT.Factorization();

    uint32      counter2 = 0;
    uint32      counter5 = 0;
    uint8       tmpLen   = tmpT.GetLen();
    uint32      tmpPrime;

    if (tmpLen == 0){
        p = 0;
        return tmpT;
    }
    for (uint8 k = 0;k < tmpLen;++k) {
        tmpPrime = tmpT.GetPP(k).GetPrime();
        if (tmpPrime == 2) {
            counter2 = tmpT.GetPP(k).GetPow();
            tmpT.TryDiv(tmpT.GetPP(k));
        }
        if (tmpPrime == 5) {
            counter5 = tmpT.GetPP(k).GetPow();
            tmpT.TryDiv(tmpT.GetPP(k));
        }
    }

    p = counter2 > counter5 ? counter2 : counter5;
    return tmpT;
}

欧拉函数的计算

首先,根据我这篇文章【C/C++无聊练手(二)】将质因数分解封装为类+快速幂求幂模函数+代码正确性单元测试(本文作为下文的基础类)编写好 Factor.hppFactor.cpp ,作为质因数分解的基础。

根据欧拉函数定义式,假设有质因数分解 t = p 1 s 1 p 2 s 2 ⋯ p n s n t=p_1^{s_1}p_2^{s_2}\cdots p_n^{s_n} t=p1s1p2s2pnsn ,那么 ϕ ( t ) = t × ( p 1 − 1 p 1 ) ( p 2 − 1 p 2 ) ⋯ ( p n − 1 p n ) \phi\left(t\right)=t\times \left(\frac{p_1-1}{p_1}\right)\left(\frac{p_2-1}{p_2}\right)\cdots \left(\frac{p_n-1}{p_n}\right) ϕ(t)=t×(p1p11)(p2p21)(pnpn1) ,我们编写如下函数——

// main.cpp(part 2)
/**
 * @brief 计算整数N的EulerPhi(N),返回一个Factor类型
 *        设N=(p1^{s1})*...(pn^{sn})
 *        EulerPhi(N)=(p1^{s1-1})*...(pn^{sn-1})(p1-1)...(pn-1)
 */
Factor EulerPhi (uint32 N) {
    Factor   tmpN(N);
    Factor   tmpMult;       // 空Factor,每次使用时记得手动SetN并Factorization
    PrimePow pp[9];         // 存(p1^1)到(pn^1)

    tmpN.Factorization();
    uint8    tmpLen = tmpN.GetLen();

    // 把每个因子取出来一个,放到pp
    for (uint8 k = 0;k < tmpLen;++k) {
        pp[k] = PrimePow(tmpN.GetFactor(k).GetPrime(), 1);
    }
    // 除掉每个pp
    for (uint8 k = 0;k < tmpLen;++k) {
        tmpN.TryDiv(pp[k]);
    }
    // 乘以每个(pp[k]-1)
    for (uint8 k = 0;k < tmpLen;++k) {
        tmpMult.SetN(pp[k].ToInt() - 1);
        tmpMult.Factorization();
        tmpN.TryMultiple(tmpMult);
    }

    return tmpN;
}

求ord10t

回忆前面所说的——

ϕ ( t ) \phi\left(t\right) ϕ(t) 质因数分解为 ϕ ( t ) = p 1 s 1 p 2 s 2 ⋯ p n s n \phi\left(t\right)=p_1^{s_1}p_2^{s_2}\cdots p_n^{s_n} ϕ(t)=p1s1p2s2pnsn ,令 s = p 1 a 1 p 2 a 2 ⋯ p n a n s=p_1^{a_1}p_2^{a_2}\cdots p_n^{a_n} s=p1a1p2a2pnan ,其中 a 1 ⩽ s 1 , ⋯   , a n ⩽ s n a_1\leqslant s_1, \cdots ,a_n\leqslant s_n a1s1,,ansn 。找出满足 1 0 s ≡ 1 ( m o d   t ) 10^{s}\equiv 1\left(\mathrm{mod}\ t\right) 10s1(mod t) 最小的一组 ( a 1 , ⋯   , a n ) \left(a_1,\cdots ,a_n\right) (a1,,an) ,此时的 s s s 记为所求的 o r d t 10 \mathrm{ord}_{t}10 ordt10

通过递归的方法,我们很容易找出这一组最小的 ( a 1 , a 2 , ⋯ a n ) \left(a_1,a_2,\cdots a_n\right) (a1,a2,an) 。直接看代码的注释和代码本身吧,懒得讲了——

// main.cpp(part 3)
/**
 * @brief 计算ord10t
 *        设EulerPhi(t)=(p1^s1)(p2^s2)...(pn^sn)=s
 *        减少s1,计算10^s(mod t),直到再少后10^s(mod t)不为1为止(记此时为a1)
 *        减少s2到a2,同样的操作,做完就s3,一直到sn
 *        返回(p1^a1)(p2^a2)...(pn^an)
 */
Factor CalOrd10T (Factor ft, uint8 now, uint32 mod) {
    if (now == ft.GetLen()) {   // 算完了
        return ft;
    }
    PrimePow tmpPP(ft.GetPP(now).GetPrime(), 1);    // 准备好打算除以的因子

    ft.TryDiv(tmpPP);                               // ft /= tmpPP
    if (MyPowMod(10, ft.GetN(), mod) == 1) {    // 如果余数为1,那么继续除以自己
        return CalOrd10T(ft, now, mod);
    }
    else {      // 如果余数为不为1,那么就不应该除,赶快乘回去,然后尝试除下一个now
        ft.TryMultiple(tmpPP);
        ++ now;
        return CalOrd10T(ft, now, mod);
    }
}

求公因子

我们不希望 M / N M/N M/N 中的 M M M N N N 有公因子。求公因子的办法可以用辗转相除法。

// main.cpp(part 4)
/**
 * @brief 辗转相除法求公因子,输入可以有0
 */
inline uint32 GCD(uint32 a,uint32 b) {
    return b>0 ? GCD(b, a % b):a;
}

计算用的CalCycleDiv类

和文章【C/C++无聊练手(一)】从「计算1/N的循环节」到「计算M/N的循环节」的理论推导&代码实现很像,不过是按照近世代数理论优化后的实现,所以内容比较模块化。

// main.cpp(part 5)
class CalCycleDiv{
public:
    CalCycleDiv(uint32 N, uint32 M=1);                        // 构造对象,并执行计算

    void displayAsCyclicDecimal ();                     // 显示该分数的循环小数形式
    void displayAllCyclicDecimal();                     // 显示分子+分母+该分数的循环小数形式
    uint32  getM         (){return m_M;}                // 返回m_M
    uint32  getN         (){return m_N;}                // 返回m_N
    uint32  getP         (){return m_loopP;}            // 返回m_loopP
    uint32  getQ         (){return m_loopQ;}            // 返回m_loopQ
    uint32  getLoopLength(){return m_loopQ - m_loopP;}  // 返回(m_loopQ - m_loopP),即循环节长度
    uint8   GetDecimal   (uint32 N){return m_decimal[N];}

private:
    uint32  m_N;             // N
    uint32  m_M;             // M
    uint8  *m_decimal;       // 小数部分
    uint32  m_loopP;         // 循环节范围(m_loopP, m_loopQ]
    uint32  m_loopQ;
};

CalCycleDiv::CalCycleDiv (uint32 N, uint32 M)
    : m_N (N)               // m_N = N; m_M = M;
    , m_M (M) {

    try {
	    if (N == 0)                         // 异常处理
            throw -1;
    }
	catch (int errNum) {
		std::cout << "CalCycleDiv的分母N为0,错误!" << std::endl;
	}

    DWORD time_start = GetTickCount();       // 计时
    
    M = M % N;                              // 只需要计算小数部分
    uint32 tmpGCD = (M == 0 ? N : GCD(N, M));
    M /= tmpGCD;                            // 除掉公因数
    N /= tmpGCD;
    
    // 正式开始计算
    Factor tmpT  = Mod2and5(N, m_loopP);    // N == (2^s1)(5^s2)(t), m_loopP计算完后会被赋值为max{s1, s2}!

    Factor tmpEP = EulerPhi(tmpT.GetN());   // tmpEP = EulerPhi(t)

    m_loopQ      = m_loopP + CalOrd10T(tmpEP, 0, tmpT.GetN()).GetN();

    m_decimal    = new uint8[m_loopQ];      // 小数部分
    
    uint64 tmpM = M;
    uint64 tmpN = N;

    for (uint32 k = 0; k < m_loopQ; ++k) {
        tmpM        *= 10;
        m_decimal[k] = tmpM / tmpN;
        tmpM        %= tmpN;
    }
    
    std::cout << m_M << "/" << m_N << "循环节计算时长:" << GetTickCount() - time_start << "ms" << std::endl;
}

// 显示该分数的循环小数形式
void CalCycleDiv::displayAsCyclicDecimal () {
    std::cout << m_M/m_N << ".";              // 整数部分
    
    for (uint32 i = 0;i < m_loopP;++i)          // 非循环小数部分
        std::cout << (int) m_decimal[i];

    if (m_loopP != m_loopQ) {                 // 循环小数部分
        std::cout << "[";
        for (uint32 i = m_loopP ;i < m_loopQ;++i)
            std::cout << (int) m_decimal[i];
        std::cout << "]";
    }
    std::cout << std::endl;
}

// 显示该分数的循环小数形式
void CalCycleDiv::displayAllCyclicDecimal () {
    std::cout << m_M << " / " << m_N << " == ";
    displayAsCyclicDecimal();
}

测试的main函数

使用 main 函数对结果进行测试,发现历时 5.485s 后, 1000000007 的循环节被计算出,长度为 1000000006 位(因为太长了,没有执行显示函数)

// main.cpp(part 6)
int main() {
                                                        // expected: 1/7循环节计算时长:0ms
    CalCycleDiv(7,  1  ).displayAllCyclicDecimal();     // expected: 1 / 7 == 0.[142857]
                                                        // expected: 29/14循环节计算时长:0ms
    CalCycleDiv(14, 29 ).displayAllCyclicDecimal();     // expected: 29 / 14 == 2.0[714285]
                                                        // expected: 0/14循环节计算时长:0ms
    CalCycleDiv(14, 0  ).displayAllCyclicDecimal();     // expected: 0 / 14 == 0.[0]
                                                        // expected: 0/1循环节计算时长:0ms
    CalCycleDiv(1,  0  ).displayAllCyclicDecimal();     // expected: 0 / 1 == 0.[0]
                                                        // expected: 11/1循环节计算时长:0ms
    CalCycleDiv(1,  11 ).displayAllCyclicDecimal();     // expected: 11 / 1 == 11.[0]
                                                        // expected: 100/5循环节计算时长:0ms
    CalCycleDiv(5,  100).displayAllCyclicDecimal();     // expected: 100 / 5 == 20.[0]
    CalCycleDiv ccd(1000000007, 1);                     // expected: 1/1000000007循环节计算时长:6985ms
    std::cout << ccd.getLoopLength() << std::endl;      // expected: 1000000006
    for (int k = 0; k < 100; ++k) {
        std::cout << (int)ccd.GetDecimal(k);
    }   // expected: 0000000009999999930000000489999996570000024009999831930001176489991764570057648009596463932824752470
    std::cout << std::endl;
    

    getchar();
    return 0;
}

结语

事实上,绝大多数时间都被浪费在那10亿次循环上了,实际上,我们还能进一步对代码进行优化。比如一次循环里不再是 *=10 ,而是 *=1000000000 ,这样刚好不会溢出,还能提升 9 倍的效率。还可以结合快速幂引入多线程的思想,充分利用多核的性能,这里就不再写代码演示了(反正也没人看,写这个本来就是我自娱自乐练手的)。本期练手系列文章到此结束,撒花

附录:子模块的UnitTest

void UnitTest2() {
    uint32 p1  = 0;
    Factor ft1 = Mod2and5(125, p1);
    ft1.DispCalResult();                        // expected: 1 =
    std::cout << p1 << std::endl;               // expected: 3
    ft1 = Mod2and5(250, p1);
    ft1.DispCalResult();                        // expected: 1 =
    std::cout << p1 << std::endl;               // expected: 3
    ft1 = Mod2and5(65535, p1);
    ft1.DispCalResult();                        // expected: 13107 = (3^1)(17^1)(257^1)
    std::cout << p1 << std::endl;               // expected: 1
    ft1 = Mod2and5(20, p1);
    ft1.DispCalResult();                        // expected: 1 =
    std::cout << p1 << std::endl;               // expected: 2
    ft1 = Mod2and5(50, p1);
    ft1.DispCalResult();                        // expected: 1 =
    std::cout << p1 << std::endl;               // expected: 2
    ft1 = Mod2and5(4294967295, p1);
    ft1.DispCalResult();                        // expected: 858993459 = (3^1)(17^1)(257^1)(65537^1)
    std::cout << p1 << std::endl;               // expected: 1
    ft1 = Mod2and5(127, p1);
    ft1.DispCalResult();                        // expected: 127 = (127^1)
    std::cout << p1 << std::endl;               // expected: 0

    EulerPhi(2).DispCalResult();                // expected: 1 =
    EulerPhi(3).DispCalResult();                // expected: 2 = (2^1)
    EulerPhi(4).DispCalResult();                // expected: 2 = (2^1)
    EulerPhi(2*2*2*3).DispCalResult();          // expected: 8 = (2^3)
    EulerPhi(3*3*3).DispCalResult();            // expected: 18 = (2^1)(3^2)
    EulerPhi(2*3*7).DispCalResult();            // expected: 12 = (2^2)(3^1)
    EulerPhi(2*2*3*7).DispCalResult();          // expected: 24 = (2^3)(3^1)
    EulerPhi(65535).DispCalResult();            // expected: 32768 = (2^15)
    EulerPhi(65537).DispCalResult();            // expected: 65536 = (2^16)
    EulerPhi(1000000007).DispCalResult();       // expected: 1000000006 = (2^1)(500000003^1)
    
    Factor ft2(63);
    ft2.Factorization();
    ft2.DispCalResult();                        // expected: 63 = (3^2)(7^1)
    Factor ft3(EulerPhi(ft2.GetN()));
    ft3.Factorization();
    ft3.DispCalResult();                        // expected: 36 = (2^2)(3^2)
    Factor ft4 = CalOrd10T(ft3, 0, ft2.GetN());
    ft4.DispCalResult();                        // expected: 6 = (2^1)(3^1)
    Factor ft5(1000000007);
    ft5.Factorization();
    ft5.DispCalResult();                        // expected: 1000000007 = (1000000007^1)
    Factor ft6(EulerPhi(ft5.GetN()));
    ft6.Factorization();
    ft6.DispCalResult();                        // expected: 1000000006 = (2^1)(500000003^1)
    Factor ft7 = CalOrd10T(ft6, 0, ft5.GetN());
    ft7.DispCalResult();                        // expected: 1000000006 = (2^1)(500000003^1)
    Factor ft8(9);
    ft8.Factorization();
    ft8.DispCalResult();                        // expected: 9 = (3^2)
    Factor ft9(EulerPhi(ft8.GetN()));
    ft9.Factorization();
    ft9.DispCalResult();                        // expected: 6 = (2^1)(3^1)
    Factor ft10 = CalOrd10T(ft9, 0, ft8.GetN());
    ft10.DispCalResult();                       // expected: 1 =
    Factor ft11(1);
    ft11.Factorization();
    ft11.DispCalResult();                       // expected: 1 =
    Factor ft12(EulerPhi(ft11.GetN()));
    ft12.Factorization();
    ft12.DispCalResult();                       // expected: 1 =
    Factor ft13 = CalOrd10T(ft12, 0, ft11.GetN());
    ft13.DispCalResult();                       // expected: 1 =
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值