Невозможно рассчитать факториалы больше 20!! Как это сделать?

Я использую формат unsigned long long integer для вычисления больших факториалов. Однако мой код в какой-то момент не работает, можете ли вы взглянуть на него? На самом деле это часть более крупного кода для расширения экспоненциальной функции Тейлора, но эта часть не имеет значения в этой точке. Буду признателен за любые предложения.

Спасибо

#include <stdio.h>
#include <math.h>
//We need to write a factorial function beforehand, since we
//have factorial in the denominators.
//Remembering that factorials are defined for integers; it is
//possible to define factorials of non-integer numbers using
//Gamma Function but we will omit that.
//We first declare the factorial function as follows:
unsigned long long factorial (int);
//Long long integer format only allows numbers in the order of 10^18 so 
//we shall use the sign bit in order to increase our range.
//Now we define it,
unsigned long long
factorial(int n)
{
//Here s is the free parameter which is increased by one in each step and
//pro is the initial product and by setting pro to be 0 we also cover the
//case of zero factorial.
    int s = 1;
    unsigned long long pro = 1;
    if (n < 0)
        printf("Factorial is not defined for a negative number \n");
    else {
    while (n >= s) { 
    printf("%d \n", s);
    pro *= s;
    s++;
    printf("%llu \n", pro);
    }
    return pro;
    }
}

int main ()
{
    int x[12] = { 1, 5, 10, 15, 20, 100, -1, -5, -10, -20, -50, -100};
//Here an array named "calc" is defined to store 
//the values of x.
unsigned long long  k = factorial(25);
printf("%llu \n", k);

//int k;
////The upper index controls the accuracy of the Taylor Series, so
////it is suitable to make it an adjustable parameter. 
//int p = 500;
//for ( k = 0; k < p; k++);

}

Ответ 1

Предел длинной длинной без знака - 18446744073709551615 или около 1,8e + 19. 20! составляет около 2,4e + 18, поэтому в радиусе действия, однако, 21! составляет около 5.1e + 19, превышая максимальный размер без знака long long.

Вы можете счесть это полезным: Существуют ли типы, большие, чем long long int на С++?

Ответ 2

Вы переполняете свой целочисленный тип. Вероятно, unsigned long long для вас 64 бит.

  • 20! 0x21c3_677c_82b4_0000, который подходит.
  • 21! 0x2_c507_7d36_b8c4_0000, который не подходит.

Вы можете посмотреть в библиотеки, такие как GMP, которые допускают сколь угодно большие целые числа.


Расширить комментарий GMP. Вот некоторый код, который будет вычислять факториал с использованием GMP:

void factorial(unsigned long long n, mpz_t result) {
    mpz_set_ui(result, 1);

    while (n > 1) {
        mpz_mul_ui(result, result, n);
        n = n-1;
    }
}

int main() {
    mpz_t fact;
    mpz_init(fact);

    factorial(100, fact);

    char *as_str = mpz_get_str(NULL, 16, fact);
    printf("%s\n", as_str);

    mpz_clear(fact);
    free(as_str);
}

Это вычислит factorial(100) и приведет к:

0x1b30964ec395dc24069528d54bbda40d16e966ef9a70eb21b5b2943a321cdf10391745570cca9420c6ecb3b72ed2ee8b02ea2735c61a000000000000000000000000

И просто для удовольствия, здесь версия С++. Конструкторы, деструкторы и перегрузка операторов, как правило, делают С++ версию этих вещей немного чище. Результат будет таким же, как и раньше.

#include <gmpxx.h>
#include <iostream>

int main() {
    mpz_class fact = 1;

    for (int i=2; i<=100; ++i)
        fact *= i;

    std::cout << "0x" << fact.get_str(16) << "\n";
}

Ответ 3

Диапазон unsigned long long обычно 0 до 2 ^ 64 - 1 (18,446,744,073,709,551,615). 21! выходит из этого диапазона.

Ответ 4

Действительно:

2^64 = 18446744073709551616
21!  = 51090942171709440000
20!  =  2432902008176640000

Кстати, чтобы вычислить результат серии (например, Тейлор), вы не должны вычислять каждый термин отдельно; это, безусловно, принесет вам такие проблемы, как этот. Вместо этого попробуйте рассчитать каждый термин, повторно используя предыдущий.

Например, для ряда Тейлора для cos требуется суммирование слагаемых типа:

(-1)^i * (x^(2*i)) / (2i)!

Легко видеть, что каждый член можно легко вычислить из предыдущего:

newterm = - oldterm * x^2 / ((2i+1)*(2i+2))

Итак, я считаю, что вам не нужно вычислять большие факториалы, за то, что вы пытаетесь сделать. С другой стороны, если вам нужно, вам придется использовать библиотеку для больших чисел, например gmp.

Ответ 5

factorial (25) должен дать результат 18 446 744 073 709 551 615, который больше, чем диапазон беззнаковых длинномерных Диапазоны типов данных

Ответ 6

Длинный длинный только такой большой и, следовательно, может представлять только числа, столь большие. Если вам нужно точное представление больших целых чисел, вам нужно будет использовать что-то еще (часть 3-й партийной библиотеки или некоторый тип данных, которые вы сами сделаете); если вам это не нужно, то вы можете использовать double вместо этого.

Ответ 7

Простой алгоритм, который я написал. но это на Java. Вы можете вычислить факториал 1000 за 15 минут.

Этот алгоритм работает с базовой формулой, которую мы узнали в начальной школе.

/* FOR BEST RESULT DON'T CHANGE THE CODE UNTIL YOU KNOW WHAT YOU'RE DOING */
    public String factorial(int number){
        if(number == 0) return "1";
        String result = "1";
        for(int i = 0; i < number; i++){
            result = *longNumberMultiplyingAlgorithm*(result, "" + (i + 1));
        }
        return result;  
    }

    public String longNumberMultiplyingAlgorithm(String number1, String number2){
            int maxLength = Math.max(number1.length(), number2.length());
            int a = 0;
            String[] numbers = new String[maxLength];

            if(number2.length() > number1.length()){
                String t = number1;
                number1 = number2;
                number2 = t;
            }       

            for(int i = 0; i < number1.length(); i++){
                numbers[i] = "";
                a = 0;
                for(int j = 0; j < number2.length(); j++){
                    int result = Integer.parseInt(String.valueOf(number1.charAt(number1.length() - i - 1))) * Integer.parseInt(String.valueOf(number2.charAt(number2.length() - j - 1)));
                    if(result + a < 10){
                        numbers[i] = (result + a) + "" + numbers[i];
                        a = 0;
                    }else{
                        result += a;
                        a = (int)((result + 0.0) / 10);
                        result -= a * 10;
                        numbers[i] = result + "" + numbers[i];
                    }
                }
                if(a != 0){
                    numbers[i] = a + "" + numbers[i];
                }
                for(int k = 0; k < i; k++){
                    numbers[i] += "0";
                }
            }
            return longNumberAdditionAlgorithm(numbers);
        }

    private String longNumberAdditionAlgorithm(String[] numbers) {
            String final_number = "0";
            for(int l = 0; l < numbers.length; l++){
                int maxLength = Math.max(final_number.length(), numbers[l].length());
                String number = "";
                int[] n = new int[maxLength];
                int a = 0;
                for(int i = 0; i < n.length; i++){
                    int result = 0;
                    if(i >= final_number.length()){
                        result = Integer.parseInt(String.valueOf(numbers[l].charAt(numbers[l].length() - i - 1)));
                    }else
                    if(i >= numbers[l].length()){
                        result = Integer.parseInt(String.valueOf(final_number.charAt(final_number.length() - i - 1)));
                    }else{
                        result = Integer.parseInt(String.valueOf(final_number.charAt(final_number.length() - i - 1))) + Integer.parseInt(String.valueOf(numbers[l].charAt(numbers[l].length() - i - 1)));
                    }
                    if(result + a < 10){
                        number = (result + a) + "" + number;
                        a = 0;
                    }else{
                        result -= 10;
                        number = (result + a) + "" + number;
                        a = 1;
                    }
                }
                if(a == 1){
                    number = a + "" + number;
                }
                final_number = number;
            }
            return final_number;
        }