计算任意数值的阶乘
作者: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