Рекурсия с максимальным целым числом

Я пишу программу для вычисления факториала числа. Я использую рекурсию для решения этой проблемы. Проблема, с которой я сталкиваюсь, заключается в том, что, как только я достигну числа 13, он выдаст мусорные числа из-за ограничения INT. Что я хочу сделать, так это реализовать способ поймать ошибку, когда она произойдет (без жесткой привязки, которая должна остановиться при x = 13, а скорее по выходным данным). Это моя попытка:

#include <stdio.h>

int factorial( int n)
{
    printf("Processing factorial( %d )\n", n);

    if (n <= 1)
    {
        printf("Reached base case, returning...\n");
        return 1;
    }
    else
    {
        int counter = n * factorial(n-1);       //Recursion to multiply the lesser numbers

        printf("Receiving results of factorial( %d ) =  %d * %d! = %d\n", n, n, (n-1), counter);

        if( counter/n != factorial(n-2) ) //my attempt at catching the wrong output
        {
            printf("This factorial is too high for this program ");
            return factorial(n-1);
        }
        return counter;


        printf("Doing recursion by calling factorial (%d -1)\n", n);
    }


}


int main()
{
    factorial(15);
}

Проблема в том, что программа теперь никогда не завершается. Он продолжает зацикливаться и выдавать мне случайные результаты.

Поскольку я не могу ответить на свой вопрос, я отредактирую свое решение:

int jFactorial(int n)
{
    if (n <= 1)
    {
        return 1;
    }
    else
    {
        int counter = n *jFactorial(n-1);
        return counter;
    }
}

void check( int n)
{
    int x = 1;
    for(x = 1; x < n+1; x++)
    {
        int result = jFactorial(x);
        int prev = jFactorial(x-1);
        if (((result/x) != prev) || result == 0 )
        {
            printf("The number %d makes function overflow \n", x);
        }
        else
        {
            printf("Result for %d is %d \n", x, result);
        }
    }
}

person user3708229    schedule 04.06.2014    source источник
comment
Почему counter / n когда-либо равнялось factorial(n-2)? Не обращайте внимания на тот факт, что добавление дополнительного рекурсивного случая немного сомнительно, ваша математика просто неверна.   -  person hobbs    schedule 04.06.2014
comment
@hobbs Я пытаюсь проверить, не равен ли factorial(N)/N факториалу предыдущего числа, потому что это скажет мне, правильный вывод или нет.   -  person user3708229    schedule 04.06.2014
comment
да, я понимаю это.   -  person hobbs    schedule 04.06.2014
comment
предыдущий номер не factorial(n-2), это factorial(n-1).   -  person hobbs    schedule 04.06.2014
comment
Я полагаю, вы знаете, что рекурсия - плохой способ вычисления факториала по сравнению с простым вычислением произведения 2..n.   -  person Andrew Morton    schedule 04.06.2014
comment
Да, это правильно, но он все еще бесконечно зацикливается   -  person user3708229    schedule 04.06.2014
comment
@user3708229 user3708229 Я реализовал полное решение C в своем ответе.   -  person Spundun    schedule 04.06.2014
comment
Почему эти числа подписаны? Что факториал(-9) должен означать?   -  person wildplasser    schedule 05.06.2014


Ответы (2)


Лучший способ сделать это:

if (n <= 1) {
   return 1;
} else {
    int prev_fact = factorial(n - 1);
    if (INT_MAX / prev_fact < n) { /* prev_fact * n will overflow */
        printf("Result too big");
        return prev_fact;
    } else {
        return prev_fact * n;
    }
}

Использует более точную проверку (надеюсь) переполнения умножения и не добавляет больше вызовов factorial.

person hobbs    schedule 04.06.2014
comment
Как я могу определить int_max? Я пробовал #define MAXIMUM INT_MAX, но это не сработает. - person user3708229; 04.06.2014
comment
Я не думаю, что это сработает. Возьмем, к примеру, 3. MAX_INT равно 4,1b, деленное на предыдущий факториал (2), который дает результат 2. Тогда 4,1b/2 больше, чем n, равное 3, и результат будет слишком большим. Посмотрите на мое редактирование, я смог решить его двумя способами, теперь я пытаюсь объединить эти два метода в 1. - person user3708229; 04.06.2014
comment
@hobbs Думаю, ты хочешь if (MAX_INT / prev_fact < n) {. < против >. - person chux - Reinstate Monica; 05.06.2014
comment
@user3708229 #include <limits.h> - person hobbs; 05.06.2014
comment
@user3708229 user3708229 и посмотрите изменение, которое я внес, предложенное chux, < вместо >. - person hobbs; 05.06.2014
comment
+1 за правильный подход: MAX_INT / prev_fact < n или #include <limits.h> INT_MAX / prev_fact < n - person chux - Reinstate Monica; 05.06.2014

Обновлять

Присмотревшись повнимательнее, оказалось, что я пропустил тот факт, что gmp также реализован для C. Вот решение в C

Я смог запустить его на своем macbook pro, используя homebrew для установки gmp (brew isntall gmp)

#include <gmp.h>

#include <stdio.h>

void factorial(mpz_t ret, unsigned n) {
  if (n <= 1) {
    mpz_set_ui(ret, 1);//Set the value to 1
  } else {
    //multiply (n-1)! with n
    mpz_t ret_intermediate;
    mpz_init (ret_intermediate);//Initializes to zero
    factorial(ret_intermediate, n-1);
    mpz_mul_ui(ret, ret_intermediate, n);
  }

  return;
}

int main(){
  mpz_t result;
  mpz_init (result);
  factorial(result, 100);
  char * str_result = mpz_get_str(NULL, 10, result);
  printf("%s\n", str_result);
  return 0;
}

Оригинальный ответ

После быстрого поиска в Google я нашел следующее решение. Обратите внимание, что это решение C++. Я кратко описываю, как вы можете сделать то же самое в ANSI C внизу.

Библиотека больших чисел в С++

https://gmplib.org/ Эта библиотека C++ может работать с произвольно большими числами.

Оформить заказ https://gmplib.org/manual/C_002b_002b-Interface-General.html

Весь код может выглядеть примерно так....

#include <gmpxx.h>

#include <iostream>

mpz_class factorial(unsigned n) {
  if (n <= 1) return mpz_class(1);

  return mpz_class(n) * factorial(n-1);
}

int main(){
  mpz_class result = factorial(100);
  std::string str_result = result.get_str();
  std::cout << str_result << std::endl;
  return 0;
}

Версия ANSI C

Вы можете реализовать то же самое, используя ansi C, со структурой для хранения расширяющегося списка чисел (используя связанный список или любые другие расширяемые контейнеры arraylist), и вам нужно всего лишь реализовать три метода... инициализировать, умножить и преобразовать нанизывать.

person Spundun    schedule 04.06.2014
comment
Но это не сработает, если результат переполнится. И это не похоже на ANSI C - person user3708229; 04.06.2014
comment
да, вы правы, это решение C++. С чем-то вроде факториала вам придется реализовать класс больших чисел, например mpz_class, вручную. Что на самом деле выполнимо. - person Spundun; 04.06.2014
comment
@user3708229 user3708229 Я добавил краткое руководство по его адаптации к ANSI C. Дайте мне знать, если все еще возникнет путаница. - person Spundun; 04.06.2014
comment
проверьте мое редактирование и скажите, считаете ли вы, что это лучшее решение, чем ваше - person user3708229; 04.06.2014
comment
@user3708229 user3708229 Этот ответ не будет переполнен до тех пор, пока программе полностью не закончится память. - person Spundun; 04.06.2014