京东2017校招笔试编程题2:进制转换、辗转相除法
2016-09-06 10:32
246 查看
如下一些内容大部分来自于维基百科:
辗转相除法,又被称为欧几里德(Euclidean)算法, 是求最大公约数的算法。辗转相除法首次出现于欧几里得的《几何原本》(第VII卷,命题i和ii)中,而在中国则可以追溯至东汉出现的《九章算术》。
两个数的最大公约数是指能同时整除它们的最大正整数。辗转相除法的基本原理是:两个数的最大公约数等于它们中较小的数和两数之差的最大公约数。例如,252和105的最大公约数是21(252 = 21 × 12;105 = 21 × 5);因为252 − 105 = 147,所以147和105的最大公约数也是21。在这个过程中,较大的数缩小了,所以继续进行同样的计算可以不断缩小这两个数直至其中一个变成零。这时,所剩下的还没有变成零的数就是两数的最大公约数。由辗转相除法也可以推出,两数的最大公约数可以用两数的整数倍相加来表示,如21
= 5 × 105 + (−2) × 252。这个重要的等式叫做贝祖等式。
辗转相除法法原先只用来处理自然数,但在19世纪,辗转相除法被推广至其他类型的数,如高斯整数和一元多项式。另外,还被用来解决丢番图方程和构造连分数等。
两个数a,b的最大公约数记为GCD(a,b)。a,b的最大公约数是两个数的公共素因子的乘积。如462可以分解成2 × 3 × 7 × 11;1071可以分解成3 × 3 × 7 × 17。462和1071的最大公约数等于它们共有的素因数的乘积3 × 7 = 21。如果两数没有公共的素因数,那么它们的最大公约数是1,也即这两个数互素,即GCD(a,b)=1。另g=GCD(a,b),则a=mg, b=ng,其中m,n均为正整数。由上述分析可知,m,n互素。因为m,n没有公共素因子,GCD(m,n)=1。
辗转相除法是一种递归算法。设k表示步骤数(从0开始计数),算法的计算过程如下。每一步的输入是都是前两次计算的余数rk−1和rk−2。因为每一步计算出的余数都在不断减小,所以,rk−1小于rk−2。在第k步中,算法计算出满足以下等式的商qk和余数 rk:
rk−2 = qk rk−1 + rk其中rk < rk−1。也就是rk−2要不断减去rk−1直到比rk−1小。
在第一步计算时(k = 0),设r−2和r−1分别等于a和b,第2步(此时k = 1)时计算r−1(即b)和r0(第一步计算产生的余数)相除产生的商和余数,以此类推。整个算法可以用如下等式表示:
a = q0 b + r0b = q1 r0 + r1r0 = q2 r1 + r2r1 = q3 r2 + r3…如果输入参数a小于b,则第一步计算的结果是交换两个变量的值:因为a < b,所以a和b相除得到的商q0等于0,余数r0等于a。所以在运算的每一步中得出的余数一定小于上一步计算的余数(rk一定小于rk−1)。由于每一步的余数都在减小并且不为负数,必然存在第N步时rN等于0,使算法终止,rN−1 就是a和b的最大公约数。其中N不可能无穷大,因为在r0和0之间只有有限个自然数。
辗转相除法的正确性可以用两步来证明。首先,算法的最终结果rN−1同时整除a和b。因为它是一个公约数,所以必然小于或者等于最大公约数g。然后,任何a和b的公约数都能整除rN−1。所以g一定小于或等于rN−1。两个不等式只在rN−1 = g是同时成立。
算法实现:
递归:
实例:
求一个数X按照2到X-1进制表达,各个位数之和的平均值是多少?所有计算基于十进制进行,结果也用十进制表示为不可约简的分数形式。例如,输入5,求出2、3、4进制的5,将各进制的数每个位加起来,除以3,并且约到最简形式,其中我们用到了辗转相除法,输出7/3.
#include<stdio.h>
#include<stdlib.h>
#include<vector>
#include<iostream>
using namespace std;
int gcd(int a, int b){
if (a < b)
swap(a, b);
if (b == 0)
return a;
else return gcd(b, a % b);
}
int convert(int u, int v){ //数u,进制v
int p = 0;
vector<int> vec;
while(u){
vec.push_back(u % v);
u /= v;
}
for(int i = vec.size() - 1; i >= 0; --i){
p += vec[i];
}
return p;
}
int main(){
int A;
cin>>A;
int sum = 0;
for(int i = 2; i < A; ++i){
sum += convert(A,i);
}
int k = A - 2;
if(sum % k == 0) return sum / k;
else{
int g = gcd(sum,k);
int p = sum / g, q = k / g;
std::cout<<p<<'/'<<q;
}
system("pause");
return 0;
}
辗转相除法,又被称为欧几里德(Euclidean)算法, 是求最大公约数的算法。辗转相除法首次出现于欧几里得的《几何原本》(第VII卷,命题i和ii)中,而在中国则可以追溯至东汉出现的《九章算术》。
两个数的最大公约数是指能同时整除它们的最大正整数。辗转相除法的基本原理是:两个数的最大公约数等于它们中较小的数和两数之差的最大公约数。例如,252和105的最大公约数是21(252 = 21 × 12;105 = 21 × 5);因为252 − 105 = 147,所以147和105的最大公约数也是21。在这个过程中,较大的数缩小了,所以继续进行同样的计算可以不断缩小这两个数直至其中一个变成零。这时,所剩下的还没有变成零的数就是两数的最大公约数。由辗转相除法也可以推出,两数的最大公约数可以用两数的整数倍相加来表示,如21
= 5 × 105 + (−2) × 252。这个重要的等式叫做贝祖等式。
辗转相除法法原先只用来处理自然数,但在19世纪,辗转相除法被推广至其他类型的数,如高斯整数和一元多项式。另外,还被用来解决丢番图方程和构造连分数等。
两个数a,b的最大公约数记为GCD(a,b)。a,b的最大公约数是两个数的公共素因子的乘积。如462可以分解成2 × 3 × 7 × 11;1071可以分解成3 × 3 × 7 × 17。462和1071的最大公约数等于它们共有的素因数的乘积3 × 7 = 21。如果两数没有公共的素因数,那么它们的最大公约数是1,也即这两个数互素,即GCD(a,b)=1。另g=GCD(a,b),则a=mg, b=ng,其中m,n均为正整数。由上述分析可知,m,n互素。因为m,n没有公共素因子,GCD(m,n)=1。
辗转相除法是一种递归算法。设k表示步骤数(从0开始计数),算法的计算过程如下。每一步的输入是都是前两次计算的余数rk−1和rk−2。因为每一步计算出的余数都在不断减小,所以,rk−1小于rk−2。在第k步中,算法计算出满足以下等式的商qk和余数 rk:
rk−2 = qk rk−1 + rk其中rk < rk−1。也就是rk−2要不断减去rk−1直到比rk−1小。
在第一步计算时(k = 0),设r−2和r−1分别等于a和b,第2步(此时k = 1)时计算r−1(即b)和r0(第一步计算产生的余数)相除产生的商和余数,以此类推。整个算法可以用如下等式表示:
a = q0 b + r0b = q1 r0 + r1r0 = q2 r1 + r2r1 = q3 r2 + r3…如果输入参数a小于b,则第一步计算的结果是交换两个变量的值:因为a < b,所以a和b相除得到的商q0等于0,余数r0等于a。所以在运算的每一步中得出的余数一定小于上一步计算的余数(rk一定小于rk−1)。由于每一步的余数都在减小并且不为负数,必然存在第N步时rN等于0,使算法终止,rN−1 就是a和b的最大公约数。其中N不可能无穷大,因为在r0和0之间只有有限个自然数。
辗转相除法的正确性可以用两步来证明。首先,算法的最终结果rN−1同时整除a和b。因为它是一个公约数,所以必然小于或者等于最大公约数g。然后,任何a和b的公约数都能整除rN−1。所以g一定小于或等于rN−1。两个不等式只在rN−1 = g是同时成立。
算法实现:
递归:
function gcd(a, b) if a<b swap(a,b); if b==0 then return a; else return gcd(b, a mod b); end循环:
function gcd(a,b) if a<b then swap(a,b); while(b!=0) { c = a mod b; a = b; b = c; } return a; end
实例:
求一个数X按照2到X-1进制表达,各个位数之和的平均值是多少?所有计算基于十进制进行,结果也用十进制表示为不可约简的分数形式。例如,输入5,求出2、3、4进制的5,将各进制的数每个位加起来,除以3,并且约到最简形式,其中我们用到了辗转相除法,输出7/3.
#include<stdio.h>
#include<stdlib.h>
#include<vector>
#include<iostream>
using namespace std;
int gcd(int a, int b){
if (a < b)
swap(a, b);
if (b == 0)
return a;
else return gcd(b, a % b);
}
int convert(int u, int v){ //数u,进制v
int p = 0;
vector<int> vec;
while(u){
vec.push_back(u % v);
u /= v;
}
for(int i = vec.size() - 1; i >= 0; --i){
p += vec[i];
}
return p;
}
int main(){
int A;
cin>>A;
int sum = 0;
for(int i = 2; i < A; ++i){
sum += convert(A,i);
}
int k = A - 2;
if(sum % k == 0) return sum / k;
else{
int g = gcd(sum,k);
int p = sum / g, q = k / g;
std::cout<<p<<'/'<<q;
}
system("pause");
return 0;
}
相关文章推荐
- 2017京东校招笔试编程题:进制转换、辗转相除
- 2017京东校招笔试编程题--python
- 京东2017校招笔试编程题iOS开发工程师
- 【真题】京东2017校招编程题 进制均值
- 2017京东在线笔试编程题之 买东西
- 采购单-京东2017在线笔试编程题C++
- 网易2017校招内推笔试练习卷编程题2--出专辑
- 去哪儿网2017校招在线笔试(前端工程师)编程题及JavaScript代码
- 京东2017校招编程题
- 【牛客网】2017-IT校招全国统一模拟笔试(秋招备战专场) 编程题 --组队竞赛
- 幸运数-京东2017在线笔试编程题
- 京东2017实习生招聘在线笔试编程题
- 2017腾讯校招笔试编程题-拼凑零钱
- 京东2017实习生笔试编程题-终结者C
- 京东校招笔试(C++方向)编程题
- 2016年9月5号京东校招笔试编程题
- 2017校招 360 笔试题 编程题 内存管理
- 京东2017实习生招聘——在线笔试编程题总结
- 2017京东笔试编程题-保卫方案 | Codeforces Beta Round #5 E.Bindian Signalizing
- 2017 京东校招编程题 进制转换