您的位置:首页 > 其它

poj 3358 Period of an Infinite Binary Expansion (费马小定理+分数化二进制小数)

2013-11-27 14:58 531 查看
题意:将一个小于1的小数表示成二进制形式。输出二进制形式下,小数部分第几开始循环,循环节长度是多少?

解析:将一个分数化成小数,转化成二进制后寻找循环节

对于分数P/Q而言,首先化成最简,调整为P'=P/GCD(P,Q) Q'=Q/GCD(P,Q)

我们知道转化成二进制,其实就是不断乘2,如果大于1,则去掉1,当前位为1,否则为0.

表示成分数的时候便是P/Q,2*P/Q如果分子大于分母,则减掉,也相当于取余。

于是我们假设在第I位的时候开始循环,第J位出现重复。

而出现循环反正在分数上便是分母和分子都相同,由于在这里分母是不变的,只考虑分子

那么(P'*2^I)%Q'==(P'*2^J)%Q ;

一个同余式,作 些调整 P'*(2^J-2^I)==0(MOD Q') P'*2^I*(2^(J-I)-1)==0(MOD Q')

变成P'*2^I*(2^(J-I)-1)|Q’

其中P'与Q'互质。那么2^I*(2^(J-I)-1)|Q’

而(2^(J-I)-1是奇数,那么I的值便是Q'里面有多少个2^的幂,第一部分已经解决

假设Q'除掉2的幂之后为Q''

若A与P互质,则A^PHI(P) == 1 (MOD P)

所以2^X ==1 (mod Q'') 必定存在解。

我们要求的是最小的解,则枚举PHI(Q'')的因子,从小开始判断

2^X==1(MOD Q'')

#include <functional>
#include <algorithm>
#include <iostream>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <numeric>
#include <cstring>
#include <cassert>
#include <cstdio>
#include <string>
#include <vector>
#include <bitset>
#include <queue>
#include <stack>
#include <cmath>
#include <ctime>
#include <list>
#include <set>
#include <map>
using namespace std;
typedef long long LL;
template<class T> T gcd(T a,T b)
{
return b?gcd(b,a%b):a;
}
LL exlur(LL x)
{
LL res=1,i;
for(i=2;i*i<=x;i++)
{
if(x%i==0)
{
res*=i-1; x/=i;
while(x%i==0)
{
res*=i; x/=i;
}
}
}
if(x>1) res*=x-1;
return res;
}
LL ppow(LL a,LL n,LL mmod)
{
LL res=1,p=a;
while(n)
{
if(n&1) res=(res*p)%mmod;
p=(p*p)%mmod;
n>>=1;
}
return res;
}
vector<LL> f;
int main()
{
long long  p,q,i,d;
int t=1,len1;
char c;
while(~scanf("%lld/%lld",&p,&q))
{
if(p==0)
{
printf("Case #%d: %d,%d\n",t++,0,0);

continue;
}
d=gcd(p,q);
p/=d;
q/=d;
len1=0;
while(!(q&1))
{
q/=2;
len1++;
}
LL x=exlur(q);
f.clear();
for(i=2;i*i<=x;i++)
{
if(x%i==0)
{
f.push_back(i);
if(i*i==x) break;
f.push_back(x/i);
}
}
f.push_back(x);
sort(f.begin(),f.end());
int j;
LL ans;
for(j=0;j<f.size();j++)
{
if(ppow(2,f[j],q)==1)
{   ans=f[j]; break;}
}

printf("Case #%d: %d,%lld\n",t++,len1+1,ans);

}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: