题意:
输入一个有理数p/q(保证是一个小数),然后将其小数部分用二进制表示。求出在此种表示下的循环起点和循环节长度
比如1/10,化为二进制就是
0.000110011
起点就是小数点后第二位,循环节是0011,长度为4
解法:
比如1/10,用乘二法,2/10,4/10,8/10,6/10,2/10
其中1*2^1=1*2^5(mod10)
用p代表分子,q代表分母,i代表一个位置,j代表一个位置,比如在1/10中,i为1,j为5,一下的p和q均为最简式
那么我们要求的就是
p*2^i=p*2^j(modq)
化简得2^(j-i)=1(modq)
观察得q能被2整除多少次,i就是多少,而且i就是循环开始位置的前一位,然后将q不断除以2直到不能整除
求出i后,另k为j-i
那么所求为
2^k=1(modq)
用欧拉定理就可解决上式
#include<algorithm>
#include<iostream>
#include<string>
#include<cstdio>
using namespace std;
long long Mulmod(const long long &a,long long b,const long long &n)//a*b%n
{
long long res=0,tmp=a%n;
while(b>0)
{
if (b&1)
if ((res+=tmp)>n) res-=n;
if ((tmp<<=1)>n) tmp-=n;
b>>=1;
}
return res;
}
long long Pow(long long a,long long d,const long long &n)//a^d%n
{
long long res=1;
while (d>0)
{
if (d&1) res=Mulmod(res,a,n);
a=Mulmod(a,a,n);
d>>=1;
}
return res;
}
long long euler(long long n)//求n的欧拉函数
{
long long ans=1,i;
for(i=2;i*i<=n;i++)
if (n%i==0)
{
ans*=i-1; n/=i;
while(n%i==0)
{ ans*=i; n/=i; }
}
if (n>1) ans*=n-1;
return ans;
}
long long gcd(long long a,long long b)//求a,b的最大公约数
{
if(b==0)
{
return a;
}
return gcd(b,a%b);
}
int numOfYinzi;
int yinzi[100000];//0,1,,,numOfYinzi-1
void divide(int n)//将n分解质因子存在yinzi数组中
{
for(int i=2;i*i<=n;i++)
{
if(n%i==0)
{
yinzi[numOfYinzi++]=i;
yinzi[numOfYinzi++]=n/i;
}
}
yinzi[numOfYinzi++]=n;
}
int main()
{
//freopen("in.txt","r",stdin);
string input;
long long counter=1;
long long p,q;
while(scanf("%lld/%lld",&p,&q)!=-1)
{
numOfYinzi=0;
long long g=gcd(p,q);
p/=g;
q/=g;
//化简为最简式
long long i=0,j;//i为起始位置的前一位置
while(!(q&1))//q能被2整除
{
q/=2;
i++;
}
long long eulerQ=euler(q);//q的欧拉函数
long long k;
divide(eulerQ);
sort(yinzi,yinzi+numOfYinzi);
for(k=0;k<numOfYinzi;k++)
{
if(Pow(2,yinzi[k],q)==1)
{
break;
}
}
cout<<"Case #"<<counter++<<": "<<i+1<<','<<yinzi[k]<<endl;
}
return 0;
}