【问题标题】:How can I calculate a factorial in C# using a library call?如何使用库调用在 C# 中计算阶乘?
【发布时间】:2009-09-30 02:34:30
【问题描述】:

我需要计算 100 以内数字的阶乘!为了确定一系列硬币翻转式数据是否是随机的,根据this Wikipedia entry on Bayesian probability. 正如您所见,必要的公式涉及 3 个阶乘计算(但有趣的是,其中两个阶乘计算是在第三)。

我看到了this question here,但我认为整数很快就会被淘汰。我还可以创建一个对阶乘计算更智能的函数(即,如果我有 11!/(7!3!),根据 wiki 示例,我可以转到 (11*10*9*8)/ 3!),但这对我来说有点过早优化的味道,从某种意义上说,我希望它工作,但我不关心速度(还)。

那么,我可以调用什么好的 C# 库来计算阶乘以获得该概率?我对阶乘计算中的所有令人敬畏的东西不感兴趣,我只想以一种我可以操纵它的方式得到结果。 Math 命名空间中似乎没有阶乘函数,因此是这个问题。

【问题讨论】:

    标签: c# factorial


    【解决方案1】:

    您可以尝试Math.NET - 我没有使用过那个库,但它们确实列出了阶乘和对数阶乘。

    【讨论】:

    【解决方案2】:

    有一个关于类似主题的previous question。那里有人链接了Fast Factorial Functions 网站,其中包括对高效算法的一些解释,甚至包括C#源代码。

    【讨论】:

    • 现在检查一下 - 伙计,我真的希望有一些最佳近似可能不适用于每个人的实现作为基础库的一部分。
    • 该代码的问题是下载似乎都是在 java 中,我只是对从 java 转换为 C# 的工作不感兴趣。作者大概有C#代码要下载,但是Math.Net的方案把结果变成了double,这正是我需要的。
    【解决方案3】:

    您要计算阶乘系数还是二项式系数?

    听起来您想计算二项式系数 - 尤其是当您提到 11!/(7!3!) 时。

    可能有一个库可以为您执行此操作,但作为(可能)访问堆栈溢出的程序员,没有理由不自己编写一个。这不是太复杂。

    为避免内存溢出,在移除所有公因数之前不要评估结果。

    这个算法还有待改进,但是你这里有一个好的算法的基础。为了获得最佳结果,需要将分母值拆分为其主要因素。就目前而言,这将很快运行 n = 50。

    float CalculateBinomial(int n, int k)
    {
        var numerator = new List<int>();
        var denominator = new List<int>();
        var denominatorOld = new List<int>();
    
        // again ignore the k! common terms
        for (int i = k + 1; i <= n; i++)
            numerator.Add(i);
    
        for (int i = 1; i <= (n - k); i++)
        {
            denominator.AddRange(SplitIntoPrimeFactors(i));
        }
    
        // remove all common factors
        int remainder;                
        for (int i = 0; i < numerator.Count(); i++)
        {
            for (int j = 0; j < denominator.Count() 
                && numerator[i] >= denominator[j]; j++)
            {
                if (denominator[j] > 1)
                {
                    int result = Math.DivRem(numerator[i], denominator[j], out remainder);
                    if (remainder == 0)
                    {
                        numerator[i] = result;
                        denominator[j] = 1;
                    }
                }
            }
        }
    
        float denominatorResult = 1;
        float numeratorResult = 1;
    
        denominator.RemoveAll(x => x == 1);
        numerator.RemoveAll(x => x == 1);
    
        denominator.ForEach(d => denominatorResult = denominatorResult * d);
        numerator.ForEach(num => numeratorResult = numeratorResult * num);
    
        return numeratorResult / denominatorResult;
    }
    
    static List<int> Primes = new List<int>() { 2, 3, 5, 7, 11, 13, 17, 19, 
        23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97 };
    
    List<int> SplitIntoPrimeFactors(int x)
    {
        var results = new List<int>();
        int remainder = 0;
    
        int i = 0;
        while (!Primes.Contains(x) && x != 1)
        {
            int result = Math.DivRem(x, Primes[i], out remainder);
            if (remainder == 0)
            {
                results.Add(Primes[i]);
                x = result;
                i = 0;
            }
            else
            {
                i++;
            }
        }
        results.Add(x);
        return results;
    }
    

    我可以估计 n = 110, k = 50(返回 6x10^31)但不能运行 n = 120, k = 50。

    【讨论】:

    • 当 n ~= 50 和 k ~= 2 时会发生什么?溢出!我需要一些方法来处理不平凡的案件。
    • 在这种情况下,正如我指出的那样,您计算 n = 50 和 k = 48。
    • 好的。什么是48!?这适合整数吗?因为这就是你的分母在这种情况下所做的事情。
    • 不管算法如何,例如 nCr 对于 n = 100 和 r = 50 的结果将溢出一个 64 位数字。您需要回到原来的问题,因为您无法存储该结果。
    • 仅当我不使用使用诸如斯特林近似之类的算法时,才将结果放入双精度数。算法的下一步是基本积分,所以“足够接近”就可以了,双精度损失也可以。
    【解决方案4】:

    下面可以在1秒内计算出5000的阶乘。

    public class Number
    {
        #region Fields
        private static long _valueDivision = 1000000000;
        private static int _valueDivisionDigitCount = 9;
        private static string _formatZeros = "000000000";
        List<long> _value;
        #endregion
    
        #region Properties
        public int ValueCount { get { return _value.Count; } }
        public long ValueAsLong
        {
            get
            {
                return long.Parse(ToString());
            }
            set { SetValue(value.ToString()); }
        }
        #endregion
    
        #region Constructors
        public Number()
        {
            _value = new List<long>();
        }
        public Number(long value)
            : this()
        {
            SetValue(value.ToString());
        }
        public Number(string value)
            : this()
        {
            SetValue(value);
        }
        private Number(List<long> list)
        {
            _value = list;
        }
        #endregion
    
        #region Public Methods
        public void SetValue(string value)
        {
            _value.Clear();
            bool finished = false;
            while (!finished)
            {
                if (value.Length > _valueDivisionDigitCount)
                {
                    _value.Add(long.Parse(value.Substring(value.Length - _valueDivisionDigitCount)));
                    value = value.Remove(value.Length - _valueDivisionDigitCount, _valueDivisionDigitCount);
                }
                else
                {
                    _value.Add(long.Parse(value));
                    finished = true;
                }
            }
        }
        #endregion
    
        #region Static Methods
        public static Number operator +(Number c1, Number c2)
        {
            return Add(c1, c2);
        }
        public static Number operator *(Number c1, Number c2)
        {
            return Mul(c1, c2);
        }
        private static Number Add(Number value1, Number value2)
        {
            Number result = new Number();
            int count = Math.Max(value1._value.Count, value2._value.Count);
            long reminder = 0;
            long firstValue, secondValue;
            for (int i = 0; i < count; i++)
            {
                firstValue = 0;
                secondValue = 0;
                if (value1._value.Count > i)
                {
                    firstValue = value1._value[i];
                }
                if (value2._value.Count > i)
                {
                    secondValue = value2._value[i];
                }
                reminder += firstValue + secondValue;
                result._value.Add(reminder % _valueDivision);
                reminder /= _valueDivision;
            }
            while (reminder > 0)
            {
                result._value.Add(reminder % _valueDivision);
                reminder /= _valueDivision;
            }
            return result;
        }
        private static Number Mul(Number value1, Number value2)
        {
            List<List<long>> values = new List<List<long>>();
            for (int i = 0; i < value2._value.Count; i++)
            {
                values.Add(new List<long>());
                long lastremain = 0;
                for (int j = 0; j < value1._value.Count; j++)
                {
                    values[i].Add(((value1._value[j] * value2._value[i] + lastremain) % _valueDivision));
                    lastremain = ((value1._value[j] * value2._value[i] + lastremain) / _valueDivision);
                    //result.Add(();
                }
                while (lastremain > 0)
                {
                    values[i].Add((lastremain % _valueDivision));
                    lastremain /= _valueDivision;
                }
            }
            List<long> result = new List<long>();
            for (int i = 0; i < values.Count; i++)
            {
                for (int j = 0; j < i; j++)
                {
                    values[i].Insert(0, 0);
                }
            }
            int count = values.Select(list => list.Count).Max();
            int index = 0;
            long lastRemain = 0;
            while (count > 0)
            {
                for (int i = 0; i < values.Count; i++)
                {
                    if (values[i].Count > index)
                        lastRemain += values[i][index];
                }
                result.Add((lastRemain % _valueDivision));
                lastRemain /= _valueDivision;
                count -= 1;
                index += 1;
            }
            while (lastRemain > 0)
            {
                result.Add((lastRemain % _valueDivision));
                lastRemain /= _valueDivision;
            }
            return new Number(result);
        }
        #endregion
    
        #region Overriden Methods Of Object
        public override string ToString()
        {
            string result = string.Empty;
            for (int i = 0; i < _value.Count; i++)
            {
                result = _value[i].ToString(_formatZeros) + result;
            }
            return result.TrimStart('0');
        }
        #endregion
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            Number number1 = new Number(5000);
            DateTime dateTime = DateTime.Now;
            string s = Factorial(number1).ToString();
            TimeSpan timeSpan = DateTime.Now - dateTime;
            long sum = s.Select(c => (long) (c - '0')).Sum();
        }
        static Number Factorial(Number value)
        {
            if( value.ValueCount==1 && value.ValueAsLong==2)
            {
                return value;
            }
            return Factorial(new Number(value.ValueAsLong - 1)) * value;
        }
    }
    

    【讨论】:

    • 它不会在我的电脑上运行 1 秒 ;)
    【解决方案5】:
    using System;
    //calculating factorial with recursion
    namespace ConsoleApplication2
    {
        class Program
        {
            long fun(long a)
            {
                if (a <= 1)
                {
                    return 1;}
                else
                {
                    long c = a * fun(a - 1);
                    return c;
                }}
    
            static void Main(string[] args)
            {
    
                Console.WriteLine("enter the number");
                long num = Convert.ToInt64(Console.ReadLine());
                Console.WriteLine(new Program().fun(num));
                Console.ReadLine();
            }
        }
    }
    

    【讨论】:

    • 请阅读其他答案。这不是一个充分的解决方案,因为 long 没有能力处理接近 100! 的数字的大小。因此,需要使用其他一些方法(例如斯特林近似)。
    • 抱歉,递归方法在任何方面都没有效率。
    【解决方案6】:

    大家好,根据这个解决方案,我有自己的解决方案,我计算数组一维元素的阶乘。代码是`int[] array = new int[5] { 4,3,4,3,8 };

            int fac = 1;
    
            int[] facs = new int[array.Length+1];
    
            for (int i = 0; i < array.Length; i++)
            {
                for (int j = array[i]; j > 0; j--)
                {
                    fac *= j;
                }
                facs[i] = fac;
                textBox1.Text += facs[i].ToString() + " ";
                fac = 1;
            }`
    

    将上面的代码复制粘贴到按钮中,它解决了数组1D元素的阶乘问题。最好的问候。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多