输入n(10 <= n <= 1000)
输出<=n的数中倒数循环节长度最长的那个数
10
7
题目要求就是求<=n( n∈[10,1000] )的数中倒数循环节长度最长的那个数。
这就意味着我们要求出所有数的倒数的循环节长度(打表),然后逐一比较得出最长的那个数。
怎样打表?那我们就要参考康明昌老师一篇paper所给出的定理来分析一下。
把1/7的循环节142 857轮换排列:如果从2出发,得285 714,这是2/7的循环节;如果从4出发,得428 571,这是3/7的循环节。以此类推。
不知大家是否已经发现规律:(可以多举几组例子,这里我就只拿7举例了哈)
1.对于无限循环的小数,循环节长度只与分母有关?且长度小于分母?
2.对于同分母真分数的循环小数来说,循环节里出现的数字都是一样的,只是有不同的排列组合?
要得证 我们首先得来看一个定理:(≡ 为同余符号)
定理1: 如果 1 ≤ b < a, a 沒有2或5的質因數, 並且 a 與 b 互質,
那麼 b/a 的循環節位數恰好等於e: min{ e ∈ N : 10^e ≡ 1 (mod a) }。
证明规律1:
由上面给出的定理可知,对于分数b/a,循环节位数 e = min{ e ∈ N : 10^e ≡ 1 (mod a) }.
即循环节长度e只由分母a决定;
又由费马小定理可知,对质数p(除了2、5。10是它们的倍数)有10^(p-1) mod p=1。
显然循环节长度最大为 p-1. 即循环节长度e必然小于分母p。
证明规律2:
因为: 1/7 = 0.142 857……
由定理1得: 10*(1/7)= 1.428 571……
因此: 3/7 = [10/7] = 0.428 571……
同理,考虑 [30/7] = 2/7,[20/7] = 6/7,[60/7] =4/7,[40/7] = 5/7,[50/7] = 1/7。得证!
3.另外我们要思考的是为什么定理中指出没有 2或5?
因为1/2=0.5、1/5=0.2,这些有限小数对循环节长度是不影响的。
举个例子:73/35 = 73/(5*7) = (2*73)/(10*7) = (1/10)·(146/7) = (1/10)·{20 + (6/7)}
因此, 对于分数73/35,其的循环节与 6/7 的循环节是一样的,都是 867142 …… 是不是很神奇
#include
#include
#include
#define INF 0x3f3f3f3f
#define NMAX 1001
using namespace std;
int n, res[NMAX];
void init(){ //打表 定理:min{e|10^e ≡ 1(mod n),e∈N+}
memset(res, -1, sizeof(res));
res[1]=0;
for (int i = 2; i < NMAX; ++i) { //分母 只有分母影响循环节
int t = i;
while (t%5==0) //约去因子2和5 为有限小数
t/=5;
while (t%2==0)
t/=2;
if (res[t] != -1) { //已算过
res[i] = res[t];
continue;
}
//未算过 即质数
//其实本质只需要计算所有分母为质数的循环节 质数的倍数的数(分母)的循环节位数===这个质数(分母)的循环节位数
int x = 10;
for (int j = 1; j <= i; ++j) { //循环节位数 必小于分母
x %= i; //同定理 无影响(同余定理)
if (x==1){
res[i] = j;
break;
}
x*=10;
}
}
}
int main(){
init();
while (cin>>n){
int ans = 7; //n>=10
for (int i = 11; i <= n; ++i) {
if (res[ans] <= res[i]) {
ans = i; //取max
}
}
cout<
有趣的小数我们就先讨论到这,更多关于循环节的定理知识大家可以去看循环节_维基百科。里面有相关定理证明以及更深入的探讨。
Over~