Подтвердить что ты не робот

Сравните два факториала без вычисления

Есть ли способ сравнить, какое факториальное число больше среди двух чисел без вычисления?
Сценарий: я создаю консольное приложение С#, которое принимает два факториала, например

123!!!!!!
456!!!  

все, что я хочу сделать, это сравнить, какое факториальное значение больше, чем другое, кусок кода, который я сделал,

try
{
    string st = Console.ReadLine();
    Int64 factCount = 0;
    while (st.Contains('!'))
    {
       factCount = st.Where(w => w == '!').Count();
       st = st.Replace('!', ' ');

    };
    decimal result = 1 ;
    for (Int64 j = 0; j < factCount; j++)
    {
        UInt64 num = Convert.ToUInt64(st.Trim());
        for (UInt64 x = num; x > 0; x--)
        {
            result = result * x;
        }
    }
    if (factCount == 0)
    {
        result = Convert.ToUInt64(st.Trim());
    }


    string st2 = Console.ReadLine();
    Int64 factCount2 = 0;
    while (st2.Contains('!'))
    {
        factCount2 = st2.Where(w => w == '!').Count();
        st2 = st2.Replace('!', ' ');
    };
    decimal result2 = 1;
    for (Int64 j = 0; j < factCount2; j++)
    {
        UInt64 num = Convert.ToUInt64(st.Trim());
        for (UInt64 x = num; x > 0; x--)
        {
            result2 = result2 * x;
        }
    }
    if (factCount2 == 0)
    {
        result2 = Convert.ToUInt64(st2.Trim());
    }

    if (result == result2)
    {
        Console.WriteLine("x=y");
    }
    else if (result < result2)
    {
        Console.WriteLine("x<y");
    }
    else if (result > result2)
    {
        Console.WriteLine("x>y");
    }
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
    Console.ReadLine();
}

но ошибка, которую я получаю, - это значение слишком велико или слишком мало для десятичного числа
Я понял ошибку, но есть ли способ сделать это

Просьба указать, есть ли какой-либо другой тип данных, который вмещает значение больше десятичного или есть ли другой способ сравнения этих факториалов

После реализации предложения @Bathsheba я немного изменил свой код

    string st = Console.ReadLine();
    int factCount = 0;
    while (st.Contains('!'))
    {
       factCount = st.Where(w => w == '!').Count();
       st = st.Replace('!', ' ');

    };

    string st2 = Console.ReadLine();
    int factCount2 = 0;
    while (st2.Contains('!'))
    {
        factCount2 = st2.Where(w => w == '!').Count();
        st2 = st2.Replace('!', ' ');
    };

    int resultFactCount = factCount - factCount2;
    decimal result = 1;
    decimal result2 = 1;

    if (resultFactCount > 0)
    {

        for (Int64 j = 0; j < resultFactCount; j++)
        {
            UInt64 num = Convert.ToUInt64(st.Trim());
            for (UInt64 x = num; x > 0; x--)
            {
                result = result * x;
            }
        }
        if (factCount == 0)
        {
            result = Convert.ToUInt64(st.Trim());
        }
        UInt64 num1 = Convert.ToUInt64(st.Trim());
        if (result == num1)
        {
            Console.WriteLine("x=y");
        }
        else if (result < num1)
        {
            Console.WriteLine("x<y");
        }
        else if (result > num1)
        {
            Console.WriteLine("x>y");
        }
    }
    else
    {
        int resultFactCount1 = System.Math.Abs(resultFactCount);
        for (Int64 j = 0; j < resultFactCount1; j++)
        {
            UInt64 num = Convert.ToUInt64(st.Trim());
            for (UInt64 x = num; x > 0; x--)
            {
                result2 = result2 * x;
            }
        }
        if (factCount2 == 0)
        {
            result2 = Convert.ToUInt64(st2.Trim());
        }
        UInt64 num1 = Convert.ToUInt64(st.Trim());

        if (result2 == num1)
        {
            Console.WriteLine("x=y");
        }
        else if (result2 < num1)
        {
            Console.WriteLine("x<y");
        }
        else if (result2 > num1)
        {
            Console.WriteLine("x>y");
        }
    }   

Извините, но еще 123!!! настолько огромна, что я получаю ту же ошибку


Традиционно m!!...! с n ! означает m(m-n)(m-2n)...., но здесь берется как (...((m!)!)!...)!
Замечание от Алека, да, я знаю, это печальная нотация, но вы видите, что обычное определение гораздо более полезно (в комбинатонике, место, где происходят факториалы), чем тот, который нужен OP.
Я бы поставил это в комментарии, но это было бы затмевано другими, и это очень важно.

4b9b3361

Ответ 1

Здесь a!! определяется как (a!)!.

123!!!!!! является абсолютно гигантским. Я думаю, вам понадобится больше частиц, чем во Вселенной, если вы должны записать его в чернилах.

Поэтому вы не можете напрямую сравнивать числа. Я предполагаю, что для этого не существует числового класса.

Что вы можете сделать, это рассмотреть фактор 123!!!!!! / 456!!!. Многие из мультипликаторов будут похожи, поэтому вы можете их отменить. Обратите внимание, что завершение ! отменяется. Это связано с тем, что x > y подразумевает, и подразумевается x! > y! где x и y - целые положительные числа.

В конце концов вы достигнете точки, в которой вы можете оценить это как меньшее или большее, чем 1, что даст вам ответ.

Я могу сказать вам, что 123!!!!!! больше, поскольку 123!!! больше, чем 456.

Ответ 2

В отличие от других ответов вы можете сделать это без каких-либо приближений.

Вот он:

123 !!!!!! > 456 !!! 

означает фактически

123 !!!!! > 456 !!
123 !!!! > 456 ! 

а также

123 !!! > 456  

Итак, вам нужно только доказать это. Это просто, потому что у вас есть хотя бы один операнд, который может вписываться в UInt64

Итак, это должно дать вам что-то вроде этого:

public class Program
{
    static bool LeftIsGreaterThanRightSide(UInt64 leftSide, int leftSidefactCount, UInt64 rightSide)
    {
        try
        {
            checked // for the OverflowException
            {
                UInt64 input2 = leftSide;
                int factCount = leftSidefactCount;
                UInt64 result = 1;

                for (Int64 j = 0; j < factCount; j++)
                {
                    UInt64 num = input2;
                    for (UInt64 x = num; x > 0; x--)
                    {
                        result = result * x;
                    }
                }

                // None of the operand are great or equal than UInt64.MaxValue
                // So let compare the result normaly
                return result > rightSide; 
            }
        }
        catch (OverflowException)
        {
            // leftSide overflowed, rightSide is a representable UInt64 so leftSide > rightSide ; 
            return true; 
        }
    }


    static void Main()
    {
        String input1 = Console.ReadLine();
        String input2 = Console.ReadLine();

        int fact1Count = input1.Count(c => c == '!');
        int fact2Count = input2.Count(c => c == '!');

        UInt64 x = Convert.ToUInt64(input1.Replace("!", String.Empty).Trim());
        UInt64 y = Convert.ToUInt64(input2.Replace("!", String.Empty).Trim());

        x = x == 0 ? 1 : x ; // Handling 0 !
        y = y == 0 ? 1 : y; 

        if (fact1Count > fact2Count)
        {
            fact1Count = fact1Count - fact2Count;
            Console.WriteLine(LeftIsGreaterThanRightSide(x, fact1Count, y) ? "x > y" : "x <= y");
        }
        else
        {
            fact2Count = fact2Count - fact1Count;
            Console.WriteLine(LeftIsGreaterThanRightSide(y, fact2Count, x) ? "y > x" : "y <= x");
        }

        Console.ReadLine();
    }


}

Ответ 3

Для заданных чисел, считая, что 456!!! означает ((456!)!)!, мы имеем

  123!!!!!! == (123!!!)!!!

и

  123!!! >>> 456 // >>> stands for "much, much...much larger", ">>" is not enough 

четный 123! (который 1.2e205) намного больше, чем просто 456

Чтобы оценить фактические значения факториалов, можно использовать приближение Стирлинга

https://en.wikipedia.org/wiki/Stirling%27s_approximation

то есть.

  ln(n!) == n * ln(n) - n
  lg(n!) == ln(n!)/ln(10) == n * ln(n) / ln(10) - n / ln(10) == n * lg(n) - n / ln(10)
      n! == n ** n / exp(n)

So ((456!)!)! около

  lg(456!)       == 1014
  lg((456!)!)    == 1e1014 * 1014- 1e1014/ln(10) == 1e1017
  lg(((456!)!)!) == 1e(1e1017) 
     ((456!)!)!  == 1e(1e(1e1017))

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

Ответ 4

Это должно быть легко:

Как говорили другие, вы можете удалить все распространенные "!" потому что x > y <==> x! > y!

Ваша программа по существу должна доказать, что факториал (123!!!) больше обычного. Вы можете решить это с помощью быстрого выхода из цикла. При вычислении факториала вы можете вернуться, как только продукт будет больше 456, так как факториал всегда будет расти с дополнительными итерациями.

// While string parsing check if one number equals 0 and has at least
// one "!" - if yes set its value to 1 ( because 0! = 1! = 1 )

int x = 123;
int y = 456;
int numberOfFactorials = 3;

try
{
    for( int i = 0; i < numberOfFactorials; ++i )
    {
        for ( int j = x-1; j > 0; --j )
        {
            x *= j;
            // This quick exit will return after one iteration
            // because 123*122 > 456
            if ( x > y ) return "x is bigger than y";
        }
    }

    return x == y ? "gosh they are the same!"
                  : "x is smaller than y";
}
catch( OverflowException e )
{
   return "x Overflowed so it is bigger than y!";
}

Вы также можете использовать BigInteger с этим методом, если вы хотите анализировать еще большие числа для входных параметров.

Ответ 5

Как говорили другие, 123!!!!!! и 456!!! просто слишком большой, который должен быть представлен компьютером, а сравнение типа x!! <=> y! сводится к x! <=> y.

Как только вы доберетесь до минимально возможного количества ! (вырезая их из строк), вы можете оценить операнды. Одно из чисел будет общим целым числом (без факториала), поэтому здесь нет работы. Другой будет иметь по крайней мере один факториал, иначе сравнение тривиально.

Предположим, что сравнение x! <=> y (один факториал). Если x >= y, все готово. Если x < y, оцените x! и сравните.

Предположим, что сравнение x!! <=> y (два факториала). Табуляция наименьших значений:

1!! = 1! = 1
2!! = 2! = 2
3!! = 6! = 720
4!! = 24! = 620,448,401,733,239,439,360,000
5!! = 120! = about 6.6895 * 10^198
6!! = 720! = about 2.6012 * 10^1746

Итак, для любого y, x > 4 приведет к x!! > y. Для x <= 4 оцените x!! и сравните.

Для получения дополнительных факториалов помните, что x!!! = (x!)!!, оцените x! и используйте вышеприведенный шаг.

Ответ 6

BigInteger Тип может обрабатывать большие целые числа. Но не достаточно большой для вашего примера.

Малые факториалы могут быть учтены в их простые множители, без необходимости сначала вычислять факториал, и идентичные факторы могут быть отменены.

Вы также можете отменить трейлинг !, как было предложено Leherenn выше, начиная с 123!!! больше, чем 456, (123!!!)!!! также будет больше (456)!!!.

Ответ 7

Определим тип для представления операции повторных факториалов:

public struct RepeatedFactorial
{
  private readonly int _baseNumber;
  private readonly int _repeats;
  public int BaseNumber
  {
    get { return _baseNumber; }
  }
  public int Repeats {
    get { return _repeats; }
  }
  public RepeatedFactorial(int baseNumber, int repeats)
  {
    if (baseNumber < 0 || repeats < 0) throw new ArgumentOutOfRangeException();
    _baseNumber = baseNumber;
    _repeats = repeats;
  }
}

Теперь, если мы реализуем IComparable<Factorial>, мы можем найти требуемый ответ.

public int CompareTo(RepeatedFactorial other)
{
  // ?
}

Рассмотрим сначала некоторые из более простых случаев.

public int CompareTo(RepeatedFactorial other)
{
  if (BaseNumber == 0)
  {
    // If Repeats is zero the value of this is zero, otherwise
    // this is the same as a value with BaseNumber == 1 and no factorials.
    // So delegate to the handling of that case.
    if (Repeats == 0) return other.BaseNumber == 0 && other.Repeats == 0 ? 0 : -1;
    return new RepeatedFactorial(1, 0).CompareTo(other);
  }
  if (other.BaseNumber == 0)
    // Likewise
    return other.Repeats == 0 ? 1 : CompareTo(new RepeatedFactorial (1, 0));
  if (Repeats == other.Repeats)
    // X < Y == X! < Y!. X > Y == X! > Y! And so on.
    return BaseNumber.CompareTo(other.BaseNumber);
  ???
}

Хорошо, единственные случаи, которые не обрабатываются, - это то, где либо this имеет меньшее количество повторных факториалов, чем other или наоборот. Позвольте превратить один из этих случаев в другой, чтобы нам было меньше иметь дело:

public int CompareTo(RepeatedFactorial other)
{
  if (BaseNumber == 0)
  {
    // If Repeats is zero the value of this is zero, otherwise
    // this is the same as a value with BaseNumber == 1 and no factorials.
    // So delegate to the handling of that case.
    if (Repeats == 0) return other.BaseNumber == 0 && other.Repeats == 0 ? 0 : -1;
    return new RepeatedFactorial(1, 0).CompareTo(other);
  }
  if (other.BaseNumber == 0)
    // Likewise
    return other.Repeats == 0 ? 1 : CompareTo(new RepeatedFactorial (1, 0));
  if (Repeats == other.Repeats)
    // X < Y == X! < Y!. X > Y == X! > Y! And so on.
    return BaseNumber.CompareTo(other.BaseNumber);
  if (Repeats > other.Repeats)
    return -other.CompareTo(this);
  ???
}

Теперь нам нужно только беспокоиться о this, имеющем меньше повторений, чем other. Так как X > Y влечет X! > Y! и т.д. мы можем свести эту проблему до той, где мы знаем, что this имеет нулевые повторы:

public int CompareTo(RepeatedFactorial other)
{
  if (BaseNumber == 0)
  {
    // If Repeats is zero the value of this is zero, otherwise
    // this is the same as a value with BaseNumber == 1 and no factorials.
    // So delegate to the handling of that case.
    if (Repeats == 0) return other.BaseNumber == 0 && other.Repeats == 0 ? 0 : -1;
    return new RepeatedFactorial(1, 0).CompareTo(other);
  }
  if (other.BaseNumber == 0)
    // Likewise
      return other.Repeats == 0 ? 1 : CompareTo(new RepeatedFactorial (1, 0));
  if (Repeats == other.Repeats)
    // X < Y == X! < Y!. X > Y == X! > Y! And so on.
    return BaseNumber.CompareTo(other.BaseNumber);
  if (Repeats > other.Repeats)
    return -other.CompareTo(this);
  if (Repeats != 0)
    return new RepeatedFactorial(BaseNumber, 0).CompareTo(new RepeatedFactorial(other.BaseNumber, other.Repeats - Repeats);
  ???
}

Теперь нам нужно, чтобы this.BaseNumber сравнивалось с other.BaseNumber с соответствующим числом применяемых факториалов. Очевидно, если other.BaseNumber больше 12, то, поскольку 13! больше, чем int.MaxValue, оно должно быть больше this.BaseNumber:

public int CompareTo(RepeatedFactorial other)
{
  if (BaseNumber == 0)
  {
    // If Repeats is zero the value of this is zero, otherwise
    // this is the same as a value with BaseNumber == 1 and no factorials.
    // So delegate to the handling of that case.
    if (Repeats == 0) return other.BaseNumber == 0 && other.Repeats == 0 ? 0 : -1;
    return new RepeatedFactorial(1, 0).CompareTo(other);
  }
  if (other.BaseNumber == 0)
    // Likewise
    return other.Repeats == 0 ? 1 : CompareTo(new RepeatedFactorial (1, 0));
  if (Repeats == other.Repeats)
    // X < Y == X! < Y!. X > Y == X! > Y! And so on.
    return BaseNumber.CompareTo(other.BaseNumber);
  if (Repeats > other.Repeats)
    return -other.CompareTo(this);
  if (Repeats != 0)
    return new RepeatedFactorial(BaseNumber, 0).CompareTo(new RepeatedFactorial(other.BaseNumber, other.Repeats - Repeats);
  if (other.BaseNumber > 12)
    return -1; // this is less than other
  ???
}

Теперь мы вычисляем фактическое число. Однако если в начале цикла факториалов мы имеем 13 или выше, то мы можем вернуть -1 по той же логике, что и выше. В противном случае, если мы когда-нибудь закончим число, большее чем this.BaseNumber, мы также можем вернуть -1.

public int CompareTo(RepeatedFactorial other)
{
    if (BaseNumber == 0)
    {
      // If Repeats is zero the value of this is zero, otherwise
      // this is the same as a value with BaseNumber == 1 and no factorials.
      // So delegate to the handling of that case.
      if (Repeats == 0) return other.BaseNumber == 0 && other.Repeats == 0 ? 0 : -1;
      return new RepeatedFactorial(1, 0).CompareTo(other);
    }
    if (other.BaseNumber == 0)
      // Likewise
      return other.Repeats == 0 ? 1 : CompareTo(new RepeatedFactorial (1, 0));
  if (Repeats == other.Repeats)
    // X < Y == X! < Y!. X > Y == X! > Y! And so on.
    return BaseNumber.CompareTo(other.BaseNumber);
  if (Repeats > other.Repeats)
    return -other.CompareTo(this);
  if (Repeats != 0)
    return new RepeatedFactorial(BaseNumber, 0).CompareTo(new RepeatedFactorial(other.BaseNumber, other.Repeats - Repeats);
  int accum = other.BaseNumber;
  for (int rep = 0; rep != other.Repeats; ++rep)
  {
    if (accum > 12 || accum > BaseNumber) return -1;
    for (int mult = accum - 1; mult > 1; --mult)
    accum *= mult;
  }
  return BaseNumber.CompareTo(accum);
}

И поэтому у нас есть наш ответ и никогда не нужно вычислять факториал больше 12!.

Объединяя все это:

public struct RepeatedFactorial : IComparable<RepeatedFactorial>
{
  private readonly int _baseNumber;
  private readonly int _repeats;
  public int BaseNumber
  {
    get { return _baseNumber; }
  }
  public int Repeats {
    get { return _repeats; }
  }
  public RepeatedFactorial(int baseNumber, int repeats)
  {
    if (baseNumber < 0 || repeats < 0) throw new ArgumentOutOfRangeException();
    _baseNumber = baseNumber;
    _repeats = repeats;
  }
  public int CompareTo(RepeatedFactorial other)
  {
    if (BaseNumber == 0)
    {
      // If Repeats is zero the value of this is zero, otherwise
      // this is the same as a value with BaseNumber == 1 and no factorials.
      // So delegate to the handling of that case.
      if (Repeats == 0) return other.BaseNumber == 0 && other.Repeats == 0 ? 0 : -1;
      return new RepeatedFactorial(1, 0).CompareTo(other);
    }
    if (other.BaseNumber == 0)
      // Likewise
      return other.Repeats == 0 ? 1 : CompareTo(new RepeatedFactorial (1, 0));
    if (Repeats == other.Repeats)
      // X < Y == X! < Y!. X > Y == X! > Y! And so on.
      return BaseNumber.CompareTo(other.BaseNumber);
    if (Repeats > other.Repeats)
      return -other.CompareTo(this);
    if (Repeats != 0)
      return new RepeatedFactorial(BaseNumber, 0).CompareTo(new RepeatedFactorial(other.BaseNumber, other.Repeats - Repeats));
    int accum = other.BaseNumber;
    for (int rep = 0; rep != other.Repeats; ++rep)
    {
      if (accum > 12 || accum > BaseNumber) return -1;
      for (int mult = accum - 1; mult > 1; --mult)
        accum *= mult;
    }
    return BaseNumber.CompareTo(accum);
  }
}

Edit:

Я просто понял, что вы действительно использовали 64-битные значения в вашем вопросе. Это легко адаптируется, и нам все равно никогда не придется выходить за счет вычисления 20!

public struct RepeatedFactorial : IComparable<RepeatedFactorial>
{
  private readonly ulong _baseNumber;
  private readonly long _repeats;
  public ulong BaseNumber
  {
    get { return _baseNumber; }
  }
  public long Repeats {
    get { return _repeats; }
  }
  public RepeatedFactorial(ulong baseNumber, long repeats)
  {
    if (baseNumber < 0 || repeats < 0) throw new ArgumentOutOfRangeException();
    _baseNumber = baseNumber;
    _repeats = repeats;
  }
  public int CompareTo(RepeatedFactorial other)
  {
    if (BaseNumber == 0)
      // This is the same as a value with BaseNumber == 1 and no factorials.
      // So delegate to the handling of that case.
      return new RepeatedFactorial(1, 0).CompareTo(other);
    if (other.BaseNumber == 0)
      // Likewise
      return CompareTo(new RepeatedFactorial (1, 0));
    if (Repeats == other.Repeats)
      // X < Y == X! < Y!. X > Y == X! > Y! And so on.
      return BaseNumber.CompareTo(other.BaseNumber);
    if (Repeats > other.Repeats)
      return -other.CompareTo(this);
    if (Repeats != 0)
      return new RepeatedFactorial(BaseNumber, 0).CompareTo(new RepeatedFactorial(other.BaseNumber, other.Repeats - Repeats));
    ulong accum = other.BaseNumber;
    for (long rep = 0; rep != other.Repeats; ++rep)
    {
      if (accum > 20 || accum > BaseNumber) return -1;
      for (ulong mult = accum - 1; mult > 1; --mult)
        accum *= mult;
    }
    return BaseNumber.CompareTo(accum);
  }
}

Ответ 8

Для положительных целых чисел, если обе стороны имеют одинаковое количество факториалов, было бы так же просто, как сравнение двух чисел

123!!!!
456!!!!

456 > 123
456!!!! > 123!!!!

В противном случае сравнение двух факториалов сводится к этому

123!!!!!!
456!!!

(123!!!)!!!
(456!!!)

123!!!
456

В этот момент мы попытаемся оценить факториалы один за другим, пока не превысим другое число.

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

Ниже приведен pesudo-код, а не фактический код:

int max_factorial (int x, int x_fact, int y, int y_fact)
{
    int A=1,B=1,F=0,product=1,sum=0;

    if (x_fact == y_fact) return (x>y?x:y);

    if (x_fact > y_fact)
    {
        A = x; B = y; F = x_fact-y_fact;
    }
    else
    {
        A = y; B = x; F = y_fact-x_fact;
    }

    for (int k=0; k<F; k++)
    {
        try
        {
            for (int i=1; i<A; i++)
            {
                // multiplication in terms of addition
                // P * i = P + P + .. P } i times
                sum = 0; for (int p=0; p<i; p++) sum += product;
                product = product + sum;
                if (product > B) return A;
            }
        }
        catch (OverflowException e)
        {
            return A;
        }
    }

    return B;
}