3.8 noip总结

高精度算法

1.什么是高精度

高精度算法,属于处理大数字的数学计算方法。在一般的科学计算中,会经常算到小数点后几百位或者更多,当然也可能是几千亿几百亿的大数字。一般这类数字我们统称为高精度数,高精度算法是用计算机对于超大数据的一种模拟加,减,乘,除,乘方,阶乘,开方等运算。对于非常庞大的数字无法在计算机中正常存储,于是,我们可以将这个数字拆开,拆成一位一位的,或者是几位几位的存储到一个数组中, 用一个数组去表示一个数字,这样这个数字就被称为是高精度数。高精度算法就是能处理高精度数各种运算的算法,但又因其特殊性,故从普通数的算法中分离,自成一家。

对于这类问题,不要指望long double这些东西了,基本数据类型不可能存的下。我们可以把这两个数当成字符串输入到数组中,然后模拟手动的竖式运算(不会的话,回去上小学)得出结果。

说白了,高精度计算就是解决long long也解决不了的问题。

2.高精度的作用

正如上面所说的,高精度的作用就是对于一些异常之大的数字进行加减乘除乘方阶乘开方等运算。比如给你一道a+b的题目,读入a和b,让你输出它们的和,但a和b的范围都是小于等于10的6666次方,这个时候你就只能用高精度了。

3.高精度读入处理数据

当一个数据很大的时候,我们用一个整数类型是存不下的,所以我们可以先用一个字符串输入,这样就可以输入很长的数,然后再利用字符串函数和操作运算,将每一位数取出,存入一个数组里,我们用数组里的每一位表示这个数的每一个数位。

例如:998244353用数组储存下来,a{3,5,3,4,4,2,8,9,9},一般是倒着存(从低位到高位,因为整数没有除个位以下的数位,但你的最高位还可以进位,那么你就又要开一个位置来存这个新的最高位)。

高精度读入Code

char s[6666];
int a[6666];
 
int main(){
    scanf("%s",s+1);//用字符串读入
    len=strlen(s+1);//这个数的长度为len
    for(int i=1;i<=len;i++){
        a[i]=s[len-i+1]-'0';//倒叙储存,每一位存一个数
    }
    return 0;
}

高精度输出Code 

int a[6666]
 
void write(int a[]){
    for(int i=lena;i>0;i--){
        printf("%d",a[i]);//一位一位输出这个数
    }
}

4.高精度比较大小

处理完数据之后,假设我们要比较两个数那个大怎么办呢?

我们先模拟比较1314520和1314530,首先我们看两个数的长度(即len1和len2)如果哪个数的len更长一点,那么这个数肯定要比另一个数大,否则我们才继续比较下去,这里两个数的长度是一样的,所以接下来我们就看这两个数的最高位(即1和1),相等,则继续比较下一位(3和3),也一样,继续比较下一位......直到比到十位的时候(2和3),因为2<3,所以第一个数<第二个数,直接退出。

所以,高精度比较大小的步骤大致如下:

1、比较两个数的长度,长度更长的数越大。

2、如果两个数长度相等,那么就从高位到低位一位一位比较,如果某一位数字不同时,较大的数大。否则继续比较下一位。

3、如果比到最后都没有比出谁大谁小,就说明这两个数一样大。

高精度比较大小Code

//比较a和b的大小,如果a>b则返回真,否则返回假
int a[6666],b[6666];
 
int compare(){
    if(lena>lenb) return 1;//lena表示a这个数的长度,lenb则表示b的长度
    if(lenb>lena) return 0;//步骤1
    for(int i=lena;i>0;i--){//从高位到底位一位一位比较
        if(a[i]>b[i]) return 1;
        if(b[i]>a[i]) return 0;
    }//步骤2
    return 0;//步骤3,a=b,即a不大于b
}

5.高精度处理进位与借位

一、那么我们怎么处理进位呢?

其实也很简单,我们再来模拟一下(模拟大法好,不会也得会),1439+887的时候,首先我们最低位相加,得到16,那么答案最低位就是6,再进个1,然后两数的十位相加,3+8=11,然后再加上进位的1,就是11+1=12,所以答案十位就是2,再进1,4+8+1=13,答案百位是3,进1,1+0+1=2,答案千位是2。所以结果就是6232!哦,不对反了,是2326,呵呵,这里要注意一下,输出的时候是倒着输出的,千万不要忘了。

总结一下,进位的步骤大致如下:

1、将当前位置的数相加,当前位的结果就是当前结果除以10的余数。

2、再讲当前结果除以10加到高位,表示进位。

注意:有同学可能会有疑问,为什么一定要倒着储存这个数呢?顺着存不是更好吗?这里我举一个非常简单的例子,比如10+90,谁都知道答案是100,那么我们来看看顺着储存和倒着储存有什么区别:

1.顺着存:a={1,0},b={9,0},当a的最高位(即数组第1位)加上b的最高位(即数组第2位)时我们是不是得进位?!?但是a的最高位是数组的第一位,那么我们要进位到a数组的第几个位置呢?第0个位置?太复杂了。还是存在第一个位置并把所有的数往数组右边移动一位?那更不行,时间复杂度又太高,所以不好办。

2.倒着存:a={0,1},b={0,9},当a的最高位(即数组第2位)加上b的最高位(即数组第2位)时我们同样要进位,这个时候就好办了,直接进位到数组的第三位就好了,所以答案的数组就是{0,0,1},倒过来输出答案100。

高精度进位Code

int a[6666],b[6666],c[6666];
int lena,lenb,lenc;
 
int main(){
    lenc=max(lena,lenb);
    for(int i=1;i<=lenc;i++){
        c[i]=c[i]+a[i]+b[i];
        c[i+1]=c[i]/10;
        c[i]=c[i]%10;
    }
    while(c[lenc+1]>0) lenc++;//答案的长度有时还会增加
}

 二、接下来讲讲当减法的时候如何借位

1、将当前位置的数向减。

2、如果结果大于或等于0就直接作为当前位的答案。

3、否则将结果加10作为当前位的答案,在将高位的数-1即可。

高精度借位Code

int a[6666],b[6666],c[6666];
int lena,lenb,lenc;
 
int main(){
    lenc=max(lena,lenb);
    for(int i=1;i<=lenc;i++){
        c[i]=c[i]+a[i]-b[i];
        if(c[i]<0) c[i]=c[i]+10,c[i+1]=-1;
    }
    while(c[lenc]==0&&lenc>1) lenc--;//细节,如果a-b结果为0,那么也要输出一个0
}

6.高精度加法

 至此,就可以进行任意你想进行的运算了,首先我们来看看加法,其实上面的代码已经差不多写出来了。

高精度加法Code

#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
using namespace std;
int a[6666],b[6666],c[6666];
int lena,lenb,lenc;
char s1[6666],s2[6666];
 
int main(){
    scanf("%s %s",s1+1,s2+1);
    lena=strlen(s1+1);
    lenb=strlen(s2+1);
    for(int i=1;i<=lena;i++) a[i]=s1[lena-i+1]-'0';
    for(int i=1;i<=lenb;i++) b[i]=s2[lenb-i+1]-'0';
    lenc=max(lena,lenb);
    for(int i=1;i<=lenc;i++){
        c[i]=c[i]+a[i]+b[i];
        c[i+1]=c[i]/10;
        c[i]=c[i]%10;
    }
    while(c[lenc+1]>0) lenc++;
    for(int i=lenc;i>0;i--) printf("%d",c[i]);
}

7.高精度减法

高精度减法Code

#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
using namespace std;
int a[6666],b[6666],c[6666];
int lena,lenb,lenc;
char s1[6666],s2[6666];
 
int main(){
    scanf("%s %s",s1+1,s2+1);
    lena=strlen(s1+1);
    lenb=strlen(s2+1);
    if(lenb>lena||(lena==lenb&&s2>s1)){//如果第二个数比第一个数大,那么结果是负数
        printf("-");
        swap(s1,s2);//swap是C++自带函数可以直接调用
        swap(lena,lenb);//别忘了交换长度
    }
    for(int i=1;i<=lena;i++) a[i]=s1[lena-i+1]-'0';
    for(int i=1;i<=lenb;i++) b[i]=s2[lenb-i+1]-'0';
    lenc=max(lena,lenb);
    for(int i=1;i<=lenc;i++){
        c[i]=c[i]+a[i]-b[i];
        if(c[i]<0) c[i]=c[i]+10,c[i+1]=-1;
    }
    while(c[lenc]==0&&lenc>1) lenc--;
    for(int i=lenc;i>0;i--) printf("%d",c[i]);
}

8.高精度乘法

1.高精度乘以单精度

高精度乘以单精度Code

#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
using namespace std;
int a[6666],b;
int lena;
char s1[6666];
 
int main(){
    scanf("%s %d",s1+1,&b);
    lena=strlen(s1+1);
    for(int i=1;i<=lena;i++) a[i]=s1[lena-i+1]-'0';
    for(int i=1;i<=lena;i++) a[i]*=b;
    for(int i=1;i<=lena;i++){
        a[i+1]+=a[i]/10;
        a[i]%=10;
    }
    while(a[lena+1]>0) lena++;
    for(int i=lena;i>0;i--) printf("%d",a[i]);
}

2.高精度乘以高精度

高精度乘以高精度Code

#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
using namespace std;
int a[6666],b[6666],c[6666];
int lena,lenb,lenc;
char s1[6666],s2[6666];
 
int main(){
    scanf("%s %s",s1+1,s2+1);
    lena=strlen(s1+1);
    lenb=strlen(s2+1);
    for(int i=1;i<=lena;i++) a[i]=s1[lena-i+1]-'0';
    for(int i=1;i<=lenb;i++) b[i]=s2[lenb-i+1]-'0';
    lenc=lena+lenb-1;
    for(int i=1;i<=lena;i++){
        for(int j=1;j<=lenb;j++){
            c[i+j-1]+=a[i]*b[j];
            c[i+j]+=c[i+j-1]/10;
            c[i+j-1]%=10;
        }
    }
    while(c[lenc+1]>0) lenc++;
    for(int i=lenc;i>0;i--) printf("%d",c[i]);
}

求最大公约数、最小公倍数。

可以用自带函数求最大公约数:

__gcd(a,b);

推荐使用手写最大公因数:

int gcd(int a,int b)
{
    if (b==0) return a;
    else return gcd(b,a%b);
}

最小公倍数的话,根据两个数相乘等于他们的gcd*lcm即可求得。

快速幂

先上代码

typedef long long ll;
ll quick_pow(ll x,ll n,ll m){
    ll res = 1;
    while(n > 0){
        if(n & 1)   res = res * x % m;
        x = x * x % m;
        n >>= 1;//相当于n=n/2.详情请参考位移运算符。
    }
    return res;
} 

假设要求x^n,如果n = 2^k,那么原题可以很轻松的表示为:x^n = ((x^2)^2)^2…。这样只要做k次平方运算就能解决,时间复杂度就从O(n)下降到log(n)。

    由上面的分析可知,只要幂运算的幂可以写成2^k的形式,就可以用上面的方法降低时间复杂度。所以我们可以将任意的实数n改写有限个2^k的形式的相加。

矩阵

一、矩阵基础知识

矩阵运算包括矩阵的加法,减法,数乘,乘法、转置,共轭和共轭转置

二、C++代码实现加法,减法,数乘,乘法,转置

1、加法,减法,数乘,乘法的实现

#include <iostream>
#include <malloc.h>
#include <stdio.h>
using namespace std;

typedef struct
{
//结构体
int row,col;
//二维指针,目的是动态分配内存
float **matrix;
} Matrix;

typedef struct
{
char *name;
char *number;
} Student;

Matrix CreateMatrix()
{
Matrix m;
int row,col;
cout << "输入行数与列数:" << endl;
cin >> row >> col;
float **enterMatrix ;
enterMatrix=(float**) malloc(row*sizeof(float*)) ;
for(int i=0; i<row; i++)
enterMatrix[i] = (float *)malloc(col * sizeof(float));
cout<<"输入你的矩阵:"<<endl;
for(int i=0; i<row; i++)
{
for(int j=0; j<col; j++)
{
cin >> enterMatrix[i][j];
}
}
m.col = col;
m.row = row;
m.matrix = enterMatrix;
return m;
}

//初始化一个行为row列为col矩阵
Matrix InitMatrix(int row,int col)
{
Matrix m;
float **matrix ;
matrix=(float**) malloc(row*sizeof(float*)) ;
for(int i=0; i<row; i++)
matrix[i] = (float *)malloc(col * sizeof(float));
for(int i=0; i<row; i++)
{
for(int j=0; j<col; j++)
{
matrix[i][j] = 0;
}
}
m.col = col;
m.row = row;
m.matrix = matrix;
return m;
}

//加法

Matrix add(Matrix m1, Matrix m2)

{
for(int i=0; i<m1.row; i++)
{
for(int j=0; j<m1.col; j++)
{
m1.matrix[i][j] = m1.matrix[i][j] +m2.matrix[i][j];
}
}
return m1;
}

 //减法

Matrix sub(Matrix m1, Matrix m2)
{
for(int i=0; i<m1.row; i++)
{
for(int j=0; j<m1.col; j++)
{
m1.matrix[i][j] = m1.matrix[i][j] -m2.matrix[i][j];
}
}
return m1;
}

int calRowCol(Matrix M1,Matrix M2,int row,int col)//row为M1的行 col为m2的列
{
int result = 0;
int same = M1.col;
for(int j=0; j<same; j++)
{
result+=M1.matrix[row][j]*M2.matrix[j][col];
}

return result;
}

//乘法
Matrix Mul(Matrix m1, Matrix m2)
{
Matrix result = InitMatrix(m1.row,m2.col);
for(int i=0; i<m1.row; i++)
{
for(int j=0; j<m2.col; j++)
{
result.matrix[i][j] = calRowCol(m1,m2,i,j);
}
}
return result;
}

//数乘

Matrix numMul(Matrix m, int num)
{
cout<<"数值:"<<num<<endl;
for(int i=0; i<m.row; i++)
{
for(int j=0; j<m.col; j++)
{
m.matrix[i][j] = m.matrix[i][j]*num;
}
}
return m;
}

//输出

Matrix printMatrix(Matrix m)

{
for(int i=0; i<m.row; i++)
{
for(int j=0; j<m.col; j++)
{
cout << m.matrix[i][j] << " ";
}
cout<<endl;
}
}

int main()
{
int num = 0;
do
{
cout<<"*************************************\n";
cout<<"* 菜单 *\n";
cout<<"* 1.矩阵相加 *\n";
cout<<"* 2.矩阵相减 *\n";
cout<<"* 3.矩阵相乘 *\n";
cout<<"* 4.矩阵数乘 *\n";
cout<<"* 5.退出 *\n";
cout<<"*************************************\n";
cin>>num;
if(1 == num|| 2 == num || 3 == num)
{
cout<<"请输入矩阵1"<<endl;
Matrix m1 = CreateMatrix();
cout<<"请输入矩阵2"<<endl;
Matrix m2 = CreateMatrix();
cout<<"两矩阵为"<<endl;
printMatrix(m1);
cout<<endl;
printMatrix(m2);
switch(num)
{
case 1:
{
if(m1.col!=m2.col || m1.row!=m2.row)
{
cout<<"行列不同"<<endl;
}
else{
cout<<"结果为:"<<endl;
printMatrix(add(m1,m2));
}
break;
}
case 2:
{
if(m1.col!=m2.col || m1.row!=m2.row)
{
cout<<"参数错误"<<endl;
}
else{
cout<<"结果为:"<<endl;
printMatrix(sub(m1,m2));
}
break;
}
case 3:
{
if(m1.col!=m2.row)
{
cout<<"参数错误"<<endl;
}
else{
cout<<"结果为:"<<endl;
printMatrix(Mul(m1,m2));
}
break;
}
default:
break;
}
}
else if(4 == num)
{
int number = 1;
cout<<"请输入矩阵"<<endl;
Matrix m = CreateMatrix();
cout<<"请输入数值"<<endl;
cin>>number;
cout<<"矩阵为:"<<endl;
printMatrix(m);
cout<<"数值为:"<<endl;
cout<<number<<endl;
printMatrix(numMul(m,number));
}
cout<<"按回车继续....";
getchar();
getchar();
system("cls");
}
while(1 == num|| 2 == num || 3 == num ||4 == num);
return 0;
}

2、给定一矩阵求其转置矩阵

  //n阶矩阵求转置,也就是沿着左对角线反转矩阵;a[i][j]  与 a[j][i] 对换。

#include<iostream>
using namespace std;
template<class T>
void swap(T* a, T* b)
{
T temp = a;
*a = *b;
*b = temp;
return;
}
template<class T>
void transpose(T& a, int rows)
{
for (int i = 0; i < rows; i++)
{
for (int j = i + 1; j < rows; j++)
{
swap(a[i][j], a[j][i]);
}
}
return;
}
int main()
{
int a[3][3] = { 1,2,3,4,5,6,7,8,9 };
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
cout << a[i][j] << " ";
}
cout << endl;
}
transpose(a, 3);
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
cout << a[i][j] << " ";
}
cout << endl;
}
return 0;
}

//n * m矩阵的转置,行和列颠倒。

#include<iostream>
using namespace std;
template<class T>
void swap(T* a, T* b)
{
T temp = a;
*a = *b;
*b = temp;
return;
}
template<class T,class T1>
void transpose(T& a, T1& b,int rows,int cols)
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
b[j][i] = a[i][j];
}
}
return;
}
int main()
{
int a[4][3] = { 1,2,3,4,5,6,1,2,3,4,5,6 };
int b[3][4] = { 0 };
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 3; j++)
{
cout << a[i][j] << " ";
}
cout << endl;
}
cout << endl;
transpose(a,b,4,3);
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 4; j++)
{
cout << b[i][j] << " ";
}
cout << endl;
}
return 0;
}

猜你喜欢

转载自www.cnblogs.com/Kyriech-Francis/p/12453519.html
3.8