注册 登录
编程论坛 C++教室

[公告]挑战C板块的 阶乘算法

kai 发布于 2005-10-31 06:51, 4873 次点击
昨天C板块搞了一个编程比赛, 题目就是那道阶乘题,具体来讲就是 1000!
据Knocker 说,他们最快的速度为 7.9 秒左右。

你们的任务就是超越这个速度,程序对语言不做限制。
考虑到对速度测试的公正性,特邀请 Knocker 测试大家的程序。

比赛截至时间为中国时间 11月1日 下午16:00

到比赛截至,我将公布我的代码。

[此贴子已经被作者于2005-10-31 7:01:08编辑过]

50 回复
#2
Knocker2005-10-31 08:59
[QUOTE]据Knocker 说,他们最快的速度为 7.9 秒左右。[/QUOTE]

PII 233 下计算50次1000!合计所需时间7.9 秒
#3
kai2005-10-31 09:19
你看看10000!要用多少时间啊?
要不要看看我的程序啊?
#4
Knocker2005-10-31 09:23

好,我与你比10000!

#5
kai2005-10-31 09:26
我的程序很慢的,是他们的100000倍左右。
#6
kai2005-10-31 09:28
那我们都到最后一刻交卷吧。这样公平些。就用你的机器测试吧。
#7
Knocker2005-10-31 11:59
11月1日 下午20:00
#8
kai2005-10-31 21:36
女士们先生们,大家努力啊,不能让这个knocker 小瞧我们C++ 板块啊。

我出3000分 作为奖励,大家努力啊,捍卫我们这里的荣誉啊。

#9
Knocker2005-11-01 13:34
30000也没用,这次你吹大了
#10
zinking2005-11-01 15:33
n!=1*2*3*....*n → lg(n!)=lg(1*2*3*....*n)=lg(1)+lg(2)+ lg(3)+..+lg(n) → n!=10^(lg(1)+lg(2)+ lg(3)+..+lg(n))

只是这样的话精度不够了
#11
kai2005-11-01 20:01
knocker,
你的程序在哪里啊?
#12
Knocker2005-11-01 20:10
你的在什么地方?
#13
kai2005-11-01 20:11
my code:
[CODE]

#include <iostream>
#include <cstdlib>
#include <climits>
#include <cmath>
#include <iomanip>
#include <cfloat>
#include <windows.h>
using namespace std;

class Factorial
{
private:
int n;
int length;
int base;
int * result;
void setLength()
{
int max = INT_MAX / n;
for( ; max>=base; base = base*10)
;
base = base / 10;
double test = 0;
for(int i = 2; i<= n; i++)
test += log10(i);
length = (int) (test) / (log10(base)) + 1;
}
void mul()
{
int carry = 0;
int r = 0;
for(int j = 3; j<=n; j++)
{
for(int i = length - 1; i>=0; i--)
{
r = result[i] * j + carry;
result[i] = r%base;
carry = r/base;
}
}
}
public:
Factorial(int n)
{
this->n = n;
base = 10;
setLength();
result = new int[length];
memset(result, 0, length*sizeof(int));
result[length-1] = 2;
}
~Factorial()
{
if(result)
delete [] result;
}
void fact()
{
memset(result, 0, length*sizeof(int));
result[length-1] = 2;
mul();
}
void display()
{
cout<<result[0];
cout.fill('0');
for(int i = 1; i<length; i++)
{
cout<<setw(log10(base))<<result[i];
}
cout<<endl;
}
};

int main()
{
LARGE_INTEGER listart,lifinish,lifrequency;
__int64 result;
int n = 10000;
QueryPerformanceCounter( &listart );
// excution
Factorial fac(n);
fac.fact();
QueryPerformanceCounter(&lifinish);
QueryPerformanceFrequency(&lifrequency);
result = (lifinish.QuadPart - listart.QuadPart)*1000000/lifrequency.QuadPart;
char dauer[20];
_i64toa(result, dauer, 10);
fac.display();
cout<<dauer<<endl;

system("pause");
return 0;
}


[/CODE]
#14
kai2005-11-01 20:13

走了,明天再来看你的。

#15
Knocker2005-11-01 20:14

[QUOTE]#include <stdio.h>
#include <math.h>
#include <malloc.h>
#include <time.h>
#include <stdlib.h>
#define CARRY 100000
clock_t start,end ;
int GetFactorialMemSize(int n);
long*InitiFactorialMem(int Size);
int Factorial(register long*FactorialMem,int n);
void PrintFactorial(long*FactorialMem,int Start);
int main(void)
{
int N,FactorialStart,FactorialMemSize ;
long*PFactorial ;
char Key='y' ;
while(Key=='y'||Key=='Y')
{
printf("N=?");
while(scanf("%d",&N)!=1)getchar();
FactorialMemSize=GetFactorialMemSize(N)/5+1 ;
PFactorial=InitiFactorialMem(FactorialMemSize);
start=clock();
FactorialStart=Factorial(PFactorial,N);
end=clock();
printf("\n计算%d!花费: %f 秒\n按回车打印....\n\n",N,(float)(end-start)/CLK_TCK);
getchar();
getchar();
start=clock();
PrintFactorial(PFactorial,FactorialStart);
end=clock();
printf("\n\n输出%d!花费: %f 秒\n",N,(float)(end-start)/CLK_TCK);
free(PFactorial);
printf("继续? y 或 Y 退出? 任意键\n");
Key=getchar();
}
return 0 ;
}
/*-----------------------------------------------------------------*/
int GetFactorialMemSize(int n)
{
double sum=1.0 ;
int i ;
for(i=1;i<=n;i++)
sum+=log10((long double)i);
return(int)sum ;
}
/*-------------------------------------------------------------------*/
long*InitiFactorialMem(int Size)
{
long*FactorialMem=(long*)calloc(Size,sizeof(long));
if(!FactorialMem)
{
printf(" Allocation Error !\n");
exit(1);
}
return FactorialMem ;
}
/*------------------------------------------------------------------*/
int Factorial(register long*FactorialMem,int n)
{
int NotZero=0,End=0,i,j ;
long int CARRY_number ;
register long int tem ;
FactorialMem[0]=1 ;
for(i=1;i<=n;i++)
{
CARRY_number=0 ;
for(j=NotZero;j<=End;j++)
{
tem=FactorialMem[j]*i+CARRY_number ;
CARRY_number=tem/CARRY ;
FactorialMem[j]=tem%CARRY ;
}
while(!FactorialMem[NotZero])NotZero++;
if(CARRY_number)FactorialMem[++End]=CARRY_number ;
}
return End ;
}
/*-------------------------------------------------------------------*/
void PrintFactorial(long*FactorialMem,int Start)
{
printf("%ld",FactorialMem[Start--]);
while(Start>=0)printf("%.5ld",FactorialMem[Start--]);
}[/QUOTE]

#16
Knocker2005-11-01 20:24

你传个执行文件上来,你的程序我编译不了,

KKK.cpp
Linking...
LIBC.lib(wincrt0.obj) : error LNK2001: unresolved external symbol _WinMain@16
Release/KKK.exe : fatal error LNK1120: 1 unresolved externals
Error executing link.exe.

KKK.exe - 2 error(s), 0 warning(s)

#17
Knocker2005-11-01 20:32
你的程序是我一倍,慢一倍
#18
Knocker2005-11-01 20:34
3000分是我的了
#19
Knocker2005-11-01 20:40

环境:PII 233 winme
我的


[QUOTE]N=?10000
计算10000!花费: 4.500000 秒
按回车打印....

............(略去)
0000000000000000000000000000000000
0000000000000000000000000000000000
0000000000000000000000000000000000
输出10000!花费: 6.590000 秒
继续? y 或 Y 退出? 任意键[/QUOTE]

你的,计算部分就要

23秒多

#20
zinking2005-11-01 22:01
高兴什么,你的也不是最快的,我查到一种算法实现了,0.07秒,我这里只有思路。
所以,knocker你的程序也不是最强的!所以不要太k........
阶乘,是求一组数列的乘积,其效率的高低,一、是取决于高精度乘法算法,二、是针对阶乘自身特点算法的优化。
前者,是一种广为习知的技术,虽然不同算法效率可天壤之别,但终究可以通过学习获得,甚至可用鲁迅先生的“拿来主义”;
后者,则有许多的发展空间,这里我将介绍一种由我完全独创的一种方法,欢迎大家讨论,如能起到“抛砖引玉”的效果,那就真正达到了目的。

我在开发“阶乘”类算法时,始终遵循如下原则:
  • 参与高精度乘法算法的两数,大小应尽可能地相近;
  • 尽可能将乘法转化为乘方;
  • 尽可能地优先调用平方;

言归正转,下面以精确计算 1000! 为例,阐述该算法:

记 F1(n) = n*(n-1)*...*1;
F2(n) = n*(n-2)*...*(2 or 1);
Pow2(r) = 2^r;
有 F1(2k+1) = F2(2k+1) * F2(2k)
= Pow2(k) * F2(2k+1) * F1(k),
F1(2k) = Pow2(k) * F2(2k-1) * F1(k),
及 Pow2(u) * Pow2(v) = Pow2(u+v),

∴ 1000! = F1(1000)
= Pow2(500)*F2(999)*F1(500)
= Pow2(750)*F2(999)*F2(499)*F1(250)
= Pow2(875)*F2(999)*F2(499)*F2(249)*F1(125)
= Pow2(937)*F2(999)*F2(499)*F2(249)*F2(125)*F1(62)
= Pow2(968)*F2(999)*F2(499)*F2(249)*F2(125)*F2(61)*F1(31)
= Pow2(983)*F2(999)*F2(499)*F2(249)*F2(125)*F2(61)*F2(31)*F1(15)
= ...

如果你预存了某些小整数阶乘(比如这里的“F1(15)”),则可提前终止分解,否则直至右边最后一项为“F1(1)”为止;这样,我们将阶乘转化为2的整数次幂与一些连续奇数的积(或再乘以一个小整数的阶乘);
#21
zinking2005-11-01 22:03

纠正一下

Factorial.exe
Ver2.1.0.1
Celeron(tm) 466MHz
64MB RAM, Win98Sec
Pentium(R) 4 1.70GHz
256MB RAM, WinXP
Result
10,000! 0.069s 0.031s 2.8462... x 10^35,659

#22
yaoguai20052005-11-01 22:16
0.07
好可怕的算法,
比较才看出差距,
天外有天

[此贴子已经被作者于2005-11-1 22:17:21编辑过]

#23
zinking2005-11-01 22:17

哈哈,程序严重有问题。
kai 的10000!的前几位280202.........
knocker 的10000!的前几位4393181........
网上公布的测试过的 10000!
2846259680
9170545189 0641321211 9868890148 0514017027 9923079417
9994274411 3400037644 4377299078 6757784775 8158840621
.............
这程序你们都有底吗,到底谁对了!

#24
Knocker2005-11-01 22:54
唉,你找的是5000!不是10000!同志!
#25
zinking2005-11-01 22:57
以下是引用knocker在2005-11-1 22:54:05的发言:
唉,你找的是5000!不是10000!同志!

没弄明白!

进一步的研究发现,knocker的算法有抄袭乌鸦丘比特的嫌疑,只是对他的算法的无关紧要部分进行了美化
加入了华丽的内存分配,但是主体部分是显然一样的!

#26
Knocker2005-11-01 22:58
不是5000!不过我的肯定是没错
#27
Knocker2005-11-01 23:09
2846259680

9170545189

0641321211

9868890148

0514017027

9923079417

9994274411

没错啊?我的是这个,我不知道你是怎么搞的?
#28
Knocker2005-11-01 23:12
以下是引用zinking在2005-11-1 22:57:55的发言:

没弄明白!

进一步的研究发现,knocker的算法有抄袭乌鸦丘比特的嫌疑,只是对他的算法的无关紧要部分进行了美化
加入了华丽的内存分配,但是主体部分是显然一样的!

哈哈,但速度不一样,你可以去看看我N年前写的

https://www.bc-cn.net/bbs/dispbbs.asp?boardID=179&ID=31537&page=1

7楼,看看算法是不是一样?

#29
Knocker2005-11-01 23:18
我也不K,但是,你C++来一个比我快的,而且我又拿不出更快的(我还有更快的 PII 233 10000!*秒^_^),我就服了,你找的两个算法请实现之。
#30
Knocker2005-11-01 23:27
我测试了kai的,虽然慢一点^_^,但是结果也是对的,鬼知道你是怎么做的
#31
kai2005-11-02 03:34
knocker,
你的程序能做50000 吗?
#32
Knocker2005-11-02 10:47
你的3000分是不是舍不得给我啊?在这里胡纠蛮缠!

int Factorial(register long*FactorialMem,int n);

这n是int 的,最多能sacnf 32767 ,你说能计算50000!吗?

根本不是算法问题,计算50000!我的程序只要115.67秒
#33
Knocker2005-11-02 10:47
给我钱!
#34
freeforever2005-11-02 11:00

我是今天才注册的,也写了这个算法的代码,不过我不会算时间,请那位帮我算算时间,谢谢:

#include<stdio.h>
/*the max is 1144*/
main()
{ int intx;
/*printf("\nInput:");
scanf("%d",&intx);*/
for(intx=0;intx<50;intx++)
opt(1000);
}

opt(int x)
{ int res[3005],reslen,i,j,k,tmp;
res[1]=reslen=1;
for(i=1;i<=x;i++)
{ for(j=1;j<=reslen;j++)
res[j]*=i;
for(k=1;k<=reslen;k++)
if(res[k]>9)
{ tmp=res[k];
res[k]%=10;
if(k==reslen)
res[++reslen]=0;
res[k+1]+=tmp/10;
}
}
printf("\n");
/*for(i=reslen;i>=1;i--)
printf("%d",res[i]);*/
printf("\n\nLenth: %d",reslen);/*getch();*/
}

#35
live412005-11-02 19:20

不要斗,to kai,有没有看过《C++性能优化***》(忘了书名),反正是蓝色封面很薄的一本书,里面说到,C++比起C语言有一定的起伏,就是,有些命令比C快,有些比C慢,而且慢N倍,其中的绞绞者就是类,用类的话,就算全部inline也不及C快,所以没得比。

这种变量单一的算法题体现不了C++的优点,要用我以前问过那种求路径最长的旅游那种N个变量N个函数N种的关系的,用C就会做到头晕。

#36
live412005-11-02 19:23
那里提到,C语言的速度是最接近汇编语言的,C++的类有时需要用到300行汇编代码来实现。对象化是要付出代价的。
#37
live412005-11-02 19:25
to knocker:不要得意,C语言没有派生,没有模板,扩展程序时超费工夫。
#38
sailer2005-11-02 23:00
都很牛啊。学生佩服,我只是略微看懂一点你们的程序。佩服!我要研究以下你们的程序啊 !
#39
Knocker2005-11-02 23:47
我只想要属于我的3000分。
#40
kai2005-11-03 04:53
你的程序不能得出真确的结果。所以没法给你分。
你只想到了快,忽略了程序的通用性。
如果要快,那干脆把结果直接打印,那是最快的。但是这样一来,程序也就没有意义了。算法是建立在真确的基础上,没有真确,那么局部意义上的快就没有意义了。

对你是严格了一些。
#41
神vLinux飘飘2005-11-03 06:28

哈哈哈哈,学过计算机组成原理后就知道,如果想比快,没有任何东西比精练的汇编来得凶悍了~~

#42
激情依旧2005-11-03 07:32
嘻~~~~~~~~如果要快直接用二进制来写算法。那是最快的。 二进制谁于争锋。
承认c++在某些地方不够c快。但是c++的模板真的太好用了。其中感觉最深的就是压栈。比如要压入Node 类型的节点。用c语言真的很麻烦。如果又在压另外一种类型的。那用c语言就真的够你晕了。c有c的优势。c++有c++的好。青菜萝卜个有所爱。我爱c++
#43
Knocker2005-11-03 10:42

To: kai 版主的二次方

假如你记性不好,我可以帮你回忆一下你设立这个贴子的目的:你是为了打击C版的学弟学妹们,你认为他们写的程序计算50次1000!竟然要7秒多,真是烂得可以。所以,你还提出一个口号:挑战速度的极限。
现在,你用什么通用性来搪塞我,只是为不给我3000分找理由!而且我们也说定比的是10000!的速度,不是10001!也不是9999!所以,你的理由不成立。
不给我3000分,你就对不行C版的学弟学妹!你是我的偶像,不给我3000分,你在我心中
的光辉形像就大打折扣了。^_^

To: 神vLinux飘飘 前C高手版版主

你说的是否正确,我愚味不知道。你就用汇编写,我用C,我们俩个再比。可是,你会汇编么?哈哈,哈哈,要么你用JAVA,JAVA你会的。哈哈,哈哈,B4你这个PT。

To: 激情依旧 现任版主

C++方便是因为别人把你认为烦的事都处理好了。如果你用VB,BCB写程序还会更方便。

#44
live412005-11-04 15:14
楼上的分明找打,我会汇编,但没心情搞。
#45
live412005-11-04 15:21
to 43楼(bc-cn现任白目/白痴版主):

写个1000!有什么?你写个10000000000000000!,然后自己用晶体管加单片机做个cpu出来运算才叫牛B,你用Intel和AMD的cpu怎么算都是老外帮你算,reg、alu和mem都是别人的,你只不过是告诉他们1000!的运算结构罢了。


“不给我3000分,你就对不行C版的学弟学妹!你是我的偶像,不给我3000分,你在我心中的光辉形像就大打折扣了。^_^”


为了钱连良心都出卖了耶~~~
#46
Knocker2005-11-04 15:26

楼上,你用汇编先写个比我快的再来打我吧

反正我钱已到手,随你怎么讲,你不服的话还可以再比。汇编?给我几星期时间,我也能写

#47
live412005-11-05 12:52

草,你去死吧。。。我在看dbx协议没空理你。

kai居然屈服,钱是事小,给老k的烂舌说服了事大。

不愤中......

#48
Knocker2005-11-05 14:14
假如你用汇编写个10000!也没我快的话,这个....这个....事就更大了....
#49
错吻星空2005-11-06 22:48
好看啊
#50
_20072007-11-02 20:52
以下是引用freeforever在2005-11-2 11:00:00的发言:

我是今天才注册的,也写了这个算法的代码,不过我不会算时间,请那位帮我算算时间,谢谢:

#include<stdio.h>
/*the max is 1144*/
main()
{ int intx;
/*printf("\nInput:");
scanf("%d",&intx);*/
for(intx=0;intx<50;intx++)
opt(1000);
}

opt(int x)
{ int res[3005],reslen,i,j,k,tmp;
res[1]=reslen=1;
for(i=1;i<=x;i++)
{ for(j=1;j<=reslen;j++)
res[j]*=i;
for(k=1;k<=reslen;k++)
if(res[k]>9)
{ tmp=res[k];
res[k]%=10;
if(k==reslen)
res[++reslen]=0;
res[k+1]+=tmp/10;
}
}
printf("\n");
/*for(i=reslen;i>=1;i--)
printf("%d",res[i]);*/
printf("\n\nLenth: %d",reslen);/*getch();*/
}

同志,终于看到一个C的,问一下,为啥我复制运行之后,老是LENTH 2568???

这代表啥?

#51
a217zxg2007-11-03 21:09
   牛人火拼,别忘记原则,我觉得输了就得认了,强烈支持给分,我是只懂一点啊
1