eaglet

本博专注于基于微软技术的搜索相关技术
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

计算任意数值的阶乘

Posted on 2009-08-20 08:46  eaglet  阅读(5522)  评论(36编辑  收藏  举报

计算任意数值的阶乘

作者:eaglet

     谈到计算阶乘,大家可能会觉得比较简单,不就是一个循环从1一直乘到n吗?是的,确实是这样,但由于计算机的计算精度问题,利用计算机提供的现成的整数类型,我们最多可以计算到22! = 17196083355034583040 再大了,64位整形就无法存储。那么如果我们想计算100! 怎么办呢?eaglet 以前在博问中回答过类似问题,今天有空把它整理出来,供大家参考。

     要实现任意数值的阶乘,最简单的办法就是自己实现一个整数类型,这个整数类型可以像int, long 这些类型一样实现加和乘的操作,但没有大小限制。下面就给出eaglet 做的十进制无符号整形类。这个类只实现了加法和乘法操作,没有实现减法和除法,但用作阶乘已经足够了,减法和除法大家有兴趣可以自己补充。

加法是基础

算法很简单,就是从个位开始按位相加,如果有进位,就进位。

乘法的方法是将乘数从各位开始逐位和被乘数相乘,然后再将相乘后得到的数累加,这个方法和手工计算乘法的方法是一样的。

个人感觉这两种方法的效率应该不是最高的,如果哪位TX有更高效的算法,也不妨一起讨论一下。

下面给出代码

    /// <summary>
    /// 十机制无符号整数类
    /// </summary>
    public class DecimalNumber
    {
        List<byte> _Data;
 
        public List<byte> Data
        {
            get
            {
                return _Data;
            }
        }
 
        public int Length
        {
            get
            {
                return _Data.Count;
            }
        }
 
        public DecimalNumber()
        {
            _Data = new List<byte>();
        }
 
        public DecimalNumber(byte[] data)
        {
            foreach (byte b in data)
            {
                System.Diagnostics.Debug.Assert(b >= 0 && b <= 9);
            }
 
            _Data = new List<byte>(data);
        }
 
        public DecimalNumber(List<byte> data)
        {
            foreach (byte b in data)
            {
                System.Diagnostics.Debug.Assert(b >= 0 && b <= 9);
            }
 
            _Data = data;
        }
 
        /// <summary>
        /// 1位10机制数和10进制序列相乘
        ///
        /// </summary>
        /// <param name="s">10进制序列</param>
        /// <param name="d">1位10机制数</param>
        /// <param name="power">10的幂数</param>
        /// <returns></returns>
        private static List<byte> Multiply(List<byte> s, byte d, int power)
        {
            System.Diagnostics.Debug.Assert(power >= 0);
            System.Diagnostics.Debug.Assert(d >= 0 && d <= 9);
 
            List<byte> result = new List<byte>();
 
            for (int i = 0; i < power; i++)
            {
                result.Add(0);
            }
 
            byte carry = 0; //进位
 
            foreach (byte si in s)
            {
                System.Diagnostics.Debug.Assert(si >= 0 && si <= 9);
 
                byte r = (byte)(si * d + carry);
                byte m = (byte)(r % 10);
                carry = (byte)(r / 10);
                result.Add(m);
            }
 
            if (carry > 0)
            {
                result.Add(carry);
            }
 
 
            return result;
        }
 
        /// <summary>
        /// 两个10进制序列相加
        /// </summary>
        /// <param name="s1">序列1</param>
        /// <param name="s2">序列2</param>
        /// <returns>相加后的序列</returns>
        private static List<byte> Plus(List<byte> s1, List<byte> s2)
        {
            List<byte> result = new List<byte>();
 
            int c1 = s1.Count;
            int c2 = s2.Count;
 
            if (c1 > c2)
            {
                for (int i = 0; i < c1 - c2; i++)
                {
                    s2.Add(0);
                }
            }
            else if (c1 < c2)
            {
                for (int i = 0; i < c2 - c1; i++)
                {
                    s1.Add(0);
                }
            }
 
            byte carry = 0; //进位
 
            for (int i = 0; i < s1.Count; i++)
            {
                System.Diagnostics.Debug.Assert(s1[i] >= 0 && s1[i] <= 9);
                System.Diagnostics.Debug.Assert(s2[i] >= 0 && s2[i] <= 9);
 
                byte r = (byte)(s1[i] + s2[i] + carry);
                byte m = (byte)(r % 10);
                carry = (byte)(r / 10);
                result.Add(m);
            }
 
            if (carry > 0)
            {
                result.Add(carry);
            }
 
            return result;
        }
 
        public static implicit operator DecimalNumber(string value)
        {
            List<byte> data = new List<byte>();
 
            for (int i = value.Length - 1; i >= 0; i--)
            {
                data.Add(byte.Parse(value[i].ToString()));
            }
 
            return new DecimalNumber(data);
        }
 
        public static implicit operator DecimalNumber(int value)
        {
            System.Diagnostics.Debug.Assert(value >= 0);
            return value.ToString();
        }
 
        public static DecimalNumber operator ++(DecimalNumber d)
        {
            return d + new DecimalNumber(new byte[] { 1 });
        }
 
        public static DecimalNumber operator +(DecimalNumber d1, int d2)
        {
            System.Diagnostics.Debug.Assert(d2 >= 0);
 
            return d1 + d2.ToString();
        }
 
        public static DecimalNumber operator +(DecimalNumber d1, DecimalNumber d2)
        {
            return new DecimalNumber(Plus(d1.Data, d2.Data));
        }
 
        public static DecimalNumber operator *(DecimalNumber d1, DecimalNumber d2)
        {
            List<List<byte>> multiplicationSerial = new List<List<byte>>();
 
            for (int i = 0; i < d1.Data.Count; i++)
            {
                multiplicationSerial.Add(Multiply(d2.Data, d1.Data[i], i));
            }
 
            List<byte> result = new List<byte>();
 
            foreach (List<byte> s in multiplicationSerial)
            {
                result = Plus(s, result);
            }
 
            return new DecimalNumber(result);
        }
 
        public override string ToString()
        {
            StringBuilder str = new StringBuilder();
 
            for (int i = _Data.Count - 1; i >= 0; i--)
            {
                str.Append(_Data[i].ToString());
            }
 
            return str.ToString();
        }
     }

 

有了这个类,我们计算阶乘就简单了

下面是计算任意数阶乘的函数

        static public DecimalNumber Factorial(int n)
        {
            if (n < 0)
            {
                throw new System.ArgumentException("n < 0!");
            }
 
            DecimalNumber result = 1;
 
            for (int i = 1; i <= n; i++)
            {
                result *= i;
            }
 
            return result;
        }

 

用这个函数,我们计算0 到 50 的阶乘

 

            for (int i = 0; i <= 50; i++)
            {
                Console.WriteLine(string.Format("{0}! = {1}", i,  Factorial(i)));
            }

看看结果:

0! = 1
1! = 1
2! = 2
3! = 6
4! = 24
5! = 120
6! = 720
7! = 5040
8! = 40320
9! = 362880
10! = 3628800
11! = 39916800
12! = 479001600
13! = 6227020800
14! = 87178291200
15! = 1307674368000
16! = 20922789888000
17! = 355687428096000
18! = 6402373705728000
19! = 121645100408832000
20! = 2432902008176640000
21! = 51090942171709440000
22! = 1124000727777607680000
23! = 25852016738884976640000
24! = 620448401733239439360000
25! = 15511210043330985984000000
26! = 403291461126605635584000000
27! = 10888869450418352160768000000
28! = 304888344611713860501504000000
29! = 8841761993739701954543616000000
30! = 265252859812191058636308480000000
31! = 8222838654177922817725562880000000
32! = 263130836933693530167218012160000000
33! = 8683317618811886495518194401280000000
34! = 295232799039604140847618609643520000000
35! = 10333147966386144929666651337523200000000
36! = 371993326789901217467999448150835200000000
37! = 13763753091226345046315979581580902400000000
38! = 523022617466601111760007224100074291200000000
39! = 20397882081197443358640281739902897356800000000
40! = 815915283247897734345611269596115894272000000000
41! = 33452526613163807108170062053440751665152000000000
42! = 1405006117752879898543142606244511569936384000000000
43! = 60415263063373835637355132068513997507264512000000000
44! = 2658271574788448768043625811014615890319638528000000000
45! = 119622220865480194561963161495657715064383733760000000000
46! = 5502622159812088949850305428800254892961651752960000000000
47! = 258623241511168180642964355153611979969197632389120000000000
48! = 12413915592536072670862289047373375038521486354677760000000000
49! = 608281864034267560872252163321295376887552831379210240000000000
50! = 30414093201713378043612608166064768844377641568960512000000000000