POJ 3358 Period of an Infinite Binary Expansion【Euler関数】

3236 ワード

タイトルリンク:
http://poj.org/problem?id=3358
テーマ:
分数形式p/qの有理数を入力し、{x}をその有理数バイナリ形式の小数部とし、{x}にループを持たせる
性,{x}= 0.A1A2A3…Ar(Ar+1Ar+2…Ar+s)^w.ループはr+1ビットから始まり,ループ節はsである.
現在、x 1=A 1 A 2 A 3…Arを{x}のループプレフィックスと呼び、x 2=Ar+1 Ar+2…Ar+sを{x}のループ部分と呼ぶ.
ループ接頭辞の長さとループ部分の長さをできるだけ小さくします.最小の循環部分の開始位置と最小を求めます
を行ないます.
たとえば、1/10=0.000110010011(0010010011)^w、0001100110011は1/10のループプレフィックスです.
0010011は1/10の循環部分である.でも一番小さいわけではありません.1/10=0.0(0011)^wなので、0は1/10です
最小ループ接頭辞、0011は1/10の最小ループ部分である.答えは:1/10の最小サイクル部分は2位から、最小
サイクル長は4です.
考え方:
小数を2進数に変換するのは毎回2を掛けて、1より大きい整数の部分を減らして(減らないで、引き続き2を掛けます)、小数の部分だけを残します.
このデータの1/10を観察します.小数表現は煩雑すぎて、ここでは点数でバイナリの変換を実現して、法則を観察します.
1/10を2乗法で得る.
1/10 2/10 4/10 8/10 16/10 32/10 …
そして、各分子はできるだけ10を減算して、次のようになります.
1/10 2/10 4/10 8/10  6/10    2/10 …
2位からの繰返しが見られたが,繰返しのサイクル節は4であり,実は最小サイクル長である.
バイナリであることから,1*2^1=1*2^5(mod 10)が分かる.
今p/qを考えます.p 1=p/gcd(p,q),q 1=q/gcd(p,q)とする.バイナリなのでp 1*2^i≡p 1*2^j
(mod q1).変換により、p 1*2^i*(2^(j−i)−1)≡0(mod q 1)が得られる.
p 1とq 1は互いに質的であるため、2^i* (2^(j-i) - 1) % q1 = 0.2^(j-i)-1は奇数、2^iは偶数、2^i
すべてはq 1に由来し,q 1にどれだけの2のべき乗があるか,iはどれだけであり,ループ開始位置の上位でもある.
q 1中の2^iを簡略化して,(2^(j-i)-1)を得る. % q1 = 0.x=j-iとすると,2^x≡1(mod q 1)を求める.
のx最小はいくらです
q 1と2は互いに(すでに2が消えている)ので,必ず解がある.オラの定理による2^φ(q1) ≡ 1(mod q1).でも
φ(q 1)必ずしも最小の値ではないが、必ずφ(q 1)の約数.列挙φ(q 1)の約数で、最小の値が見つかります.
ACコード:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;

int GCD(int a,int b)
{
    if(b == 0)
        return a;
    return GCD(b,a%b);
}

int MultiPower(int a,int b,int m)   //a^b % m
{
    int ans = 1;
    while(b > 0)
    {
        if(b&1)
            ans = (__int64)ans * a % m; //ans = ans * a % m      。
        a = (__int64)a * a % m;
        b >>= 1;
    }
    return ans;
}

int Euler(int n)        //     φ(q1)
{
    int ret = n;
    for(int i = 2; i*i <= n; ++i)
    {
        if(n%i == 0)
        {
            n /= i;
            ret = ret - ret/i;
        }
        while(n % i == 0)
            n /= i;
    }
    if(n > 1)
        ret = ret - ret/n;
    return ret;
}

int factor[1010000];

int main()
{
    int kase = 0,p,q;
    while(~scanf("%d/%d",&p,&q))
    {
        int gcd = GCD(p,q);
        p /= gcd;
        q /= gcd;
        int time = 1;
        while(q%2 == 0)
        {
            q >>= 1;
            time++;
        }
        int phi = Euler(q);
        int ans = 0;
        if(phi == 1)
            ans = 1;
        else
        {
            int id = 0;
            for(int i = 1; i*i <= phi; ++i)     //  φ(q1)
            {
                if(phi % i == 0)
                {
                    factor[id++] = i;
                    factor[id++] = phi/i;
                }
            }
            sort(factor,factor+id);
            for(int i = 0; i < id; ++i)     //      
            {
                if(MultiPower(2,factor[i],q) == 1)
                {
                    ans = factor[i];
                    break;
                }
            }
        }

        printf("Case #%d: %d,%d
",++kase,time,ans); } return 0; }