1 求反序数
int Reverse(int x){ //求反序数
int revx = 0;
while (x != 0){
revx *= 10;
revx += x % 10;
x /= 10;
}
return revx;
}
2 最大公约数gcd
求解最大公约数常用欧几里得算法(即辗转相除法)
欧几里得算法基于以下定理:
- 设a、b均为正整数,则gcd(a, b) = gcd(b, a%b)。
int gcd (int a, int b){
if (b == 0) return a;
else return gcd(a, a%b);
}
3 最小公倍数lcm
最小公倍数往往通过通过最大公约数来求,假设a、b均为正整数,如果a和b的最大公约数为d,那么a和b的最小公倍数为a*b/d。
int lcm (int a, int b){
return a / gcd(a, b) * b;
}
4 判断是否为素数
//判断是否为素数
bool isPrime(int n){
if (n < 2)
return false;
int bound = sqrt(n);
for (int i = 2; i <= bound; i++){
if (n % i == 0)
return false;
}
return true;
}
5 大整数运算
5.1 大整数的存储
使用数组即可(定义int类型数组d[1000])。
需要注意的是,存储时需要顺位存储的,即整数的高位存储在数组的高位 (如将235存储在数组中,则有d[0] = 5, d[1] = 3, d[2] = 2);而把整数按字符串%s读入的时候,实际上是逆位存储的,(即d[0] = 2, d[1] = 3, d[2] = 5),因此在读入后需要再另存为至d[]数组的时候反序一下。
为了方便随时获取大整数的长度,一般都会定义一个int型变量len来记录其长度,并和d数组结合成一个结构体。
//大整数结构体
struct bignum {
int d[1000];
int len;
//构造函数
bignum() {
memset(d, 0, sizeof(d));
len = 0;
}
};
//将读入的字符串存储在数组中
bignum change(string str) {
bignum a;
a.len = str.size();
for (int i = 0; i < a.len; i++){
a.d[i] = str[a.len - i - 1] - '0'; //逆着赋值
}
return a;
}
//比较两个bignum变量的大小, a>b返回1, a==b返回0, a<b返回-1
int compare (bignum a, bignum b){
if (a.len > b.len) return 1;
else if (a.len < b.len) return -1;
else {
for (int i = a.len - 1; i >= 0; i--){
if(a.d[i] > b.d[i]) return 1;
else if(a.d[i] < b.d[i]) return -1;
}
return 0;
}
}
//输出bignum
void print(bignum a){
for (int i = a.len - 1; i >= 0; i--){
printf("%d", a.d[i]);
}
}
5.2 高精度加法
如果有有一个是负数,则可以在转换到数组这一步时,将负号去掉,然后采用高精度减法。
如果两个数都是负的,就都去掉负号用高精度加法,最后再把负号加回去即可。
bignum add(bignum a, bignum b) { //高精度a + b
bignum c;
int carry = 0; //carry表示进位
for (int i = 0; i < a.len || i < b.len; i++){
int temp = a.d[i] + b.d[i] + carry; //对应位相加
c.d[c.len++] = temp % 10; //个位数为该位结果
carry = temp / 10;
}
if (carry != 0) {
c.d[c.len++] = carry;
}
return c;
}
5.3 高精度减法
使用sub函数前需要比较两个数的大小,如果被减数小于减数,需要交换两个变量,然后输出负号,再使用sub函数。
bignum sub(bignum a, bignum b) { //高精度a - b
bignum c;
for (int i = 0; i < a.len || i < b.len; i++){ //以较长的为界限
if (a.d[i] < b.d[i]) { //如果不够减
a.d[i + 1]--; //向高位借位
a.d[i] += 10; //当前位加10
}
c.d[c.len++] = a.d[i] - b.d[i];
}
while (c.len - 1 >= 1 && c.d[c.len - 1] == 0){
c.len--;
}
return c;
}
5.4 高精度与低精度的乘法
如果a和b中存在负数,需要先记录下其负号,运算之后再加上负号。
bignum multi(bignum a, int b){ //高精度 a * b
bignum c;
int carry = 0; //进位
for (int i = 0; i < a.len; i++){
int temp = a.d[i] * b + carry;
c.d[c.len++] = temp % 10; //个位作为该位结果
carry = temp / 10; //高位部分作为新的进位
}
while (carry != 0) { //和加法不一样,乘法的进位可能不止一位,因此用while
c.d[c.len++] = carry % 10;
carry /= 10;
}
return c;
}
5.5 高精度与低精度的除法
bignum divide(bignum a, int b, int &r) { //高精度除法,r为余数
bignum c;
c.len = a.len; //被除数的每一位和商的每一位是一一对应的,因此先令长度相等
for (int i = a.len - 1; i >= 0; i--){ //从高位开始
r = r * 10 + a.d[i]; //和上一位遗留的余数组合
if (r < b) c.d[i] = 0; //不够除,该位为0
else { //够除
c.d[i] = r / b; //商
r = r % b; //获得新的余数
}
}
while (c.len - 1 >= 1 && c.d[c.len - 1] == 0) {
c.len--; //去除高位的0,同时至少保留一位最低位
}
return c;
}
5.6 大整数运算例题
- PAT B1017 A除以B (20)
- PAT A1023 Have Fun with Numbers (20)
- PAT A1024 Palindromic Number (25)
5.7 大整数加法完整代码
#include <cstdio>
#include <iostream>
using namespace std;
//大整数结构体
struct bignum {
int d[1000];
int len;
//构造函数
bignum() {
memset(d, 0, sizeof(d));
len = 0;
}
};
//将读入的字符串存储在数组中
bignum change(string str) {
bignum a;
a.len = str.size();
for (int i = 0; i < a.len; i++){
a.d[i] = str[a.len - i - 1] - '0'; //逆着赋值
}
return a;
}
//大整数加法
bignum add(bignum a, bignum b) {
bignum c;
int carry = 0; //carry表示进位
for (int i = 0; i < a.len || i < b.len; i++){
int temp = a.d[i] + b.d[i] + carry; //对应位相加
c.d[c.len++] = temp % 10; //个位数为该位结果
carry = temp / 10;
}
if (carry != 0) {
c.d[c.len++] = carry;
}
return c;
}
//输出bignum
void print(bignum a){
for (int i = a.len - 1; i >= 0; i--){
printf("%d", a.d[i]);
}
}
int main(){
string str1, str2;
cin >> str1 >> str2;
bignum a = change(str1);
bignum b = change(str2);
print(add(a, b));
return 0;
}