所以我最近做了大学考试,其中一个问题要求我们创建一个程序,打印出tribonacci序列中的第n个数字(1,1,1,3,5,9,17,31 .. )。据说这些数字长达1500位。我创建了一个递归函数,适用于前37个tribonacci数字。但是第38个数字发生了堆栈溢出。这个问题已经警告过我们,并说我们会以某种方式需要克服这一点,但我不知道如何。我们是否打算创建自己的数据类型?
double tribonacci(int n){
if(n < 4){
return 1;
}else{
return tribonacci(n-3) + tribonacci(n-2) + tribonacci(n-1);
}
}
int main(int argc, char *argv[]){
double value = tribonacci(atoi(argv[1]));
printf("%lf\n", value);
}
这是我在考试条件下写的解决方案,在15分钟内完成。
程序从命令行的输入中获取n的值。除了stdlib.h和stdio.h之外,我们不允许使用任何库。所有这一切,如何创建一个足够大的数据类型来打印出1500位数的数字(因为双数据类型只能容纳到第37个tribonacci数)?或者这个问题有另一种方法吗?
答案 0 :(得分:2)
如果您的老师允许,您应该使用一些arbitrary-precision arithmetic库(a.k.a.Bigints或bignums)。我建议GMPlib,但有others。
另请参阅this answer(特别是如果您的老师希望您编写一些粗任意精度添加)。
答案 1 :(得分:2)
您需要不同的算法。发布的代码不会受到整数溢出的影响,因为它会在双精度计算中进行所有计算。所以你可能正在获得堆栈溢出。发布的代码使用指数时间和空间,并且在N = 38时,指数空间可能溢出堆栈。一些替代方案,按效率和复杂性的顺序递增:
你还需要一个&#34;大号&#34;数据结构 - 见其他答案。
答案 2 :(得分:2)
对于开发时间限制的考试解决方案,我肯定会选择快速和快速的考试。肮脏的方法,但我不会在15分钟内完成它。
问题大小限制为1500个字符,计算tribonacci表示您将始终需要携带子结果N-3,N-2和N-1以计算子结果N.因此,让我们定义一个合适的静态数据结构正确的起始值(在你的问题中它的1; 1; 1,但我认为它应该是0; 1; 1):
char characterLines[4][1501] = { { '0', 0 }, { '1', 0 }, { '1', 0 } };
然后定义一个对字符数组进行操作的add函数,期望'\0'
作为数组的结尾,字符编号'0' to '9'
作为数字,以最低有效数字排在第一位。
void addBigIntegerCharacters(const char* i1, const char* i2, char* outArray)
{
int carry = 0;
while(*i1 && *i2)
{
int partResult = carry + (*i1 - '0') + (*i2 - '0');
carry = partResult / 10;
*outArray = (partResult % 10) + '0';
++i1; ++i2; ++outArray;
}
while(*i1)
{
int partResult = carry + (*i1 - '0');
carry = partResult / 10;
*outArray = (partResult % 10) + '0';
++i1; ++outArray;
}
while(*i2)
{
int partResult = carry + (*i2 - '0');
carry = partResult / 10;
*outArray = (partResult % 10) + '0';
++i2; ++outArray;
}
if (carry > 0)
{
*outArray = carry + '0';
++outArray;
}
*outArray = 0;
}
使用必要数量的添加来计算tribonacci:
// n as 1-based tribonacci index.
char* computeTribonacci(int n)
{
// initialize at index - 1 since it will be updated before first computation
int srcIndex1 = -1;
int srcIndex2 = 0;
int srcIndex3 = 1;
int targetIndex = 2;
if (n < 4)
{
return characterLines[n - 1];
}
n -= 3;
while (n > 0)
{
// update source and target indices
srcIndex1 = (srcIndex1 + 1) % 4;
srcIndex2 = (srcIndex2 + 1) % 4;
srcIndex3 = (srcIndex3 + 1) % 4;
targetIndex = (targetIndex + 1) % 4;
addBigIntegerCharacters(characterLines[srcIndex1], characterLines[srcIndex2], characterLines[targetIndex]);
addBigIntegerCharacters(characterLines[targetIndex], characterLines[srcIndex3], characterLines[targetIndex]);
--n;
}
return characterLines[targetIndex];
}
请记住,打印结果时,您的最低位数首先出现
void printReverse(const char* start)
{
const char* printIterator = start;
while (*printIterator)
{
++printIterator;
}
do
{
putchar(*(--printIterator));
} while (printIterator != start);
}
int main()
{
char* c = computeTribonacci(50); // the real result is the array right-to-left
printReverse(c);
}
如上所述,这是一种快速的方式。脏编码,但仍然不在15分钟内。
我使用每个十进制数字单独char
的原因主要是可读性和符合十进制数学在笔和纸上的工作方式,这是开发时间有限时的一个重要因素。关注运行时约束而不是开发时间,我可能将数字分组为unsigned long long
数组,每个数字代表18个十进制数字。我仍然会专注于十进制数字分组,因为使用标准库函数打印成字符要容易得多。 18因为我需要一个数字用于数学溢出,19是unsigned long long
的完全可用的十进制数字的限制。这将导致更多的更改... 0
不能再用作终止字符,因此可能值得保存每个数组的有效长度。 add
和computeTribonacci
的原则与一些小的技术变化保持一致,打印需要进行一些调整,以确保每组数字的长度为18输出,而不是最重要的数字。
答案 3 :(得分:1)
您需要将+
操作替换为您自己创建的运算符ADD
并根据需要编码BigIntegers - 有很多方法可以编码BigIntegers。
所以你需要自己定义一个数据类型BigInteger和以下操作
ADD : BigInteger, BigInteger -> BigInteger
1+ : BigInteger -> BigInteger
2- : BigInteger -> BigInteger
<4 : BigInteger -> boolean
The constants 1,2,4 as BigInteger
并且在替换了这些东西之后写了一个标准函数来计算线性时间和空间中的fibb。