输出杨辉三角的前一百万行,假设屏幕足够宽。

mzlogin 2010-04-06 04:05:36
一直以为,杨辉三角只是一个很小儿科的问题……直到那一天,我遇见了它……
    输出杨辉三角的前一百万行,假设屏幕足够宽。




第一次初识它时,只觉得天空很蓝,云淡风清。后来叫了GCC和VC过来和它认识了一下以后,狂风大作,昏天黑地。

在我所了解到的知识中,杨辉三角的输出算法比较简单的有两种,一种是需要申请数组的,用前一行的值计算出下一行的值;另一种是不需要申请数组的,直接根据行号来得出每一个数。输出10行的简单demo分别如下:

//需要申请数组的
int a[10] = {0};

a[0] = 1;
a[1] = 1;
cout << “1″ << endl;
cout << “1 1″ << endl;

for(int i = 2; i < 10; i++)
{
for(int j = i; j > 0; j–)
{
a[j] = a[j] + a[j-1];
cout << a[j] << ” “;
}
cout << “1″ << endl;
}

这是第一种,需要申请一个行号那么大的数组。
 
//不需要申请数组的
for(int i = 1; i <= 10; i++)
{
int n = 1;
cout << n << ” “;
for(int j = 1; j < i; j++)
{
n = n*(i-j)/j;
cout << n << ” “;
}
cout << endl;
}

这种不需要申请数组,每次根据行号来计算每一个数,只需要存储个中间变量就行了。

本来以为,我只要抛出上面两种方法中随便一种,就可以秒杀此题的,后来才发现,一切都不是那么简单的……

分析:

一、栈空间的限制。分配一个int[1000000]在栈中是不行的,经过我的测试,在GCC下,如果main函数里面除了这个数组和一个循环变量外什么其它变量都不申请,最多可以申请int[520635],否则将产生段错误;而在VC下,这个数字更是将减少到258651。这应该就是默认分配的一个函数的栈空间大小,估算一下GCC编译器下应该是2MB,而VC下应该是1MB。所以分配一个1000000的int至少需要扩容栈空间后才能做,或者,将其分配到堆空间或静态区。

二、数据溢出。在杨辉三角计算到第31行时,int型数据就已经溢出,这样一样,就算我申请为__int64等类型,那也肯定是过不了百行的,数据用什么样的数据类型或结构存储很成问题。大数存储?比如使用vector向量容器来解决。

显然面对这个数量级,申请一个数组来储存它是不明智的。。。那么比较好一点的处理方案会是从不需要申请数组的方案中生出来。以前也写过一点大数存储的东西,是用vector限制每个元素的位数然后进位,但又是乘又是除的,我没什么太好的思路……



这是以前在笔试中遇到的一个问题,后来仔细想了想,还是没能把它干掉,问题老挂着会让人抑郁的,期待坛中的牛人们来点思路来点demo。。。
...全文
404 21 打赏 收藏 转发到动态 举报
写回复
用AI写文章
21 条回复
切换为时间正序
请发表友善的回复…
发表回复
starboy520 2010-04-07
  • 打赏
  • 举报
回复
ls请问,gmp是什么。。。。我好弱
cattycat 2010-04-07
  • 打赏
  • 举报
回复
分配空间的要实现大数加法,要动态分配空间。
不分配空间的,要还要实现大数乘除了。
mLee79 2010-04-07
  • 打赏
  • 举报
回复
简单的试了下, 在我的破本上, 如果要输出结果, 那还是洗洗睡吧.....
如果不输出结果, 试了下 10K 用时大概是 23秒, 20K 用时大概是 3分钟, 基本上还是洗洗睡吧.....
gmp 性能不算非常好, 特别是10进制格式化输出的时候, 八过基本上要比 gmp 快一点点都是要死很多脑细胞的, 看起来这个不是个好问题.....

#include <stdio.h>
#include <stdlib.h>
#include <gmp.h>

#ifndef XNUM
#define XNUM 1024
#endif

int main()
{
int i , j ;
mpz_t *arr = (mpz_t*)malloc( XNUM * sizeof( mpz_t ) );

for( i = 0; i < XNUM; ++i )
mpz_init( arr[i] );

mpz_set_ui( arr[0] , 1 );
mpz_set_ui( arr[1] , 1 );

printf( "1\n1 1\n" );
for( i = 2; i < XNUM; ++i )
{
if( 0 == i%(XNUM/100) )
{
fputc( '.' , stderr );
fflush(stderr);
}
for( j = i; j > 0 ; --j )
{
mpz_add( arr[j] , arr[j] , arr[j-1] );
gmp_printf( "%Zd " , arr[j] );
}
printf( "1\n" );
}
fputc( '\n' , stderr );

for( i = 0; i < XNUM; ++i )
mpz_clear( arr[i] );
return 0;
}

1K 情况下生存的数据文件是 74M
$ gcc -DXNUM=1024 -O3 -Os c1.c -lgmp && time ./a > result
.....................................................................................................

real 0m5.860s
user 0m3.405s
sys 0m1.265s

2K 情况下生存的数据文件是 592M
$ gcc -DXNUM=2048 -O3 -Os c1.c -lgmp && time ./a > result
......................................................................................................

real 0m47.219s
user 0m31.124s
sys 0m8.562s

4K 情况下生存的数据文件是 4.62G
$ gcc -DXNUM=4096 -O3 -Os c1.c -lgmp && time ./a > result
......................................................................................................

real 6m40.796s
user 4m30.796s
sys 1m11.421s

更大的 8K+ , 16K+ 啥的时候还是洗洗睡吧.....

lovesi3344 2010-04-07
  • 打赏
  • 举报
回复
学习了
汉森cd 2010-04-07
  • 打赏
  • 举报
回复
第一个问题,咱能想到的是用数据库或者文件等方式解决,第二个问题,只能GMP了(俺不懂具体做法,但百度了下看到了貌似是能计算任意精度的意思)
mzlogin 2010-04-07
  • 打赏
  • 举报
回复
[Quote=引用 15 楼 mlee79 的回复:]

C/C++ code
简单的试了下, 在我的破本上, 如果要输出结果, 那还是洗洗睡吧.....
如果不输出结果, 试了下 10K 用时大概是 23秒, 20K 用时大概是 3分钟, 基本上还是洗洗睡吧.....
gmp 性能不算非常好, 特别是10进制格式化输出的时候, 八过基本上要比 gmp 快一点点都是要死很多脑细胞的, 看起来这个不是个好问题.....

#include <stdi……
[/Quote]

这貌似是个不错的解决之道……
我们求的也不是非得说要把这一百万行全部输出来,
只是求一个理论上可行实际中应该可行的方案而已,
有事要出去,回来再细看关于GMP的东西。。。
期待更多解答。
mLee79 2010-04-06
  • 打赏
  • 举报
回复
用 gmp 吧....
mLee79 2010-04-06
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 mzlogin 的回复:]

引用 11 楼 mlee79 的回复:

1E6行数据不过 1E12 大小左右, long long 足矣, 完全不用虾米大数运算....


不会吧。。。
我看了下每行最大数是接近指数增长的。。。。
离百万行级别的存储long long还是不够的。。。
[/Quote]
嗯, 头发晕了, 你是对的, C( n , a ) 按阶乘增长...
mzlogin 2010-04-06
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 mlee79 的回复:]

1E6行数据不过 1E12 大小左右, long long 足矣, 完全不用虾米大数运算....
[/Quote]

不会吧。。。
我看了下每行最大数是接近指数增长的。。。。
离百万行级别的存储long long还是不够的。。。
mLee79 2010-04-06
  • 打赏
  • 举报
回复
1E6行数据不过 1E12 大小左右, long long 足矣, 完全不用虾米大数运算....
赵4老师 2010-04-06
  • 打赏
  • 举报
回复
请复习小学一年级加法竖式。
嘎文 2010-04-06
  • 打赏
  • 举报
回复
有点惊人,这溢出总是个问题
xiuxianshen 2010-04-06
  • 打赏
  • 举报
回复
UP
验证码识别 2010-04-06
  • 打赏
  • 举报
回复
up up
十八道胡同 2010-04-06
  • 打赏
  • 举报
回复
按照 n = n*(i-j)/j; 来说
要实现大数的乘法 加法和除法
jackyjkchen 2010-04-06
  • 打赏
  • 举报
回复
用大数库是做高效的解决方案。

楼主不用再纠结了
jackyjkchen 2010-04-06
  • 打赏
  • 举报
回复
……你猜得没错,数组分配的空间限制就是栈,不过VC和gcc都可以修改默认栈,当然,分配大数组不是好的编程习惯
bullbat 2010-04-06
  • 打赏
  • 举报
回复
呵呵,友情帮顶。。
candeabc123 2010-04-06
  • 打赏
  • 举报
回复
太强悍了!
fallening 2010-04-06
  • 打赏
  • 举报
回复
你只需要实现一个大数加法

64,648

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧