web-dev-qa-db-fra.com

Comparer deux factorielles sans calculer

Existe-t-il un moyen de comparer le nombre factoriel le plus élevé entre deux nombres sans effectuer de calcul?
Le scénario est que je crée une application console c # qui prend deux entrées factorielles comme 

123!!!!!!
456!!!  

tout ce que je veux faire est de comparer quelle valeur factorielle est plus grande que les autres, le code que j'ai fait est 

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

mais l'erreur que je reçois est
la valeur est trop grande ou trop petite pour le nombre décimal
J'ai compris l'erreur mais existe-t-il un moyen de le faire? 

Indiquez s'il existe un autre type de données contenant une valeur supérieure à décimale ou s'il existe un autre moyen de comparer ces factorielles. 

Après avoir implémenté la suggestion de @Bathsheba, je change un peu de mon code 

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

Désolé de dire mais quand même 123 !!! est tellement énorme que je reçois la même erreur


Traditionnellement m!!...! avec n!s signifie m(m-n)(m-2n).... mais sa valeur est prise en tant que (...((m!)!)!...)!
Note de Alec, oui, je sais, c’est une notation malheureuse, mais vous voyez que la définition conventionnelle est beaucoup plus utile (en combinatoire, le lieu d’origine des factorielles) que celle souhaitée par le PO.
Je mettrais cela dans un commentaire, mais les autres l’éclipseraient et c’est très important.

37
Co. Aden

Ici, a!! est défini comme (a!)!.

123!!!!!! est absolument gigantesque. Je pense que vous auriez besoin de plus de particules qu'il n'y en a dans l'univers si vous les notiez à l'encre.

Vous ne pouvez donc pas comparer les chiffres directement. Je suppose qu'il n'y a pas une classe de nombres capable de le faire.

Ce que vous pouvez faites, est de considérer le quotient 123!!!!!! / 456!!!. La plupart des multiples seront similaires, vous pouvez donc les annuler. Notez également que le ! final sera annulé. C'est parce que x> y implique, et est impliqué par x! > y! où x et y sont des entiers positifs.

Finalement, vous atteindrez un point où vous pouvez évaluer cela comme étant inférieur ou supérieur à 1, donnant ainsi votre réponse.

Je peux vous dire à l'inspection que 123!!!!!! est plus grand que 123!!! est plus grand que 456.

52
Bathsheba

Contrairement aux autres réponses, vous pouvez le faire sans aucune approximation. 

C'est ici : 

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

signifie réellement 

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

et aussi

123 !!! > 456  

Il suffit donc de prouver ce qui précède. C’est simple, car vous avez au moins un opérande qui peut tenir dans un UInt64

Donc, cela devrait vous donner quelque chose comme ça: 

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


}
22
Mr. Fahrenheit

Pour des nombres donnés, en supposant que 456!!! signifie ((456!)!)! nous avons

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

et 

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

même 123! (qui est 1.2e205) bien plus grand que 456

Pour estimer les valeurs réelles des factorielles, utilisons approximation de Stirling

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

c'est à dire.

  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)

Donc, ((456!)!)! est sur le point

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

qui est nombre extrêmement élevé _ (note triple exponentiation) et c'est pourquoi on ne peut pas le représenter sous la forme d'une valeur naïve BigInteger.

14
Dmitry Bychenko

Cela devrait être facile:

Comme d’autres l’ont dit, vous pouvez supprimer tous les "!" parce que x > y <==> x! > y!

Votre programme devra essentiellement prouver qu'un facteur (123 !!!) est supérieur à un nombre ordinaire. Vous pouvez résoudre ce problème en sortant rapidement de la boucle. Lors du calcul de la factorielle, vous pouvez retourner dès que le produit est supérieur à 456, puisqu'un factoriel augmentera toujours avec des itérations supplémentaires.

// 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!";
}

Vous pouvez également utiliser BigInteger avec cette méthode si vous souhaitez analyser des nombres encore plus grands pour les paramètres d'entrée.

6
Falco

Comme d'autres l'ont dit, 123 !!!!!! et 456 !!! sont simplement trop gros pour être représentés par un ordinateur, et une comparaison du type x!! <=> y! se réduit à x! <=> y.

Une fois que vous avez atteint le nombre minimum possible de ! (en les coupant dans les chaînes), vous pouvez évaluer les opérandes. L'un des nombres sera un entier commun (pas de factoriel), donc pas de travail ici. L'autre aura au moins une factorielle, sinon la comparaison est triviale.

Supposons que la comparaison est x! <=> y (une factorielle). Si x >= y, vous avez terminé. Si x < y, évaluez x! et comparez.

Supposons que la comparaison est x!! <=> y (deux factorielles). Tabuler les plus petites valeurs:

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

Donc, pour à peu près toute y, x > 4 donnera x!! > y. Pour x <= 4, évaluez x!! et comparez.

Pour plus de factorielles, rappelez-vous que x!!! = (x!)!!, évaluez x! et utilisez les étapes ci-dessus.

2
jose_castro_arnaud

Le BigInteger Type peut gérer des entiers de grande taille. Mais pas assez grand pour votre exemple.

Les petites factorielles peuvent être intégrées dans leurs facteurs premiers, sans avoir à calculer d'abord la factorielle elle-même, et les facteurs identiques peuvent être annulés.

Vous pouvez également annuler les ! finaux comme suggéré par Leherenn ci-dessus , depuis 123 !!! est plus grand que 456, (123 !!!) !!! sera également plus grand que (456) !!!.

1
User42

Pour les entiers positifs, si les deux côtés ont le même nombre de factorielles, ce serait aussi simple que de comparer les deux nombres.

123!!!!
456!!!!

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

Sinon, comparer les deux factorielles revient à ceci

123!!!!!!
456!!!

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

123!!!
456

À ce stade, nous allons essayer d’évaluer les factorielles une par une, jusqu’à ce que nous ayons dépassé l’autre nombre.

Comme l'autre nombre est un nombre pouvant être stocké dans une variable, cela signifie que si nous avons atteint un nombre plus élevé en calcul ou si une exception de débordement a été interceptée, il s'agit d'un nombre plus grand, sinon il est plus petit.

Ce qui suit est un code pesudo, pas un code réel:

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;
}
0
Khaled.K

Définissons un type pour représenter une opération de factorielles répétées:

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

Maintenant, si nous implémentons un IComparable<Factorial>, nous pouvons trouver la réponse voulue.

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

Examinons d’abord les cas les plus simples.

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

OK, les seuls cas non traités sont ceux où this a moins de factorielles répétées que other ou vice versa. Tournons l'un de ces cas dans l'autre afin que nous ayons moins à traiter:

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

Maintenant, nous n'avons plus qu'à nous soucier de this ayant moins de répétitions que other. Puisque X> Y implique X! > Y! et ainsi de suite, nous pouvons réduire ce problème à un problème où nous savons que this a zéro répétition:

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

Nous devons maintenant comparer this.BaseNumber à other.BaseNumber avec le nombre approprié de factorielles appliquées. Évidemment, si other.BaseNumber est supérieur à 12, alors depuis 13! est supérieur à int.MaxValue il doit être supérieur à 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
  ???
}

Il ne nous reste plus qu'à calculer le nombre réel. Toutefois, si au début d'un cycle de factorielles, nous avons 13 ou supérieur, nous pouvons renvoyer -1 par la même logique que ci-dessus. Sinon, si nous nous retrouvons avec un nombre supérieur à this.BaseNumber, nous pouvons aussi renvoyer -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);
}

Et donc nous avons notre réponse et ne devons jamais calculer une factorielle supérieure à 12!.

Mettre tous ensemble:

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

Modifier:

Je viens de me rendre compte que vous utilisiez en fait des valeurs 64 bits dans votre question. C'est facilement adapté pour, et nous n'avons toujours pas à dépasser le calcul de 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);
  }
}
0
Jon Hanna