計算せずに、2つの数値の中でどちらの階乗数が大きいかを比較する方法はありますか?
シナリオは、次のような2つの階乗入力を受け取るc#コンソールアプリケーションを作成していることです。
_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();
}
_
しかし、私が得ているエラーは
値が10進数に対して大きすぎるか小さすぎる
エラーは理解しましたが、これを行う方法はありますか
10進数より大きい値を収容する他のデータ型があるかどうか、またはこれらの階乗を比較する他の方法があるかどうかを提案してください
@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
_!
_ sはm(m-n)(m-2n)....
を意味しますが、ここでは_(...((m!)!)!...)!
_と見なされます。
アレックからのメモ、はい、私は知っています、これは不幸な表記ですが、従来の定義は、OPが望むものよりもはるかに便利です(組み合わせ論では、階乗が由来する場所)。
これをコメントに入れますが、他の人に隠されてしまうので、これは非常に重要です。
ここで、a!!
は(a!)!
として定義されています。
123!!!!!!
は絶対に巨大です。インクで書き留めるとしたら、宇宙にあるよりも多くの粒子が必要になると思います。
したがって、数値を直接比較することはできません。これができる数のクラスはないと思います。
canすることは、商123!!!!!! / 456!!!
を考慮することです。倍数の多くは類似しているので、キャンセルすることができます。末尾の!
はキャンセルされることにも注意してください。これは、x> yが意味し、xによって暗示されるためです。 > y!ここで、xとyは正の整数です。
最終的には、これを1未満または1より大きいと評価できるようになるので、答えが得られます。
検査時123!!!!!!
は123!!!
よりも大きいため、456
の方が大きいことがわかります。
他の答えとは異なり、あなたはそれを行うことができます近似なしで。
ここにあります :
123 !!!!!! > 456 !!!
実際に意味します
123 !!!!! > 456 !!
123 !!!! > 456 !
そしてまた
123 !!! > 456
したがって、上記を証明するだけで済みます。UInt64
に収まるオペランドが少なくとも1つあるので、簡単です。
だからこれはあなたにこのようなものを与えるはずです:
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();
}
}
与えられた数について、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)
つまり、((456!)!)!
は約
lg(456!) == 1014
lg((456!)!) == 1e1014 * 1014- 1e1014/ln(10) == 1e1017
lg(((456!)!)!) == 1e(1e1017)
((456!)!)! == 1e(1e(1e1017))
これは非常に膨大な数(注3乗)であり、それがナイーブとして表現できない理由ですBigInteger
値。
これは簡単なはずです:
他の人が言っているように、あなたはすべての一般的な「!」を削除することができますなぜなら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!";
}
Input-Parametersのさらに大きな数値を解析する場合は、このメソッドでBigIntegerを使用することもできます。
他の人が言ったように、123 !!!!!!と456 !!!ただ大きすぎるコンピュータで表現するには、タイプ_x!! <=> y!
_の比較は_x! <=> y
_になります。
_!
_の可能な最小数(文字列からそれらを切り取る)に達したら、オペランドを評価できます。数値の1つは一般的な整数(階乗なし)になるため、ここでは機能しません。もう一方には少なくとも1つの階乗があります。それ以外の場合、比較は簡単です。
比較が_x! <=> y
_(1階乗)であると仮定します。 _x >= y
_の場合、これで完了です。 _x < y
_の場合は、_x!
_を評価して比較します。
比較が_x!! <=> y
_(2階乗)であると仮定します。最小値の表:
_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!
_を評価し、上記の手順を使用してください。
BigInteger タイプは大きな整数を処理できます。しかし、あなたの例には十分な大きさではありません。
小さな階乗は、最初に階乗自体を計算する必要なしに、素因数に因数分解でき、同じ因数をキャンセルできます。
提案されているように、末尾の!
をキャンセルすることもできます 上記のLeherennによる 、123以降!!! 456より大きい(123 !!!)!!!また、(456)よりも大きくなります!!!。
正の整数の場合、両側の階乗の数が同じであれば、2つの数を比較するのと同じくらい簡単です。
123!!!!
456!!!!
456 > 123
456!!!! > 123!!!!
そうでなければ、2つの階乗を比較すると、これに帰着します
123!!!!!!
456!!!
(123!!!)!!!
(456!!!)
123!!!
456
この時点で、他の数を超えるまで、階乗を1つずつ評価しようとします。
もう1つの数値は変数に格納できる数値であるため、これは、天気が計算上より高い数値に到達したか、オーバーフロー例外をキャッチしたことを意味します。それ以外の場合は、より大きな数値になります。
以下は疑似コードであり、実際のコードではありません。
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;
}
繰り返される階乗の演算を表す型を定義しましょう。
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
より少ないかその逆の場合です。これらのケースの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);
???
}
これで、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);
}
}
編集:
質問で実際に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);
}
}