题意:
输入一个有理数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; }