进制转换
进制转换主要方法有两个,一个是十进制转为别的进制,只要不断求余数然后做除法,直到得到的数为0即可,注意得到的结果是反向的。其他进制转化为十进制则为从低位到高位不断加上对应的数,每次相加后都乘以进制数。代码如下:
//十进制转二进制
int n;
cin>>n;
vector<int> ans;
while(n!=0){
ans.push_back(n%2);
n/=2;
}
//ans中的二进制是反向的
//二进制转十进制
int n=0;
vector<int> binary;
//binary中按从低位到高位(反向)的顺序存储了二进制的值
for(int i=0;i<binary.size();i++){
n+=binary[i];
n*=2;
}
附一道练习题,10进制 VS 2进制
这道题从十进制转化为2进制,又转化为10进制,同时还包含了字符串的加除乘。
#include<iostream>
using namespace std;
string divide(string str,int x){
int remainder=0;
for(int i=0;i<str.length();i++){
int current=str[i]-'0'+remainder*10;
str[i]=current/x+'0';
remainder=current%x;
}
int pos=0;
while(str[pos]=='0'&&str.length()-pos>1)
pos++;
return str.substr(pos);
}
string multiple(string str,int x){
int carry=0;
for(int i=str.length()-1;i>=0;i--){
int current=x*(str[i]-'0')+carry;
str[i]=current%10+'0';
carry=current/10;
}
if(carry!=0)
str=to_string(carry)+str;
return str;
}
string add(string str,int x){
int carry=x;
for(int i=str.length()-1;i>=0;i--){
int current=str[i]-'0'+carry;
str[i]=current%10+'0';
carry=current/10;
}
if(carry!=0)
str="1"+str;
return str;
}
int main(){
string n;
while(cin>>n){
if(n=="0"){
cout<<"0"<<endl;
continue;
}
string ans;
while(n!="0"){
ans.push_back((n[n.length()-1]-'0')%2+'0');
n=divide(n,2);
}
string num="0";
for(int i=0;i<ans.length();i++){
num=multiple(num,2);
num=add(num,ans[i]-'0');
}
cout<<num<<endl;
}
return 0;
}
随题附赠一个字符串的减法,不保证正确,而且不优雅
string myMinus(string str,int x){
int carry=0;
for(int i=str.size()-1;i>=0;i--){
int current=str[i]-'0'-carry;
if(current-x%10<0){
current+=10;
carry=1;
}else
carry=0;
str[i]=current+'0'-x%10;
x/=10;
}
while(str[0]=='0'&&str.length()>1)
str.erase(0,1);
return str;
}
最大公约数与最小公倍数
求最大公约数,原理是a,b的最大公约数与a mod b的最大公约数大小一致,因此可以一直取余直到某一数为0,注意,如果小数%大数,结果仍为小数,会出现死循环的情况,因此需要判断数的大小
#include<iostream>
using namespace std;
int main(){
int a=0,b=0;
while(cin>>a>>b){
while(a!=0&&b!=0){
if(a>b)
a=a%b;
else
b=b%a;
}
cout<<(a==0?b:a)<<endl;
}
}
一种不需要每轮判断大小的方式如下,需要每轮交换a与b的值(Q:如何不借助第三个变量交换两个变量的值)
int main() {
int a,b;
while (scanf("%d%d", &a,&b) != EOF) {
while ( b != 0) {
int temp = a % b;
a=b; b = temp;
}
printf("%d\n", a);
}
return 0;
}
更为常见的递归形式如下,也是交换两个变量的值
int gcd(int a, int b) {
if (a==0||b == 0)return a + b;
else return gcd(b, a%b);
}
求出了最大公约数后,很容易求出两数的最小公倍数,即为两数的乘积除以最大公约数
质数
- 求单个数n是否为素数:
只要判断[2,sqrt(n)]这个范围内的数是否有n的因子即可,值得注意的是,sqrt(n)返回值是double类型,由于精度的原因,可能出现漏掉sqrt(n)这个数的情况 - 素数筛
求出一个范围内的素数,因为每个合数必然存在一个质因子(每个数都可以表示成若干个质数的乘积),因此所求范围内所有质数的非1倍数即为所有合数,代码如下,注意循环开始与结束的条件
Bool num[MAXN]
void init(){
for(int i=0;i<=MAXN;i++)
num[i]=true;
num[0]=false;
num[1]=false;
for(int i=2;i<=MAXN;i++){
if(!num[i])
continue;
if(i*i>MAXN)
continue;
for(int j=i*i;j<=MAXN;j+=i)
num[j]=false;
}
}
素数筛的进一步优化:欧拉筛……
分解质因数
每个数都可以表示成若干个质数的乘积,分解方法即为用[2,sqrt(n)+1]的质数不断试除,因为一个数n最多只有一个大于sqrt(n)的因子,用上述范围的质因子与其不断相除,最终得到的非1的数即为唯一大于sqrt(n)的质因子。
附一个流程:
代码如下:
#include<iostream>
#include<vector>
using namespace std;
#define MAXN 10001
bool num[MAXN];
vector<int> prime;
void init(){
for(int i=0;i<=MAXN;i++)
num[i]=true;
num[0]=false;
num[1]=false;
for(int i=2;i<=MAXN;i++){
if(!num[i])
continue;
prime.push_back(i);
if(i*i>MAXN)
continue;
for(int j=i*i;j<=MAXN;j+=i)
num[j]=false;
}
}
int main(){
init();
int n=0;
while(cin>>n){
int count=0;
for(int i=0;i<prime.size();i++){
if(prime[i]>n)
break;
while(n%prime[i]==0){
n/=prime[i];
count++;
}
}
注意这里更通用的范围MAXN。
快速幂
快速幂是快速求a^b的做法,为此需要将b转化为二进制,从低位到高位依次遍历b的二进制的各位,每遍历一位就求a*=a,如果b的改位为1,则结果ans*=a,具体的原理应该挺好想的。
附一道例题 求root(N, k)
代码如下:
#include<iostream>
using namespace std;
long long fastexp(int x,int y,int k){
int t=1;
x=x%(k-1);
while(y!=0){
if(y%2==1){
t*=x;
t%=k-1;
}
x*=x;
x%=k-1;
y>>=1;
}
return t==0?k-1:t;//想想为什么有这一步
}
int main(){
int x,y,k;
while(cin>>x>>y>>k){
int n=fastexp(x,y,k);
cout<<n<<endl;
}
思路如下:
矩阵与矩阵快速幂
- 矩阵加减乘
- 矩阵快速幂(与上类似)
字符串(长整数)计算
高精度数的计算,但是Java有BigDecimal,这也太不公平了!!!