【问题标题】:Calculate the factorial of an arbitrarily large number, showing all the digits计算任意大数的阶乘,显示所有数字
【发布时间】:2009-12-27 14:33:16
【问题描述】:

我最近在一次采访中被要求描述一种计算任意大数的阶乘的方法;一种我们获得所有答案数字的方法。

我搜索了各个地方并在几个论坛中询问。但我想知道是否有任何方法可以在不使用 GMP 之类的库的情况下完成此操作。

谢谢。

【问题讨论】:

    标签: c++ factorial


    【解决方案1】:

    GNU 多精度库是一个不错的库!但是既然你说不允许使用外部库,我认为唯一可行的方法是获取一个 int 数组,然后像在纸上用笔一样乘以数字!

    这是我前段时间写的代码..

    #include<iostream>
    #include<cstring>
    
    int max = 5000;
    
    void display(int arr[]){
        int ctr = 0;
        for (int i=0; i<max; i++){
            if (!ctr && arr[i])         ctr = 1;
            if(ctr)
                std::cout<<arr[i];
        }
    }
    
    
    void factorial(int arr[], int n){
        if (!n) return;
        int carry = 0;
        for (int i=max-1; i>=0; --i){
            arr[i] = (arr[i] * n) + carry;
            carry = arr[i]/10;
            arr[i] %= 10;
        }
        factorial(arr,n-1);
    }
    
    int main(){
        int *arr = new int[max];
        std::memset(arr,0,max*sizeof(int));
        arr[max-1] = 1;
        int num;
        std::cout<<"Enter the number: ";
        std::cin>>num;
        std::cout<<"factorial of "<<num<<"is :\n";
        factorial(arr,num);
        display(arr);
        delete[] arr;
        return 0;
    }
    

    'arr' 只是一个整数数组,阶乘是一个简单的函数,可以将给定数字乘以“大数”。

    希望这能解决您的问题..

    【讨论】:

    • main 中的那一行应该是factorial(arr,num),否则你将永远得到 10!输出。让我解决这个问题:)
    • 非常感谢您的即时回复..!这就是我要找的! :)
    • 哎呀!!是的,感谢您指出这一点!我只是复制了我的代码并粘贴到这里!! :P
    • @Prasoon,“更好”是什么意思?
    • @Prasoon:这个解决方案并不意味着高效,那么为什么还要将递归转换为迭代呢?
    【解决方案2】:

    接受的答案很好,但这是 C++;我们可以做得更好。让我们开始我们自己的Bignum 类,它的位数完全没有限制。

    为了获得最高效率,我们将使用纯二进制数,用我们可以有效处理的尽可能多的位来打包每个数组元素。更简单的方法是在每个元素中存储一个十进制数字。在这里,我进行了妥协,在每个 uint32_t 元素中存储 9 个十进制数字。

    数据以小端方式存储,因为当我们需要更高阶的元素时,在末尾扩展 vector 会容易得多。

    一旦我们有了这个类,阶乘函数本身就是简单的。

    #include <assert.h>
    #include <iomanip>
    #include <iostream>
    #include <stdint.h>
    #include <vector>
    
    class Bignum
    {
    public:
        Bignum(int value)
        {
            assert(value >= 0 && value <= 999999999);
            parts.push_back(value);
        }
    
        Bignum& operator*=(int rhs)
        {
            assert(rhs >= 0 && rhs <= 999999999);
            uint32_t carry = 0;
            for (size_t i = 0; i < parts.size(); i++)
            {
                uint64_t product = (uint64_t)parts[i] * (uint64_t)rhs + carry;
                parts[i] = (uint32_t)(product % 1000000000LL);
                carry = (uint32_t)(product / 1000000000LL);
            }
            if (carry != 0)
                parts.push_back(carry);
            return *this;
        }
    
        friend std::ostream & operator<<(std::ostream& stream, const Bignum& num);
    
    private:
        std::vector<uint32_t> parts;
    };
    
    inline std::ostream& operator<<(std::ostream& stream, const Bignum& num)
    {
        char oldfill = stream.fill('0');
        for (std::vector<uint32_t>::const_reverse_iterator it = num.parts.rbegin(); it != num.parts.rend(); it++)
            stream << *it << std::setw(9);
        stream.fill(oldfill);
        stream.width(0);
        return stream;
    }
    
    Bignum factorial(int n)
    {
        Bignum fac = 1;
        for (int i = 2; i <= n; i++)
            fac *= i;
        return fac;
    }
    
    int main(int argc, char* argv[])
    {
        for (int n = 0; n <= 52; n++)
            std::cout << factorial(n) << std::endl;
        return 0;
    }
    

    【讨论】:

      【解决方案3】:

      Srivatsan Iyer 的好解决方案和我的建议是:

      1. 使用 unsigned char 数组而不是使用 int 数组来存储数字仍然可以提高内存效率。 它只需要 int 数组的 25% 的内存需求。

      2. 为了最好的内存优化,我们也可以用单字节来表示一个2位数。 因为只有 4 位就足以表示从 0 到 9 的任何数字。 所以我们可以使用按位运算将两个数字打包在一个字节中。 它将占用 int 数组所需内存的 12.5%。

      【讨论】:

      • 或者我们可以使用 32 位存储九位数字。或 64 位存储 19 位。
      【解决方案4】:

      BigInteger 类可以解决您的问题,并且上面的 C 实现让您了解 BigInt 的实现方式,除了代码针对速度进行了优化并且仅针对计算阶乘进行了定制。

      【讨论】:

        【解决方案5】:

        好吧,您必须使用数组编写自己的数学例程。加法很容易,乘法有点难,但还是可以的。

        编辑:想发布一个示例,但 Srivatsan Iyer 的示例很好。

        【讨论】:

          【解决方案6】:

          我有一个计算阶乘的解决方案,它至少适用于 n

          使用数组的缺点是会留下大量未使用的字节。超过一定的时间,你就不能为数组保留空间了。此外,数组往往会变慢。

          您可以在 ideone 上查看我的程序:http://ideone.com/K410n7

          我相信我的解决方案仍然可以优化。请提出建议。

          include<stdio.h> 
          
          char res[200000];
          
          inline int fact(int n)
          {
          
              int i,j;
          
              register int m,c;
          
              m=1;
          
              res[0]='1';
          
              for(i=2;i<=n;i++)
              {
          
                  c=0;
          
                  for(j=0; j< m; j++){
          
                      c =((res[j]-48)*i)+c;
          
                      res[j]=(c%10)+48;
          
                      c=c/10;
          
                  }
                  while(c>0){
                      res[m]=(c%10)+48;
          
                      c=c/10;
          
                      m++;
          
                  }
          
              }
          
              return m;
          
          }
          
          int main() {
          
          
              int n,i,d;
          
              scanf("%d",&n);
          
          
              d=fact(n);
          
              for(i=d-1;i>=0;i--)
          
                  printf("%c",res[i]);
          
          
              return 0;
          }
          

          【讨论】:

            【解决方案7】:
            #include <iostream>
            using namespace std;
            int main ()
            {
                int i,n,p=1;
                cout<<"Enter a number: ";
                cin>>n;
                cout<<endl;
            
                for (i=1;i<=n; i++)
                {
                    cout<<i<<" X "; 
                    p=p*i;
                }
                cout<<endl<<endl<<p<<" factorial of "<<n;
            
                return 0;
            }
            

            【讨论】:

              【解决方案8】:

              这其实很简单。这里有两种方法。一个是精确的,一个是近似的。对于确切的数字,任何超过 10,000 的数字都需要几秒钟的时间来计算。估计它需要几微秒,直到你达到数百万。如果有人感兴趣,这是斯特林的近似值。

              10,000,000 的因数约为 1.2024234127436e+65657059 这需要 5.9 秒 找到确切的数量需要 34 天。

              <?php
              $test= 3579;
              
              echo 'Factorial of '.$test.'<br><br>';
              
              $tm= microtime( true);
              
              echo 'Exact '.( $f= factorialexact( $test)).' e+'.(strlen( $f)-1).' missing decimal place after first digit<br>';
              
              echo ( microtime( true) - $tm). ' seconds<br><br>';
              
              $tm= microtime( true);
              
              echo 'Aprox '.factorialapprox( $test).'<br>';
              
              echo ( microtime( true) - $tm). ' seconds<br><br>';
              
              
              function factorialexact( $n){
                  $f= '1';
                  for ( $i=$n; $i>1; $i--){
                      $f= JL_bcmul( $f, (''.$i));
                  }
                  return $f;
              }
              
              function factorialapprox( $n){
                  // Stirling's factorial approximation
                  // aprox factorial n = sqrt( 2 * pi * n) * n^n / e^n
                  // store in t the easy part, calc the first term easily
                  $t= sqrt( 2 * 3.14159265358979 * $n);
                  // things get tough from here because for large n
                  // n^n can blow away floating point pachages 
                  // declare exponent of the number
                  $e= 0;
                  // the remaining terms are n^n / e^n
                  // both n and e (natural log) are raised to the same power
                  // that is n, just negative of each other
                  for ( $i=0; $i<$n; $i++){
                      // loop to 
                      // mulitply by n and divide by e for each iteration
                      $t= $t * $n / 2.71828182845904;
                      // exponents are going to get away from us 
                      // so reduce or increase t
                      while ( $t>1000){
                          $t= $t/1000;
                          $e= $e+3;
                      } 
                      while ( $t<0.001){
                          $t= $t*1000;
                          $e= $e-3;
                      } 
                  }
                  // garentee the base number is between 1 and 10
                  while ( $t>=10){
                      $t= $t/10;
                      $e= $e+1;
                  } 
                  while ( $t<1){
                      $t= $t*10;
                      $e= $e-1;
                  } 
                  // return at a floating string.
                  // do not use parseFloat() or floatval() 
                  // $v= explode( 'e', $result); $floatvalue= $v[0] * pow( 10, $v[1]);  
                  // won't work either.  $v[1] is way too large
                  // the exponent can easily be in the tens of thousands
                  $p= '-';
                  if ( $e>=0){ $p= '+'; }
                  return $t.'e'.$p.$e;
              }    
              
              function JL_bcmul( $a, $b){
                  if ( function_exists( 'bcmul')){
                      return bcmul( ( ''.$a), (''.$b));
                  }
                  $s= array();
                  for ($i=0; $i < count( $a) + count( $b); $i++){ $s[$i]= '0'; }
                  $t= 0;
                  for ($i=0; $i < strlen( $b); $i++){ 
                      for ($j=0; $j < strlen( $a); $j++){
                          $t= $s[$i+$j] + intval( $a[strlen( $a) - $j - 1]) * intval( $b[ strlen( $b) - $i - 1]); 
                          $s[$i+$j]= $t % 10;
                          $s[$i+$j+1]= $s[$i+$j+1] + floor( $t / 10);
                      }
                  }
                  $s= array_reverse( $s);
                  return trim( trim(( implode( '', $s).'_'), '0'), '_');
              }
              

              【讨论】:

                【解决方案9】:
                #include<stdio.h>
                #include<string.h>
                char f[10000];
                char factorial[1010][10000];
                void multiply(int k){
                    int ci,sum,i;
                    int len = strlen(f);
                    ci=0;
                    i=0;
                    while(i<len){
                        sum=ci+(f[i] - '0') * k;
                        f[i] = (sum % 10) + '0';
                        i++;
                        ci = sum/10;
                    }
                    while(ci>0){
                        f[i++] = (ci%10) + '0';
                        ci/=10;
                    }
                    f[i]='\0';
                    for(int j=0;j<i;j++)factorial[k][j]=f[j];
                    factorial[k][i]='\0';
                }
                void fac(){
                    int k;
                    strcpy(f,"1");
                    for(k=2;k<=1000;k++)multiply(k);
                }
                void print(int n){
                    int i;
                    int len = strlen(factorial[n]);
                    printf("%d!\n",n);
                    for(i=len-1;i>=0;i--){
                        printf("%c",factorial[n][i]);
                    }
                    printf("\n");
                }
                int main()
                {
                    int n;
                    factorial[0][0]='1';
                    factorial[1][0]='1';
                    fac();
                    while(scanf("%d",&n)==1){
                        print(n);
                    }
                    return 0;
                }
                

                【讨论】:

                  【解决方案10】:

                  代码如下:

                  #include<bits/stdc++.h>
                  using namespace std;
                  #define MAX 5000
                  
                  void factorial(int n)
                  {
                      int carry , res_size = 1, res[MAX];
                      res[0] = 1;
                  
                      for(int x=2; x<=n; x++)
                      {
                          carry = 0;
                          for(int i=0; i<res_size; i++)
                          {
                            int prod = res[i]*x + carry;
                            res[i] = prod % 10;
                            carry  = prod/10;
                          }
                          while (carry)
                          {
                            res[res_size++] = carry%10;
                            carry = carry/10;
                          }
                       }
                       for(int i=res_size-1; i >= 0; i--) 
                       {
                           cout<<res[i];
                       }
                  }
                  int main()
                  {
                        int n;
                        cin>>n;
                        factorial(n);
                        cout<<endl;
                        return 0;
                  }
                  

                  【讨论】:

                    【解决方案11】:

                    由于每个人都投票给 Srivatsan,我只是对这个问题有疑问。您需要存储所有数字吗?如果是,那么 Srivatsan 的解决方案很好。如果不是,为什么在计算阶乘时不只显示数字?我没有正确格式化输出,但这可以达到目的。

                    int factorial(int num)
                    {
                       if (num <= 0)
                          return 1;
                       else
                       {
                          std::cout << num << std::endl;
                          return num * factorial(num - 1);
                       }
                    }
                    

                    更新 对于所有反对者,尽管这个 5 年前的帖子,以及factorial(3); 的输出

                    3
                    2
                    1
                    6 // this is the result of the factorial and the digits above are the ones all the digits in the calculation.
                    

                    我以为这是问的问题。

                    【讨论】:

                    • 你为什么不直接编译看看结果如何..!
                    • 尝试使用该方法找到 100 的阶乘。
                    猜你喜欢
                    • 1970-01-01
                    • 2023-03-22
                    • 2010-11-25
                    • 2020-02-10
                    • 1970-01-01
                    • 2014-09-23
                    • 2016-11-29
                    相关资源
                    最近更新 更多