本文的主要内容是探讨关于小数的一些问题,先介绍小数与分数的转化,然后再来探讨小数循环节等问题。其中涉及
到一些数论的内容,本文会进行详细介绍。
题目:http://acm.hdu.edu.cn/showproblem.php?pid=1717
题意:给定一个纯小数,将其转化为分数输出。
代码:
#include <iostream> #include <string.h> #include <stdio.h> using namespace std; const int N = 15; int gcd(int a, int b) { return b ? gcd(b, a % b) : a; } void DecimalToFraction(char str[]) { int a = 0; int b = 0; int L1 = 0; int L2 = 0; int len = strlen(str); for(int i = 2; i < len; i++) { if(str[i] == '(') break; a = a * 10 + str[i] - '0'; L1++; } bool flag = 0; for(int i = 2; i < len; i++) { if(str[i] == '(' || str[i] == ')') { flag = 1; continue; } b = b * 10 + str[i] - '0'; L2++; } L2 -= L1; int p = b - a; int q = 0; if(!flag) { p = b; q = 1; L2 = 0; } for(int i = 0; i < L2; i++) q = q * 10 + 9; for(int i = 0; i < L1; i++) q = q * 10; int g = gcd(p, q); p /= g; q /= g; printf("%d/%d\n", p, q); } int main() { int T; char str[N]; scanf("%d", &T); while(T--) { scanf("%s", str); DecimalToFraction(str); } return 0; }
题目:http://acm.hdu.edu.cn/showproblem.php?pid=2522
题意:给定一个分数,将其转化为小数。本题需要用到如下定理
代码:
#include <iostream> #include <string.h> #include <stdio.h> using namespace std; const int N = 100005; void FractionToDecimal(int n) { //负数标识 bool isNeg = 0; if(n < 0) { n = -n; isNeg = 1; } int res[N], ok[N]; memset(res, 0, sizeof(res)); memset(ok, 0, sizeof(ok)); int k = 1; ok[k] = 1; int cnt = 0; while(k && n != 1) { k *= 10; res[cnt++] = k / n; k %= n; if(ok[k]) break; ok[k] = 1; } if(isNeg) printf("-"); if(n == 1) puts("1"); else { printf("0."); for(int i = 0; i < cnt; i++) printf("%d", res[i]); puts(""); } } int main() { int T, n; scanf("%d", &T); while(T--) { scanf("%d", &n); FractionToDecimal(n); } return 0; }
题目:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1037
题意:求小于等于的数中倒数循环节长度最长的那个数,其中。
分析:从上题的定理看,我们需要在小于等于的数中找一个数,使得同余方程的最小正
整数解最大。很明显,此方程的最大正整数解为,如果10为模的一个原根,那么将得到方程的最
小正整数解为,这时得到的循环节当然也就最长。
设小于等于且原根为10的最大的素数为。接下来,看似只需要枚举闭区间内的每个数,找
出满足同余方程的最小正整数解为的所有数,然后取循环节长度最大的那个。
但是遗憾的是,枚举的数可能比较多,而且每次枚举都需要解同余方程,时间花费很多。实际上只需要枚举
原根存在的。根据数论原根中的一个定理
根据上题的定理知道,肯定不含有2,所以只需要枚举形如的数即可。
我们要在闭区间内找形如且原根为10的所有。如果原根为10,那么同余方程的最小正整数解
为,也就是小数循环节的长度,在数论欧拉函数章节中,学过。
综上所述,本题思路为:从开始往下枚举,直到为素数且10为模的一个原根时停止,判断是
否形如,如果是,进一步验证是否是的最小正整数解,最终取循环节长度最大
的。
代码:
#include <iostream> #include <stdlib.h> #include <string.h> #include <algorithm> #include <assert.h> #include <stdio.h> #include <math.h> #include <vector> const int Times = 10; const int N = 1000005; using namespace std; typedef long long LL; bool prime[N]; int p[N]; int cnt; LL gcd(LL a, LL b) { return b? gcd(b, a % b) : a; } LL multi(LL a, LL b, LL m) { LL ans = 0; a %= m; while(b) { if(b & 1) { ans = (ans + a) % m; b--; } b >>= 1; a = (a + a) % m; } return ans; } LL quick_mod(LL a, LL b, LL m) { LL ans = 1; a %= m; while(b) { if(b & 1) { ans = multi(ans, a, m); b--; } b >>= 1; a = multi(a, a, m); } return ans; } bool Miller_Rabin(LL n) { if(n == 2) return true; if(n < 2 || !(n & 1)) return false; LL m = n - 1; int k = 0; while((m & 1) == 0) { k++; m >>= 1; } for(int i = 0; i < Times; i++) { LL a = rand() % (n - 1) + 1; LL x = quick_mod(a, m, n); LL y = 0; for(int j = 0; j < k; j++) { y = multi(x, x, n); if(y == 1 && x != 1 && x != n - 1) return false; x = y; } if(y != 1) return false; } return true; } LL pollard_rho(LL n, LL c) { LL i = 1, k = 2; LL x = rand() % (n - 1) + 1; LL y = x; while(true) { i++; x = (multi(x, x, n) + c) % n; LL d = gcd((y - x + n) % n, n); if(1 < d && d < n) return d; if(y == x) return n; if(i == k) { y = x; k <<= 1; } } } void find(LL n, int c, vector<LL> &pri) { if(n == 1) return; if(Miller_Rabin(n)) { pri.push_back(n); return ; } LL p = n; int k = c; while(p >= n) p = pollard_rho(p, c--); find(p, k, pri); find(n / p, k, pri); } void Divide(LL n, vector<LL> &pri, vector<int> &num) { find(n, 120, pri); sort(pri.begin(), pri.end()); num.push_back(1); int k = 1; for(int i = 1; i < pri.size(); i++) { if(pri[i] == pri[i - 1]) ++num[k - 1]; else { num.push_back(1); pri[k++] = pri[i]; } } pri.resize(num.size()); } void SearchFactor(int dept, LL ans, vector<LL> &pri, vector<int> &num, vector<LL> &fac) { if(dept == num.size()) { fac.push_back(ans); return; } for(int i = 0; i <= num[dept]; i++) { SearchFactor(dept + 1, ans, pri, num, fac); ans *= pri[dept]; } } vector<LL> FindFactor(LL n) { int cnt; vector<LL> pri; vector<int> num; vector<LL> fac; Divide(n, pri, num); SearchFactor(0, 1, pri, num, fac); sort(fac.begin(), fac.end()); return fac; } //素数筛选 void PrimeFilter() { cnt = 0; memset(prime, true, sizeof(prime)); for(int i = 2; i < N; i++) { if(prime[i]) { p[cnt++] = i; for(int j = i + i; j < N; j += i) prime[j] = false; } } } bool Judge(LL m, LL &phi, bool &isprime) { // p^1 if(Miller_Rabin(m)) { phi = m - 1; isprime = true; return true; } //p^2 LL srt = (LL)sqrt(1.0 * m); if(srt * srt == m && Miller_Rabin(srt)) { phi = srt * (srt - 1); return true; } //p^3, p^4, ... LL tm = m; LL fm = 1; for(int i = 0; i < cnt; i++) { if(tm % p[i] == 0) { fm = p[i]; while(tm % p[i] == 0) { tm /= p[i]; fm *= p[i]; } if(tm == 1) { phi = fm - fm / p[i]; return true; } break; } } return false; } bool Success(LL m, LL &loop, bool &ok) { LL phi = 0; bool isprime = false; if(!Judge(m, phi, isprime)) return false; LL res = 0; vector<LL> fac = FindFactor(phi); for(int i = 0; i < fac.size(); i++) { if(quick_mod(10, fac[i], m) == 1) { res = fac[i]; break; } } if(res != phi) return false; loop = phi; if(isprime) ok = true; return true; } int main() { LL n; PrimeFilter(); while(scanf("%I64d", &n) != EOF) { n++; bool ok = 0; LL ans = 0; LL res = 0; while(n--) { LL loop = 0; if(Success(n, loop, ok)) { if(ans < loop) { ans = loop; res = n; } if(ok) { printf("%I64d\n", res); break; } } } } return 0; }