作为一个可选的赋值,我正在考虑编写自己的BigInteger类实现,在那里我将提供自己的加法,减法,乘法等方法。
这将是任意长整数,甚至数百位数。
在对这些数字进行数学计算时,逐位数字并不难,您认为最好的数据结构代表我的“BigInteger”是什么?
起初我正在考虑使用一个数组,但后来我认为在一次大的加法或乘法之后我仍然可能会溢出(用完数组插槽)。这是一个使用链表的好例子,因为我可以处理O(1)时间复杂度的数字?
是否还有一些其他数据结构比链表更适合?我的数据结构所保存的类型应该是我可以使用的最小可能整数类型吗?
另外,我应该注意如何存储“carry”变量吗?它本身应该是我的“BigInteger”类型吗?
答案 0 :(得分:3)
查看David R. Hanson撰写的书C Interfaces and Implementations。它有两个章节,涵盖了矢量结构,字大小以及您可能遇到的许多其他问题。
它是为C编写的,但大多数都适用于C ++和/或Java。如果您使用C ++,它会更简单一些,因为您可以使用std::vector
之类的东西为您管理数组分配。
答案 1 :(得分:1)
始终使用能够完成所需工作的最小int类型(字节)。链接列表应该可以正常工作,因为您不必担心溢出。
答案 2 :(得分:1)
如果使用二叉树(其叶子是整数),则可以使用更简单的分治算法获得链表(无界数字等)的所有优点。在这种情况下,你没有一个基数,但很多都取决于你工作的水平。
如果这样做,您需要使用BigInteger进行携带。您可以认为这是“链接列表”方法的一个优点,即进位总是可以表示为int(对于任何基数都是如此,不仅仅是对于10,因为大多数答案似乎都假设您应该使用。 ..在任何基数中,进位总是一位数)
我不妨这样说:当你使用2 ^ 30或2 ^ 31时,使用10号基地是一种可怕的浪费。
答案 3 :(得分:1)
访问链接列表的元素很慢。我认为数组是可行的方法,需要大量的绑定检查和运行时数组调整大小。
澄清:遍历链表并遍历数组都是O( n )操作。但遍历链表需要在每一步都引用一个指针。仅仅因为两个算法都具有相同的复杂性,并不意味着它们都需要花费相同的时间来运行。在链接列表中分配和取消分配 n 节点的开销也将比单个 n 的数组的内存管理重得多,即使必须调整数组的大小几次。
答案 4 :(得分:1)
也就是说,C / C ++也不适合这项任务。大整数是一种扩展精度数学。大多数CPU提供的指令用于以与正常数学相当或相同的速度(每指令位数)处理扩展精度数学运算。当您添加2 ^ 32 + 2 ^ 32时,答案为0 ...但是处理器的ALU还有一个特殊的进位输出,程序可以读取和使用。
C ++无法访问该标志,C中也没办法。你必须使用汇编程序。
为了满足好奇心,您可以使用标准布尔运算来恢复进位等。但是,下载现有的库会更好。
答案 5 :(得分:0)
我会说一堆整数。
答案 6 :(得分:0)
我会说一个std :: char的向量(因为它只能保持0-9)(如果你打算用BCD工作)
如果不是BCD,那么使用int的向量(你没有说清楚)
链接列表
的空间开销要小得多并且所有建议都说“使用向量,除非你有充分的理由不这样做”
答案 7 :(得分:0)
数组确实很自然。我认为当你在你的记忆中用完时抛出OverflowException是可以接受的。老师会注意细节。
乘法大致加倍数字,加法最多增加1.可以很容易地创建一个足够大的数组来存储操作结果。
在乘法中,Carry最多是一位数的长数字(9 * 9 = 1,携带8)。单个int就可以了。
答案 8 :(得分:0)
std::vector<bool>
或std::vector<unsigned int>
可能就是您想要的。您需要push_back()
或resize()
,因为您需要更多的空间来进行乘法等。另外,如果您使用的是双重赞美,请记得推送正确的符号位。
答案 9 :(得分:0)
根据经验,使用std::vector
代替std::list
,除非您需要经常在序列中间插入元素。向量往往更快,因为它们是连续存储的,因此可以从更好的空间局部性(现代平台上的主要性能因素)中获益。
确保使用平台自然的元素。如果您想独立于平台,请使用long
。请记住,除非你有一些特殊的编译器内在函数,否则你需要一个至少两倍大的类型来执行乘法。
我不明白你为什么要把携带变成一个大整数。 Carry是一个用于加法的单个位和用于乘法的元素大小。
确保您阅读Knuth的计算机编程艺术,在很大程度上描述了与任意精度算术有关的算法。