高精度算法基础,实战与进阶

高精度算法基础,实战与进阶

高精度算法的本质就是用数组模拟超大数据的计算,用算法模拟进位操作。

一,高精度算法的基础,初始化函数。
c++ 复制代码
int init(int a[], string &s) {
	int len = s.size();
	for (int i = 0; i < len; i++) {
		a[i] = s[len - 1 - i] - '0';
	}
	return len;
}

先将超大数作为字符串输入,然后将大数由低位到高位存储到int数组中。然后将数组长度返回,这里可以将string作为引用传递或在初始化函数中声明和赋值。

二,整数高精度算法

1,高精度加法

其实我们要进行高精度加法,一般要准备三个数组,两个存储加法的两个参数,一个数组存储结果。我们由小到大依次将两个参数数组对应的值加到结果数组中,如果结果数组满足进位条件,则让结果数组的下一位+1...,由于整数数组自动初始化为0值,最后要进行消0操作,去除没有用到的数组中的0。最后从高位向低位输出。

c++ 复制代码
int hadd() {
	int lena = init(a);
	int lenb = init(b);
	int lenc = max(lena, lenb);
	for (int i = 0; i < lenc; i++) {
		c[i] += a[i] + b[i];
	//进位
		if (c[i] >= 10) {
			c[i + 1]++;
			c[i] %= 10;
		}
	}
	//加的时候可能会进位,减的时候只可能会减小。
	while (c[lenc] == 0 && lenc > 0) {
		lenc--;
	}
	for (int i = lenc; i >= 0; i--) {
		cout << c[i];
	}
	return 0;
}

2,高精度减法

减法的思路和加法类似,开始前需要进行大小判断,如果减法的结果是负数要加"-"。借位操作可以让结果数组的下一位-1。

c++ 复制代码
int hsub() {
	string s1, s2;
	int lena = init(a, s1);
	int lenb = init(b, s2);
	int lenc = max(lena, lenb);

	if (lena < lenb || (lena == lenb && s1 < s2)) {
		swap(a,b);
		cout<<"-";
	}

	for (int i = 0; i < lenc; i++) {
		c[i] += a[i] - b[i];
        
		if (c[i] < 0) {
			c[i + 1]--;
			c[i] += 10;
		}
	}
    //因为减法的最高位不会扩大,所以不用判断结果数组的lenc位上不用进行除0判断。
	lenc--;
    
	while (c[lenc] == 0 && lenc > 0) {
		lenc--;
	}
	for (int i = lenc; i >= 0; i--) {
		cout << c[i];
	}
	return 0;
}

3,高精度乘法

乘法的实现模拟了列竖式的做法,先用两层循环处理需要的所有"下标",之后再逐一进行进位操作。

c++ 复制代码
int hmuti() {
	int lena = init(a);
	int lenb = init(b);
	int lenc = lena + lenb;
	for (int i = 0; i < lena; i++) {
		for (int j = 0; j < lenb; j++) {
			c[i + j] += a[i] * b[j];
		}
	}
	for (int i = 0; i < lenc; i++) {
		if (c[i] >= 10) {
			c[i + 1] += c[i] / 10;
			c[i] %= 10;
		}
	}
	while (c[lenc] == 0 && lenc > 0) {
		lenc--;
	}
	for (int i = lenc; i >= 0; i--) {
		cout << c[i];
	}
	return 0;
}

4,高精度除法

高精度除法只能计算一个超大数除以低精度数字,计算过程类似于列式除法。

c++ 复制代码
int hd() {
	string s1;
	cin >> s1 >> b; // 输入被除数和除数
	int lena = init(a, s1); // 初始化被除数
	int lenc = 0; // 结果的位数
	int x = 0; // 余数
	// 从最高位开始逐位计算
	for (int i = lena - 1; i >= 0; i--) {
		x = x * 10 + a[i]; // 当前位的值加上余数
		c[lenc++] = x / b; // 计算当前位的商
		x %= b; // 更新余数
	}
	// 去除前导零
	int start = 0;
	while (start < lenc && c[start] == 0) {
		start++;
	}
	// 输出结果
	if (start == lenc) {
		cout << "0"; // 如果结果是0
	}
	else {
		for (int i = start; i < lenc; i++) {
			cout << c[i];
		}
	}
	cout << endl;
	return 0;
}
三,高精度算法实战:阶乘之和

题源:洛谷P1009

描述:用高精度计算出 S =1!+2!+3!+⋯+n !(n≤50)。

其中 ! 表示阶乘,定义为 n !=n ×(n −1)×(n−2)×⋯×1。例如,5!=5×4×3×2×1=120。

这是一道纯coding问题,可以作为coding练习。

c++ 复制代码
int n;
int cur[90], last[90], nex[90], result[90];
int lcur, llast = 1, lnext = 0, len_ans, fsize = 0;

int main() {
    cin >> n;
    last[0] = 1; // 初始化

    for (int i = 1; i <= n; i++) { // 计算 i 的阶乘
        memset(nex, 0, sizeof(nex)); // 初始化 nex 数组
        lcur = 0; // 初始长度
        int p = i; // 1 - n 每个数字的开端
        while (p > 0) { // 把 i 存进 cur 数组(按位)
            cur[lcur++] = p % 10;
            p /= 10;
        }

        // 高精度乘法
        for (int j = 0; j < lcur; j++) {
            for (int k = 0; k < llast; k++) {
                nex[j + k] += cur[j] * last[k];
            }
        }

        lnext = lcur + llast - 1; // 更新 lnext
        for (int j = 0; j < lnext; j++) { // 进位检查
            if (nex[j] >= 10) {
                nex[j + 1] += nex[j] / 10;
                nex[j] %= 10;
            }
        }
        if (nex[lnext]) {
            lnext++; // 看最高位要不要进位
        }

        len_ans = llast;
        llast = lnext;
        fsize = max(fsize, lnext); // 更新 fsize

        for (int k = 0; k < lnext; k++) {
            last[k] = nex[k]; // 将 nex 数组的值复制到 last 数组
        }

        // 高精度加法
        for (int j = 0; j < fsize; j++) {
            result[j] += last[j];
            if (result[j] >= 10) {
                result[j + 1] += result[j] / 10;
                result[j] %= 10;
            }
        }
        if (result[fsize]) {
            fsize++; // 如果最高位有进位,增加 fsize
        }
    }

    while (fsize > 0 && result[fsize] == 0) {
        fsize--; // 去掉前导零
    }
    for (int i = fsize; i >= 0; i--) {
        cout << result[i]; // 倒序输出
    }
    return 0;
}
四,高精度算法扩展------带小数的高精度加减法。

1,高精度小数加法

我的思路是限定100位整数和5位小数,用数组将他们分区存储起来。并且优化了进位判断,将进位值设立为单独的变量,在下一次计算中更新,很好地连接了小数和整数部分。

c++ 复制代码
class Myadd {
private:
	int a[106] = { 0 }, b[106] = { 0 }, c[106] = { 0 };
public:

	void init(int num[], string& str) {
		int len = str.size();  
		bool hasDot = false;  //是否存在小数点
		int dotpos = 0;  //小数点在字符串中的位置

		//查找小数点的位置
		for (int i = 0; i < len; i++) {
			if (str[i] == '.') {
				dotpos = i;
				hasDot = true;
				break;
			}
		}

		//处理整数部分
		int index = 0;
		for (int i = dotpos - 1; i >= 0; i--) {
			num[index++] = str[i] - '0';
		}

		//处理小数部分
		if (hasDot) {
			//小数部分占用数组的100下标之后
			index = 100;
			for (int i = dotpos + 1; i < len; i++) {
				num[index++] = str[i] - '0';
			}
		}
		
	}

	string add(string& str1, string& str2) {
		init(a, str1);
		init(b, str2);

		//设置carry,处理进位,便于关系处理
		int carry = 0;

		//处理小数部分
		for (int i = 105; i >= 100; i--) {
			c[i] = a[i] + b[i] + carry;
			carry = c[i] / 10;
			c[i] %= 10;
		}

		for (int i = 0; i < 100; i++) {
			c[i] = a[i] + b[i] + carry;
			carry = c[i] / 10;
			c[i] %= 10;
		}

		int zeroInt = 0;
		for (int i = 99; i >=0; i--) {
			if (c[i] == 0) {
				zeroInt++;
			}
			else {
				break;
			}
		}

		int zeroFloat = 0;
		for (int i = 104; i >= 100; i--) {
			if (c[i] == 0) {
				zeroFloat++;
			}
			else {
				break;
			}
		}
		string result;
		if (carry > 0) {
			result += to_string(carry);
		}

		//添加整数部分
		for (int i = 99 - zeroInt; i >= 0; i--) {
			result += to_string(c[i]);
		}

		if (result.empty()) {
			result = "0";
		}

		//添加小数部分
		result += ".";
		for (int i = 100; i < 105 - zeroFloat; i++) {
			result += to_string(c[i]);
		}

		return result;
	}
};

2,高精度小数减法

这里要注意两数大小判断,和是否借位的true/false判断。

c++ 复制代码
class Mysub {
private:
   int a[105] = { 0 }, b[105] = { 0 }, c[105] = { 0 };

public:
   void init(int num[], const std::string& str) {
   	int len = str.size();
   	bool hasDot = false;  // 是否存在小数点
   	int dotpos = 0;      // 小数点在字符串中的位置

   	// 查找小数点的位置
   	for (int i = 0; i < len; i++) {
   		if (str[i] == '.') {
   			dotpos = i;
   			hasDot = true;
   			break;
   		}
   	}

   	// 处理整数部分
   	int index = 0;
   	for (int i = dotpos - 1; i >= 0; i--) {
   		num[index++] = str[i] - '0';
   	}

   	// 处理小数部分
   	if (hasDot) {
   		// 小数部分占用数组的100下标之后
   		index = 100;
   		for (int i = dotpos + 1; i < len; i++) {
   			num[index++] = str[i] - '0';
   		}
   	}
   }

   bool isGreaterOrEqual(const std::string& str1, const std::string& str2) {
   	// 去掉小数点
   	std::string s1 = str1, s2 = str2;
   	s1.erase(std::remove(s1.begin(), s1.end(), '.'), s1.end());
   	s2.erase(std::remove(s2.begin(), s2.end(), '.'), s2.end());

   	// 补齐长度
   	while (s1.size() < s2.size()) s1 = "0" + s1;
   	while (s2.size() < s1.size()) s2 = "0" + s2;

   	return s1 >= s2;
   }

   std::string sub(const std::string& str1, const std::string& str2) {
   	bool isNegetive = false;

   	if (!isGreaterOrEqual(str1, str2)) {
   		isNegetive = true;
   		init(a, str2);
   		init(b, str1);
   	}
   	else {
   		init(a, str1);
   		init(b, str2);
   	}
   	
   	// 判断是否借位
   	bool borrow = false;

   	// 处理小数部分
   	for (int i = 104; i >= 100; i--) {
   		c[i] = a[i] - b[i] - borrow;
   		if (c[i] < 0) {
   			c[i] += 10;
   			borrow = true;
   		}
   		else {
   			borrow = false;
   		}
   	}

   	// 处理整数部分
   	for (int i = 0; i < 100; i++) {
   		c[i] = a[i] - b[i] - borrow;
   		if (c[i] < 0) {
   			c[i] += 10;
   			borrow = true;
   		}
   		else {
   			borrow = false;
   		}
   	}

   	// 去除整数部分前导零
   	int zeroInt = 0;
   	for (int i = 99; i >= 0; i--) {
   		if (c[i] == 0) {
   			zeroInt++;
   		}
   		else {
   			break;
   		}
   	}

   	// 去除小数部分后导零
   	int zeroFloat = 0;
   	for (int i = 104; i >= 100; i--) {
   		if (c[i] == 0) {
   			zeroFloat++;
   		}
   		else {
   			break;
   		}
   	}

   	std::string result;

   	if (isNegetive) {
   		result += "-";
   	}

   	// 添加整数部分
   	for (int i = 99 - zeroInt; i >= 0; i--) {
   		result += to_string(c[i]);
   	}

   	if (result.empty()) {
   		result = "0";
   	}

   	// 添加小数部分
   	result += ".";
   	for (int i = 100; i < 105 - zeroFloat; i++) {
   		result += to_string(c[i]);
   	}

   	return result;
   }
};
五,结语

高精度算法难不在于思路,而在于代码,很多时候能大概想到怎么做,但就是敲不出来,这可能就是所谓的coding能力的差异吧。

相关代码上传至github仓库:github.com/kair998/Alg...

相关推荐
程序员yt3 分钟前
211 本硕研三,已拿 C++ 桌面应用研发 offer,计划转音视频或嵌入式如何规划学习路线?
c++·学习·音视频
goTsHgo10 分钟前
完整的类在JVM中的生命周期详解
java·开发语言·算法
LuckyLay23 分钟前
LeetCode算法题(Go语言实现)_08
算法·leetcode·职场和发展·golang
一只_程序媛30 分钟前
【leetcode hot 100 78】子集
算法·leetcode·职场和发展
进击的_鹏1 小时前
【C++】多态
开发语言·c++
小丽今天学代码了吗1 小时前
练习-班级活动(map存储键值对)
数据结构·算法
Aurora_wmroy2 小时前
算法竞赛备赛——【数据结构】链表
数据结构·c++·算法·链表·蓝桥杯
ん贤2 小时前
单调栈详解【C/C++】
数据结构·c++·算法·贪心算法·单调栈
空雲.2 小时前
ABC 373
算法·深度优先
C++ 老炮儿的技术栈3 小时前
vector和list的区别是什么
开发语言·c++·笔记·学习