笔记——超大数求n次方与取余运算

一、超大数求n次方

超大数:指的是在内存中没有直接的类型能够一次性存储得下的那些数

1、问题简要介绍

计算 90 ^ 13,如下图所示。得出的结果是非常庞大的一个数,我们无法直接使用某种数据类型将这个结果存储下来,无论是int还是longlong类型,都存在一个存储上限,超过这个上限的部分就会发生数据溢出,而这是我们最不希望看到的结果。

对于上述问题,有一个解决方案——将这个数转换成字符串进行处理。从理论上分析,只要内存充足,字符串就不会因数据大而溢出,例如使用 string str = “25418658283290000000000000” 来存储上述结果,但是我们要如何让计算机明白,算出来的结果要用字符串来存呢?可以参考下面的解释。

2、理清思路

先用一个小数的2次方来分析计算过程:
123 ^ 2 = 123 * 1 * 100 + 123 * 2 * 10 + 123 * 3

对于n次方的运算,也可以拆解成 m 份的小数先相乘再相加

3、将超大数a^b拆解相乘的函数

/**************************************************************
	例:	123 ^ 2 = 123 * 100 + 123 * 20 + 123 * 3
				= 15129
			123 ^ 3 = 123 * 123 * 123
				= 15129 * 123
				= 15129 * 100 + 15129 * 20 + 15129 * 3
				= 1860867
***************************************************************/
string RSABigIntrger::getAToBPower(long long a, long long b)
{
	if (a < 0 || b < 0)
		return "-1";
	string aStr = to_string(a);
	size_t aLength = aStr.length();
	// 从最低位开始算, 例如 123 ^ 5, 则此时 aIndex 为数字 3 的下标
	int aIndex = aLength - 1;
	string resultStr = aStr;
	// 记录两个小单位之间,相加的结果
	string addResultStr = "0";
	//保存每次小单位计算的结果
	string* eachResultStr = new string[aLength];
	int eachResultIndex = 0;
	// 记录当前的计数单位,个十百千万,简而言之就是记录需要加几个 0 在结果后面
	int countUnits = 0;
	// 记录拆解后的小单元内,两数相乘的进位,例如 15129 * 3 时,记录 9 * 3 的进位
	int carry = -1;
	// 计算 b 次方,循环 b 次
	for (long long y = 1; y < b; y++)
	{
		// aStr * resultStr
		while (aIndex != -1)
		{
			// 通过Ascall码计算出当前 aStr[aIndex] 是哪个数字
			int x1 = aStr[aIndex] - 48;
			size_t resultIndex = resultStr.length() - 1;
			// 通过计数单位 countUnits 添加对应个数的 0 在小单元计算结果的后面
			for (int z = 0; z < countUnits; z++)
				eachResultStr[eachResultIndex].insert(0, 1, '0');
			while (resultIndex != -1)
			{
				int x2 = resultStr[resultIndex] - 48;
				int resultTemp = x1 * x2;
				// 如果上一轮计算有进位,则需要先加上之后再处理计算结果
				if (carry != -1)
					resultTemp = resultTemp + carry;
				// 计算结果需要进位时
				if (resultTemp >= 10)
				{
					carry = resultTemp / 10;
					resultTemp = resultTemp % 10;
				}
				else
				{
					carry = -1;
				}
				// 在当前小单位计算结果的字符串开头前,插入这个计算结果
				eachResultStr[eachResultIndex].insert(0, 1, resultTemp + 48);
				resultIndex--;
			}
			// 判断小单位计算完之后是否有进位,有,则记录到小单位的结果中
			if (carry != -1)
				eachResultStr[eachResultIndex].insert(0, 1, carry + 48);
			// 计算并存储各个小单位计算结果的和
			addResultStr = getAddResult(addResultStr, eachResultStr[eachResultIndex]);
			countUnits++;
			eachResultIndex++;
			aIndex--;
			carry = -1;
		}
		countUnits = 0;
		resultStr = addResultStr;
		addResultStr = "0";
		for (int z = 0; z < eachResultIndex; z++)
			eachResultStr[z] = "";
		eachResultIndex = 0;
		aIndex = aStr.length() - 1;
	}
	delete[] eachResultStr;
	return resultStr;
}

4、a + b超大数相加函数

将 a ^ b 拆解成多个小单元相乘计算之后,下一步就是将这些小单元的计算结果逐个相加,即上述的 GetAToBPower() 函数中调用的 GetAddResult() 函数,具体实现如下:

string RSABigIntrger::getAddResult(string aStr, string bStr)
{
	string resultStr = "";
	// 记录进位
	int carry = -1;
	size_t aIndex = aStr.length() - 1;
	size_t bIndex = bStr.length() - 1;
	// 将较大的数放在 aStr 中,方便处理
	if (bIndex > aIndex)
	{
		string tempStr = aStr;
		aStr = bStr;
		bStr = tempStr;
		aIndex = aStr.length() - 1;
		bIndex = bStr.length() - 1;
	}
	// 从最低位开始,逐位相加
	while (aIndex != -1)
	{
		int x1 = aStr[aIndex] - 48;
		// 当 bStr 内的数字全部参加过计算后,与 aStr 高位相加的就只有0和进位了
		int x2 = 0;
		if (bIndex != -1)
			x2 = bStr[bIndex] - 48;
		int resultTemp = x1 + x2;
		// 存在进位, 先加上进位,再处理计算结果
		if (carry != -1)
			resultTemp = resultTemp + carry;
		if (resultTemp >= 10)
		{
			carry = resultTemp / 10;
			resultTemp = resultTemp % 10;
		}
		else
		{
			carry = -1;
		}
		resultStr.insert(0, 1, resultTemp + 48);
		aIndex--;
		if (bIndex != -1)
			bIndex--;
	}
	// 存在进位, 则在结果前面补上
	if (carry != -1)
		resultStr.insert(0, 1, carry + 48);
	return resultStr;
}

5、测试

如图所示,调用 GetAToBPower() 可以正确获得 90 ^ 13 次方的结果,并且这个数值可以被保存下来,供后续使用。在这里插入图片描述

二、超大数取余

同理,我们将超大数放在字符串中进行计算与存储

1、理清思路

先用一个小数的取余来分析计算过程,例如 124 % 3 = 1, 可以拆解为以下几步:
① 1 % 3 = 1
② (1 * 10 + 2) % 3 = 12 % 3 = 0
③ 4 % 3 = 1
其中,第②步算式中的 1 * 10 的 1 ,是第①步计算的结果。

2、超大数取余函数

总的来说,取余操作,需要从最高位开始计算,如果上一位有余数,则需要加上之后,再进行当前位的取余,直到最后一位取余完成。

long long GetAToBRemainder(string aStr, long long b) 
{
	if (b < 0)
		return -1;
	long long result = -1;
	size_t aIndex = 0;
	size_t aLength = aStr.length();
	while (aIndex < aLength)
	{
		int x1 = aStr[aIndex] - 48;
		if (result != -1)
			x1 = x1 + (int)(result * 10);
		result = x1 % b;
		aIndex++;
	}
	if (result != -1)
		return result;
	else
		return 0;
}

3、测试

如下图所示,计算 4654789454 % 3 = 2,结果正确。
在这里插入图片描述

最后

可优化的点还有很多,恳请大佬们多多指教

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
FPGA自学笔记——设计与验证JMB FPGA(可编程逻辑门阵列)是一种可编程的硬件平台,可以实现各种数字电路的设计与验证。本文将简要介绍使用FPGA自学设计与验证JMB(低功耗、高效能、集成度高的多媒体芯片)的过程。 首先,我们需要了解JMB的功能和特性。JMB是一种面向多媒体应用的芯片,具备低功耗、高效能和高集成度的优势。我们需要详细研究JMB的硬件架构和内部模块,包括处理器核、存储器模块、图像和音频处理模块等。 接下来,我们可以使用FPGA开发板来设计和验证JMB。首先,我们需要熟悉FPGA设计工具,例如Vivado或Quartus等。这些工具提供了图形化界面和硬件描述语言(HDL)等设计方法。我们可以使用HDL编写JMB的功能模块,并将其综合为FPGA可执行的位流文件。 在设计完成后,我们需要验证JMB的功能和性能。我们可以使用仿真工具(例如ModelSim或ISE Simulator)来模拟JMB在不同情况下的行为。通过设计测试程序并运行仿真,我们可以验证JMB的各个模块是否正确地工作,是否满足设计要。 在验证完成后,我们可以将位流文件下载到FPGA开发板中进行智能芯片的物理实现和测试。通过与外部设备的连接以及相关测试程序的运行,我们可以验证JMB在实际硬件中的功能和性能。 总结起来,学习FPGA设计与验证JMB,我们需要熟悉JMB的硬件架构和内部模块,并使用FPGA开发工具进行设计与验证。通过仿真和物理实现测试,我们可以验证JMB的功能和性能。这些过程需要理论知识和实践经验的结合,希望这些笔记能够给你提供一些参考和指导。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值