Как вычислить факториал в C# с помощью вызова библиотеки?

Мне нужно вычислить факториал чисел до 100! чтобы определить, является ли серия данных в стиле монетного флипа случайной, согласно эта запись Википедии о Байесовской вероятности. Как вы можете видеть, необходимая формула включает в себя 3 факторных вычисления (но, что интересно, два из этих факторных вычислений вычисляются по пути к третьему).

Я видел этот вопрос здесь, но я думаю, что integer собирается получить сдуло довольно быстро. Я также мог бы сделать функцию, которая более умна в факториальном расчете (т. е., если у меня есть 11!/(7!3!), согласно примеру wiki, я мог бы перейти к (11*10*9*8)/3!), но это пахнет преждевременной оптимизацией для меня, в том смысле, что я хочу, чтобы она работала, но меня не волнует скорость (пока).

Итак, какую хорошую библиотеку C# я могу вызвать для вычисления факториала, чтобы получить эту вероятность? Меня не интересуют все те чудеса, в которые можно войти. факториальный расчет, я просто хочу получить результат таким образом, чтобы я мог им манипулировать. В пространстве имен Math не существует факторной функции, поэтому возникает вопрос.

5 ответов


вы можете попробовать Math.NET - я не использовал эту библиотеку, но они перечисляют Факториал и логарифмический Факториал.


есть предыдущий вопрос по аналогичной теме. Кто-то там связал Быстрые Факториальные Функции веб-сайт, который включает в себя некоторые объяснения эффективных алгоритмов и даже исходный код C#.


вы хотите рассчитать факториалы или биномиальные коэффициенты?

похоже, вы хотите рассчитать биномиальные коэффициенты-тем более, что вы упомянули 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.


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();
        }
    }
}

следующее может вычислить факториал 5000 в 1 секунду.

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;
    }
}