LINQ聚合函数在13阶乘后无法正常工作?

zpgglvta  于 2022-12-20  发布在  其他
关注(0)|答案(2)|浏览(119)

出于我的好奇心,我写了一个代码来寻找大数的阶乘,例如:25。为此,我写了下面的代码。我使用BigInteger,因为它可以存储任何长度的值,如每个内存定义。

int a = 13;
BigInteger factorial1 = a == 0 ? 1 : Enumerable.Range(1, a).Aggregate((i, j) => i * j); // Aggregate function not working properly after 12 factorial

Console.WriteLine(factorial1);

但令人惊讶的是,我没有得到正确的答案,我尝试了更小的数字,直到12的阶乘,它给出了正确的答案,但对于13及以上,答案是错误的。
我试过这个非常简单的代码,它给出了正确的答案。

BigInteger factorial3 = 1;
while (n > 0)
{
    factorial3 = factorial3 * n;
    --n;
}
Console.WriteLine(factorial3);

但是这里的问题是BigInteger是不可变的,所以上面的代码占用了很大的内存,这是不可取的。

jaql4c8m

jaql4c8m1#

您应该指定起始聚合值,它应该是BigInteger.One

BigInteger factorial1 = a == 0 ? 1 : Enumerable
  .Range(1, a)
  .Aggregate(BigInteger.One, (i, j) => i * j);

否则,对于.Aggregate((i, j) => i * j),结果将是int类型(因为ij都是int),并且只有在那时(在 * 整数溢出 * 之后,13! = 6227020800 > int.MaxValue = 2147483647),它才将被强制转换为BigInteger

hkmswyz6

hkmswyz62#

Enumerable.Range生成int s,而不是BigInteger s。Enumerable.Range(1, a).Aggregate((i, j) => i * j);始终使用Int32,并在13之后结束溢出。
为了避免溢出,您需要在处理这些整数之前将它们转换为BigInteger:

var n=Enumerable.Range(1, 13).Select(i=>(BigInteger)i)
                             .Aggregate((i, j) => i * j);

这将生成6227020800
另一个选项是创建您自己的Range方法,该方法创建BigInteger值:

public static IEnumerable<BigInteger> BigRange(BigInteger start,int count)
{
    for(var i=start;i<start+count;i++)
    {
        yield return i;
    }
}

...

var n=BigRange(1, 13).Aggregate((i, j) => i * j);

相关问题