无心人 发表于 2010-8-23 20:56:07

:)

我重读这个帖子,期待对另外一个问题程序的改进

wayne 发表于 2010-8-24 09:25:04

我期待这个题有一个新的突破

云梦 发表于 2012-10-31 08:09:30

如果允许小数的话,问题就变得很简单了。
比如 n!=3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446 E1000000000000
那么n=94851898540.1396977006676482198750221184274755718919148371815155857816611145105279808242712122064124653944534478504904850688162913

clockman 发表于 2012-12-1 20:22:20

1# medie2005

做新手任务,凑字数

郭先抢 发表于 2012-12-3 18:16:10

这个题目真的很有意思!

clockman 发表于 2012-12-4 16:16:05

1# medie2005


难度确实挺大

gua64 发表于 2012-12-6 18:08:52

我曾发过帖子说2的幂能以任何数字开始
这个问题和那帖子上的问题类似
但更难做了
无心人 发表于 2008-12-4 11:38 http://bbs.emath.ac.cn/images/common/back.gif
Matlab?

liangbch 发表于 2012-12-12 14:56:54

62# 无心人

今天仔细考虑了这个题目。我认为无需使用大数库。无心人的程序中调用了大数库,而且用到对数,故速度较慢。其实这个题目虽然需要做高精度计算,但只用到小整数乘以大数的运算,完全可以自己实现。我刚刚写了一个程序,未用到任何大数库。这个程序从1开始,一直查找到2^32-1,在我的电脑上只需要3分钟左右。

说明:
    1. 我在程序中设定的精度是4个DWORD,每个DWORD表示9位10进制数,故最大精度不超过36位,若需更高精度,则只需改动一处即可。
    2. 目前的代码最多可计算到2^32-1,若需要扩大搜索范围,需要实现一个64bit数和大数相乘的算法。
    3. 我的程序依次计算每个32位整数n,求n!的科学计数法表示和pi的差,若其差小于当前的最小值,则输出n和n!.

下面是我的程序的输出结果:
20!=2.432902008176640000*10^18
23!=2.5852016738884976640000*10^22
28!=3.04888344611713860501504000000*10^29
154!=3.0897696138473508879585646636*10^271
332!=3.1034430734498676144426322732*10^694
612!=3.1345284031431087019726722580*10^1441
2703!=3.1398094010536875630936179148846*10^8104
8945!=3.139993064736104560624185498*10^31464
17254!=3.14084642621024495446191444792*10^65612
28726!=3.14106463595708329690285209393642766*10^115595
50583!=3.1415379577347882738422653077918*10^215977
527600!=3.14155838836242816120923043886*10^2789957
780018!=3.1415890794236473559982729593497*10^4257193
1812538!=3.141589535267260763834817102767*10^10556211
5385973!=3.141591234607732809306850985*10^33915312
5573998!=3.14159238184880918674222498012222406*10^35182367
52604972!=3.1415925665166406146782547740*10^383318353
65630447!=3.141592602756036723755404377401952*10^484537182
187024036!=3.141592617297502030148476882134*10^1465820139
410390476!=3.141592622630656164649753699858562*10^3356543814
464736214!=3.14159264915921444226889681595*10^3826132373
688395641!=3.141592651962886290907283002629*10^5784962808
It took 188 secs

liangbch 发表于 2012-12-12 14:59:02

#include "stdlib.h"
#include "stdio.h"
#include "string.h"
#include "windows.h"

typedef __int64INT64;

typedef unsigned long DWORD;

#define DEC_RADIX 1000000000

#define PRECISION_DIV9 4                //10进制数表示的数字个数除以9


const DWORD PI_VALUE[]={
        {3,141592653},
        {31,415926535},
        {314,159265358},
        {3141,592653589},
        {31415,926535897},
        {314159,265358979},
        {3141592,653589793},
        {31415926,535897932},
        {314159265,358979323},
};

double scales[]=
{
        1.00,
        10.00,
        100.00,
        1000.00,
        10000.00,
        100000.00,
        1000000.00,
        10000000.00,
        100000000.00,
};


int Uint32BitCount(DWORD n)
{
        _asm
        {
                bsr eax,n
                inc eax
        }
}

//******************************************************
DWORD DEC_Array_Mul_DWORD_ALU(DWORD *a, size_t len, DWORD num)
//******************************************************
{
        static DWORD _s_TEN9=DEC_RADIX;
        //---------------------------       
        _asm
        {
                push esi

                mov ecx,len
                mov esi,a
                lea esi,
                xor ecx,ecx                        //carry

                test esi,3                        //the a must be 4byte align
                jnzthisExit

                jmp cmp000

loop_start:
                mov eax,
                mul dword ptr num
                add eax,ecx
                adc edx,0
                div dword ptr _s_TEN9
                mov ,edx
                mov ecx,eax
                sub esi,4
cmp000:
                cmp esi,a
                jae loop_start
thisExit:
                mov eax,ecx
                pop esi
        }
}

double check_diff(DWORD num[],int shift)
{
        int t,carry;
        DWORD tArr;

        tArr=PI_VALUE;
        tArr=PI_VALUE;
       
        t=tArr-num;
        if (t<0)
        {
                carry=1;
                t+=DEC_RADIX;
        }
        else
                carry=0;

        tArr=t;
       
        t=tArr-num-carry;
        tArr=t;

        if (t<0)
        {
                return 999;
        }
        else
        {
                double diff;
                diff =tArr + tArr * 0.000000001;
                diff /= scales;
                return diff;

        }
}

void print_number(DWORD n,DWORD res[],int len,double exp)
{
        char buff;
        char *p;
        int i,s;
        INT64 e;

        p=buff;
        p+=sprintf(p,"%d",res);
        s=strlen(buff);

        if (len>1)
        {
                for (i=1;i<len;i++)
                        p+=sprintf(p,"%09d",res[ i ]);
        }
       
        printf("\n%u!=",n);
        for (i=0;i<strlen(buff);i++)
        {
                printf("%c",buff[ i ]);
                if (i==0)
                        printf(".");
        }

        e=(INT64)(exp+(len-1)*9+s-1);
        printf("*10^%I64d\n",e);
}


void search_n()
{
        DWORD i,j;
        DWORD carry;

        int shift;
        int bc,fac_len;                // fac(i)的结果的数组的长度
        double diff,min_diff;
        double res_exp;
       
        DWORD fac; //fac(i)的结果的数组
        DWORD time;

       
        time=GetTickCount();
        min_diff=1;
        res_exp=0;
        fac=1;
        fac_len=1;
       
        i=1;
        //while (i<DEC_RADIX)
        while (i!=0)
        {
                carry=DEC_Array_Mul_DWORD_ALU(fac,fac_len,i);
                if (carry>=DEC_RADIX)
                {
                        for (j=PRECISION_DIV9-2;j>=2;j--)
                                fac=fac;
                        fac=carry / DEC_RADIX;
                        fac=carry % DEC_RADIX;
                       
                        if ( fac_len+2 <= PRECISION_DIV9)
                                fac_len+=2;
                        else if (fac_len+1 ==PRECISION_DIV9)
                        {
                                fac_len=PRECISION_DIV9;
                                res_exp+=9;
                        }
                        else
                                res_exp+=18;
                }
                else if (carry>0)
                {
                        for (j=PRECISION_DIV9-1;j>=1;j--)
                                fac=fac;
                        fac=carry;
                        if (fac_len<PRECISION_DIV9)
                                fac_len++;
                        else
                                res_exp+=9;
                }
                bc=Uint32BitCount(fac);
               
                switch (bc)
                {
                case 2:
                       shift=0;
                       diff=check_diff(fac,shift);
                       if (diff<min_diff)
                       {
                               min_diff=diff;
                               if (fac_len>=2)
                                       print_number(i,fac,fac_len,res_exp);
                       }
                       break;
                case 5:
                        shift=1;
                        diff=check_diff(fac,shift);
                        if (diff<min_diff)
                        {
                               min_diff=diff;
                               if (fac_len>=2)
                                       print_number(i,fac,fac_len,res_exp);
                       }
                       break;
                case 9:
                       shift=2;
                        diff=check_diff(fac,shift);
                       if (diff<min_diff)
                       {
                               min_diff=diff;
                               if (fac_len>=2)
                                       print_number(i,fac,fac_len,res_exp);
                       }
                       break;
                case 12:
                       shift=3;
                        diff=check_diff(fac,shift);
                       if (diff<min_diff)
                       {
                               min_diff=diff;
                               if (fac_len>=2)
                                       print_number(i,fac,fac_len,res_exp);
                       }
                       break;
                case 15:
                       shift=4;
                        diff=check_diff(fac,shift);
                       if (diff<min_diff)
                       {
                               min_diff=diff;
                               if (fac_len>=2)
                                       print_number(i,fac,fac_len,res_exp);
                       }
                       break;
                case 19:
                       shift=5;
                        diff=check_diff(fac,shift);
                       if (diff<min_diff)
                       {
                               min_diff=diff;
                               if (fac_len>=2)
                                       print_number(i,fac,fac_len,res_exp);
                       }
                       break;
                case 22:
                       shift=6;
                        diff=check_diff(fac,shift);
                       if (diff<min_diff)
                       {
                               min_diff=diff;
                               if (fac_len>=2)
                                       print_number(i,fac,fac_len,res_exp);
                       }
                       break;
                case 25:
                       shift=7;
                        diff=check_diff(fac,shift);
                       if (diff<min_diff)
                       {
                               min_diff=diff;
                               if (fac_len>=2)
                                       print_number(i,fac,fac_len,res_exp);
                       }
                       break;
                case 29:
                        shift=8;
                        diff=check_diff(fac,shift);
                       if (diff<min_diff)
                       {
                               min_diff=diff;
                               if (fac_len>=2)
                                       print_number(i,fac,fac_len,res_exp);
                       }
                       break;
                default:
                        break;
                }
                i++;
        }
       
        time=(GetTickCount()-time);
        printf("It took %d secs\n",time/1000);

}

int main(int argc, char* argv[])
{
        search_n();
        return 0;
}

liangbch 发表于 2012-12-12 21:34:01

刚才仔细看了一下无心人的代码,明白了其所用的算法。其主要算法如下,
从start开始,逐个计算每个整数n的阶乘的常用对数的小数部分,记作log(fac(n)), 和PI的常用对数的小数部分比较,符合条件则输出。
    当n是1000的整数倍时,计算base=n, lf0=log10(fac(base))的小数部分,
    若n不是base的整数倍时,则计算从base+1到n之间所有数的累乘积的自然对数的小数部分lf1, 则 n!的对数的小数部分为 lf= lf0+lf1。

无心人的算法主要受制于 c语言库中(或者CPU浮点指令)中的双精度数的对数函数精度的制约,很难提高精度。


受无心人的启发,我打算编写一个不使用求双精度对数和多重精度对数函数的程序。
主要特点有:
1. 可计算到10^15或者更高。
2. 可分段计算。
3. 当n小于10亿时,使用自己写的多重精度大数乘以32bit整数的子程序,计算n的阶乘,fac(n),以10^9进制表示.
   当n>=10亿且n是100万的倍数时,使用斯特林公式的普通形式(而不是指数形式)计算 fac(n),以10^9进制表示,记作fac_0
   当n>10亿且n不是100万的倍数时,令n=base+d(base是100万的整数倍,d>0 且 d<base),使用自己写的多重精度大数乘以64bit整数的子程序,计算base+1到n的累乘积,记作fac_1, 则n的阶乘fac(n)=fac0*fac1

我的算法和无心人算法的差别,我的记作B,无心人的记作A。
    1. B使用斯特林公式的普通形式,而A使用斯特林公式的对数形式。
    2. B不计算n的阶乘的常用对数,而是直接计算n的阶乘的值,以10亿进制的表示形式。
    3. A的核心部分为计算64bit整数的的常用对数,使用C语言函数库,A受制于double型数的精度,无法提高精度。 B的和核心部分计算一个多精度数与64bit整数的乘积,可通过增加多精度数数的长度来提高精度。
    4. 性能的PK. 主要看 多精度数与64bit整数的乘积 VS log10(double),那个算得更快。
页: 1 2 3 4 5 6 7 8 [9] 10 11 12 13 14
查看完整版本: 阶乘和圆周率