算法分析: 题意: 输入一个有理数p/q(保证是一个小数),然后将其小数部分用二进制表示。求出在此种表示下的循环起点和循环节长度 分析: 十进制小数转化成二进制方法(点这里) 我们可以观察一下1/10这组数据,按照二进制转换法(乘二法),我们可以得到: 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 ... 出现了重复,那么这个重复就是我们要求的最小循环。 我们来抽象化:给出p/q 1.约分: p/=gcd(p,q) q/=gcd(p,q) 2.根据重复出现的条件:p*2^j%q=p*2^i (i<j,i即为所求点,j-i即为所求循环长度) (p*2^j-p*2^i)%q=0 即q | p*2^i(2^(j-i)-1) 根据约分,p,q互质,gcd(p,q)=1; 2^(j-i)-1为奇数,不可能分出2来与q约分 只有2^i里面有2,只有它能约2, 所以q里面有多少2,就是i的结果。 Q=q约2(约到没有为止) Q | 2^(j-i)-1 (2^(j-i)-1)%Q=0 2^(j-i)≡1(mod Q) 2与Q互质(Q上面的约分2) 欧拉定理:若a与p互质,则a^Φ(p) ≡1 (modp) 所以说2^(j-i)≡1(mod Q)肯定有解,但不一定是最小解 所以我们从小到大枚举Φ(Q)的因子即可。 即通过枚举φ(q)的因子(最小因子)就能得出结果.x=φ(q)一定是一个解,但不一定是最小的解,若x=φ(q)不是最小的解,那么最小的解x一定是φ(q)的因子. 证明:假设x是满足2^x≡1(mod n)的最小的x,假设x不是φ(n)的因子,令r=φ(n)mod x,则r>0&&r<x.由2^φ(n)≡ 1(mod n)和2^x≡1(mod n),可以得2^r mod n=1,则存在一个比x还小的数,使得2^r≡1(mod n),这与假设矛盾,所以x为φ(n)因子。 代码实现:
|
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<cmath>
#include<iostream>
#include<sstream>
#include<iterator>
#include<algorithm>
#include<string>
#include<vector>
#include<set>
#include<map>
#include<stack>
#include<deque>
#include<queue>
#include<list>
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b) { return b == 0 ? a : gcd(b, a % b); }
ll fact[100005];
ll phi(ll n)
{
ll res=n;
for(int i=2;i*i<=n;i++){
if(n%i==0){
res=res-res/i;
do{
n/=i;
}while(n%i==0);
}
}
if(n>1) res=res-res/n;
return res;
}
ll quick_qow(ll a,ll b,ll k)
{
long long res=1;
while(b>0)
{
if(b%2) res=(res*a)%k;
a=(a*a)%k;
b/=2;
}
return res;
}
int k;
int factor(ll x) //找x的因子 (包括1和本身)
{
k=0;
for(int i=1;i*i<=x;i++)
{
if(x%i==0)
{
k++;
fact[k]=i;
k++;
fact[k]=x/i;
}
}
}
int main()
{
int t=0;
ll p,q;
while(scanf("%lld/%lld",&p,&q)!=EOF)
{
t++;
ll g=gcd(p,q);
p/=g;
q/=g;
ll cnt=0;
while(q%2==0) //取q里面的2
{
q>>=1;
cnt++;
}
ll ans=phi(q);
factor(ans);
sort(fact+1,fact+k+1);
int i;
for( i=1;i<=k;i++)
{
if(quick_qow(2,fact[i],q)==1)
break;
}
printf("Case #%d: %lld,%lld\n",t,cnt+1,fact[i]);
}
}