大整数/BigRational问题,转换为double和back。

时间:2021-11-20 16:49:27

Maybe I didn't understood how to use those two types: BigInteger/BigRational, but generally speaking I want to implement this equations: 大整数/BigRational问题,转换为double和back。

也许我不知道如何使用这两种类型:BigInteger/BigRational,但一般来说,我想要实现这个等式:

This is my data: n=235, K = 40 and this small p (which actually is called rho) is 5. In the beginning the problem was with the Power function: the results were very very very big - so that is why I used the BigInteger library. But then I realize that there will be a division made and the result will be a number of type double - so I changed to BigRational library.

这是我的数据,n=235, K = 40,这个小p(实际上叫做)是5。在开始时,问题在于幂函数:结果非常非常大——这就是为什么我使用了BigInteger库。但后来我意识到将会有一个部门,结果将会是一些类型的double -所以我改为BigRational library。

Here is my code:

这是我的代码:

static void Main(string[] args)
    {
        var k = 40;
        var n = 235;
        var p = 5;

        // the P(n) equation
        BigRational pnNumerator = BigRational.Pow(p, n);
        BigRational pnDenominator = BigRational.Pow(k, (n - k)) * Factorial(k);


        // the P(0) equation

        //---the right side of "+" sign in Denominator
        BigRational pk = BigRational.Pow(p, k);
        BigRational factorialK = Factorial(k);
        BigRational lastPart = (BigRational.Subtract(1, (double)BigRational.Divide(p, k)));
        BigRational factorialAndLastPart = BigRational.Multiply(factorialK, lastPart);
        BigRational fullRightSide = BigRational.Divide(pk, factorialAndLastPart);
        //---the left side of "+" sign in Denominator
        BigRational series = Series(k, p, n);


        BigRational p0Denominator = series + fullRightSide;
        BigRational p0Result = BigRational.Divide(1, p0Denominator);

        BigRational pNResult = BigRational.Divide((pnNumerator * p0Result), pnDenominator);
        Console.WriteLine(pNResult);
        Console.ReadKey();
    }

    static BigRational Series(int k, int p, int n)
    {
        BigRational series = new BigRational(0.0);
        var fin = k - 1;
        for (int i = 0; i < fin; i++)
        {
            var power = BigRational.Pow(p, i);
            var factorialN = Factorial(n);
            var sum = BigRational.Divide(power, factorialN);
            series += sum;
        }
        return series;
    }

    static BigRational Factorial(int k)
    {
        if (k <= 1)
            return 1;
        else return BigRational.Multiply(k, Factorial(k - 1));
    }

And the main problem is that it doesn't return any "normal" value like for example 0.3 or 0.03. The result which is printed to the console is a very long number (like 1200 digits in it)...

主要的问题是它没有返回任何“正常”值,例如0.3或0.03。输出到控制台的结果是一个非常长的数字(比如它的1200位数)……

Can someone please take a look at my code and help me fix the problem and be able to solve this equations by the code. Thank you

请大家看看我的代码,帮助我解决这个问题,并能通过代码来解决这个问题。谢谢你!

1 个解决方案

#1


5  

Console.WriteLine(pNResult); calls BigRational.ToString() under-the-hood, which prints the number in the form numerator/denominator.

Console.WriteLine(pNResult);调用bigration. tostring()底层,它打印出分子/分母的数字。

It's easy to miss the / in the output given how large the numerator and denominator both are in this case.

考虑到分子和分母都有多大,很容易忽略输出。

BigRational supports conversions to decimal and to double. The result is too small to fit in a decimal in this case though. Converting to a double, gives the result 7.89682541396914E-177.

BigRational支持转换为decimal和double。结果是太小了,不能在这个例子中放入小数。转换为double,结果为7.89682541396914E-177。

If you need better precision, you'll need a custom conversion to a decimal-format string, like the one in this * answer.

如果您需要更好的精度,您将需要一个自定义转换到一个十进制格式的字符串,就像这个*的答案一样。

Using that custom conversion routine to get the result to 1000 decimal places -

使用这个自定义转换例程将结果计算到1000位小数。

Console.WriteLine(pNResult.ToDecimalString(1000));

- gives the result as:

-给出的结果为:

0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000078968254139691306770128897137459492828971170349380336740935269651539684650525033676003134593283361305530675112470528408219177025044254116462798561450442318290046626248451723040397770263675109107145461310779641705093156106311143727608208629473359566457461384474633112850335950017209558136575135801388668687571284492241030561019606955986265585636660304889792027894460104216176719717671500843399685686146432982358441225578366059001576682388503227237202077881334695352338638383337717103303153521108812750644260562351186866587629456292506971252525125976755540274041651740194108430555751648707933592643410475214924394223640168857340953563111097979394441303100701008120008166339365089771585037880235325673143152814510586536335380671360865230428857049658368242543653234599817430185879648427434216378356518036776477170130227628307039

0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000078968254139691306770128897137459492828971170349380336740935269651539684650525033676003134593283361305530675112470528408219177025044254116462798561450442318290046626248451723040397770263675109107145461310779641705093156106311143727608208629473359566457461384474633112850335950017209558136575135801388668687571284492241030561019606955986265585636660304889792027894460104216176719717671500843399685686146432982358441225578366059001576682388503227237202077881334695352338638383337717103303153521108812750644260562351186866587629456292506971252525125976755540274041651740194108430555751648707933592643410475214924394223640168857340953563111097979394441303100701008120008166339365089771585037880235325673143152814510586536335380671360865230428857049658368242543653234599817430185879648427434216378356518036776477170130227628307039


To check that your calculation code is operating correctly, you can add unit-tests for the different functions (Factorial, Series and the computation of P itself).

为了检查您的计算代码是否正确运行,您可以为不同的函数添加单元测试(Factorial、Series和P本身的计算)。

An approach that is practical here is to calculate the results by hand for certain small values of k, n and p and check that your functions compute the same results.

这里的实用方法是用手工计算出k、n和p的小值,并检查函数是否计算相同的结果。

If you're using Visual Studio, you can use this MSDN page as a starting point for creating a unit-test project. Note that the functions under test must be visible to the unit-test project, and your unit-test project will need to have a reference added to your existing project where you're doing the computation, as explained in the link.

如果您正在使用Visual Studio,您可以使用这个MSDN页面作为创建单元测试项目的起点。请注意,测试中的函数必须对单元测试项目可见,而您的单元测试项目需要在您的现有项目中添加一个引用,在这个项目中,您正在进行计算,正如链接所解释的那样。

Starting with Factorial, which is the easiest to check, you could add a test like this:

从阶乘开始,这是最容易检查的,您可以添加这样的测试:

[TestClass]
public class UnitTestComputation
{
    [TestMethod]
    public void TestFactorial()
    {
        Assert.AreEqual(1, Program.Factorial(0));
        Assert.AreEqual(1, Program.Factorial(1));
        Assert.AreEqual(2, Program.Factorial(2));
        Assert.AreEqual(6, Program.Factorial(3));
        Assert.AreEqual(24, Program.Factorial(4));
    }
}

The code in your question passes that test.

您的问题中的代码通过了这个测试。

You can then add a test method for your Series function:

然后,您可以为您的系列函数添加一个测试方法:

[TestMethod]
public void TestSeries()
{
    int k = 1;
    int p = 1;
    BigRational expected = 1;
    Assert.AreEqual(expected, Program.Series(k, p));

    k = 2;
    p = 1;
    expected += 1;
    Assert.AreEqual(expected, Program.Series(k, p));

    k = 3;
    p = 1;
    expected += (BigRational)1 / (BigRational)2;
    Assert.AreEqual(expected, Program.Series(k, p));

    k = 1;
    p = 2;
    expected = 1;
    Assert.AreEqual(expected, Program.Series(k, p));

    k = 2;
    p = 2;
    expected += 2;
    Assert.AreEqual(expected, Program.Series(k, p));
}

This showed up some problems in your code:

这在你的代码中出现了一些问题:

  • n shouldn't actually be a parameter to this function, because in this context n isn't the parameter to function P, but actually just the "index-of-summation". n's local value in this function is represented by your i variable.
  • n不应该是这个函数的一个参数,因为在这个上下文中n不是函数P的参数,而是“求和的指数”。n在这个函数中的局部值由i变量表示。
  • This then means that your Factorial(n) call needs to change to Factorial(i)
  • 这意味着您的Factorial(n)调用需要更改为Factorial(i)
  • The loop is also off-by-one, because the Sigma notation for the summation is inclusive of the number at the top of the Sigma, so you should have <= fin (or you could also have written this simply as < k).
  • 这个循环也是不一样的,因为求和的符号是包含在Sigma顶端的数字,所以你应该有<= fin(或者你也可以把它写成< k)。

This is the updated Series function:

这是更新后的系列函数:

// CHANGED: Removed n as parameter (n just the index of summation here)
public static BigRational Series(int k, int p)
{
    BigRational series = new BigRational(0.0);
    var fin = k - 1;
    // CHANGED: Should be <= fin (i.e. <= k-1, or < k) because it's inclusive counting
    for (int i = 0; i <= fin; i++)
    {
        var power = BigRational.Pow(p, i);
        // CHANGED: was Factorial(n), should be factorial of n value in this part of the sequence being summed.
        var factorialN = Factorial(i);
        var sum = BigRational.Divide(power, factorialN);
        series += sum;
    }
    return series;
}

To test the P(n) calculation you can move that out into its own function to test (I've called it ComputeP here):

为了测试P(n)的计算,您可以将其移到它自己的函数中进行测试(我将它称为ComputeP):

[TestMethod]
public void TestP()
{
    int n = 1;
    int k = 2;
    int p = 1;
    // P(0) = 1 / (2 + 1/(2*(1 - 1/2))) = 1/3
    // P(1) = (1/(1/2 * 2)) * P(0) = P(0) = 1/3 
    BigRational expected = 1;
    expected /= 3;
    Assert.AreEqual(expected, Program.ComputeP(k, n, p));

    n = 2;
    k = 2;
    p = 1;
    // P(2) = (1/(1*2)) * P(0) = 1/6
    expected = 1;
    expected /= 6;
    Assert.AreEqual(expected, Program.ComputeP(k, n, p));
}

This showed up a problem with calculating P(n) - you had a cast to double in there which shouldn't have been present (the result is inaccurate then - you need to keep all the intermediate results in BigRational). There's no need for the cast, so just removing it fixes this problem.

这在计算P(n)时出现了一个问题——你有一个在不应该出现的情况下的替身(结果是不准确的——你需要保留所有中间结果的BigRational)。不需要cast,所以只要移除它就可以解决这个问题。

Here is the updated ComputeP function:

这里是更新后的ComputeP函数:

public static BigRational ComputeP(int k, int n, int p)
{
    // the P(n) equation
    BigRational pnNumerator = BigRational.Pow(p, n);
    BigRational pnDenominator = BigRational.Pow(k, (n - k)) * Factorial(k);


    // the P(0) equation

    //---the right side of "+" sign in Denominator
    BigRational pk = BigRational.Pow(p, k);
    BigRational factorialK = Factorial(k);
    // CHANGED: Don't cast to double here (loses precision)
    BigRational lastPart = (BigRational.Subtract(1, BigRational.Divide(p, k)));
    BigRational factorialAndLastPart = BigRational.Multiply(factorialK, lastPart);
    BigRational fullRightSide = BigRational.Divide(pk, factorialAndLastPart);
    //---the left side of "+" sign in Denominator
    BigRational series = Series(k, p);


    BigRational p0Denominator = series + fullRightSide;
    BigRational p0Result = BigRational.Divide(1, p0Denominator);

    BigRational pNResult = BigRational.Divide((pnNumerator * p0Result), pnDenominator);
    return pNResult;
}

For avoidance of confusion, here is the whole updated calculation program:

为避免混淆,这里是整个更新的计算程序:

using System;
using System.Numerics;
using System.Text;
using Numerics;

public class Program
{
    public static BigRational ComputeP(int k, int n, int p)
    {
        // the P(n) equation
        BigRational pnNumerator = BigRational.Pow(p, n);
        BigRational pnDenominator = BigRational.Pow(k, (n - k)) * Factorial(k);


        // the P(0) equation

        //---the right side of "+" sign in Denominator
        BigRational pk = BigRational.Pow(p, k);
        BigRational factorialK = Factorial(k);
        // CHANGED: Don't cast to double here (loses precision)
        BigRational lastPart = (BigRational.Subtract(1, BigRational.Divide(p, k)));
        BigRational factorialAndLastPart = BigRational.Multiply(factorialK, lastPart);
        BigRational fullRightSide = BigRational.Divide(pk, factorialAndLastPart);
        //---the left side of "+" sign in Denominator
        BigRational series = Series(k, p);


        BigRational p0Denominator = series + fullRightSide;
        BigRational p0Result = BigRational.Divide(1, p0Denominator);

        BigRational pNResult = BigRational.Divide((pnNumerator * p0Result), pnDenominator);
        return pNResult;
    }

    // CHANGED: Removed n as parameter (n just the index of summation here)
    public static BigRational Series(int k, int p)
    {
        BigRational series = new BigRational(0.0);
        var fin = k - 1;
        // CHANGED: Should be <= fin (i.e. <= k-1, or < k) because it's inclusive counting
        for (int i = 0; i <= fin; i++)
        {
            var power = BigRational.Pow(p, i);
            // CHANGED: was Factorial(n), should be factorial of n value in this part of the sequence being summed.
            var factorialN = Factorial(i);
            var sum = BigRational.Divide(power, factorialN);
            series += sum;
        }
        return series;
    }

    public static BigRational Factorial(int k)
    {
        if (k <= 1)
            return 1;
        else return BigRational.Multiply(k, Factorial(k - 1));
    }

    static void Main(string[] args)
    {
        var k = 40;
        var n = 235;
        var p = 5;
        var result = ComputeP(k, n, p);
        Console.WriteLine(result.ToDecimalString(1000));
        Console.ReadKey();
    }
}

// From https://*.com/a/10359412/4486839
public static class BigRationalExtensions
{
    public static string ToDecimalString(this BigRational r, int precision)
    {
        var fraction = r.GetFractionPart();

        // Case where the rational number is a whole number
        if (fraction.Numerator == 0 && fraction.Denominator == 1)
        {
            return r.GetWholePart() + ".0";
        }

        var adjustedNumerator = (fraction.Numerator
                                           * BigInteger.Pow(10, precision));
        var decimalPlaces = adjustedNumerator / fraction.Denominator;

        // Case where precision wasn't large enough.
        if (decimalPlaces == 0)
        {
            return "0.0";
        }

        // Give it the capacity for around what we should need for 
        // the whole part and total precision
        // (this is kinda sloppy, but does the trick)
        var sb = new StringBuilder(precision + r.ToString().Length);

        bool noMoreTrailingZeros = false;
        for (int i = precision; i > 0; i--)
        {
            if (!noMoreTrailingZeros)
            {
                if ((decimalPlaces % 10) == 0)
                {
                    decimalPlaces = decimalPlaces / 10;
                    continue;
                }

                noMoreTrailingZeros = true;
            }

            // Add the right most decimal to the string
            sb.Insert(0, decimalPlaces % 10);
            decimalPlaces = decimalPlaces / 10;
        }

        // Insert the whole part and decimal
        sb.Insert(0, ".");
        sb.Insert(0, r.GetWholePart());

        return sb.ToString();
    }
}

And here is the whole unit-test program:

这是整个单元测试程序:

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Numerics;


[TestClass]
public class UnitTestComputation
{
    [TestMethod]
    public void TestFactorial()
    {
        Assert.AreEqual(1, Program.Factorial(0));
        Assert.AreEqual(1, Program.Factorial(1));
        Assert.AreEqual(2, Program.Factorial(2));
        Assert.AreEqual(6, Program.Factorial(3));
        Assert.AreEqual(24, Program.Factorial(4));
    }

    [TestMethod]
    public void TestSeries()
    {
        int k = 1;
        int p = 1;
        BigRational expected = 1;
        Assert.AreEqual(expected, Program.Series(k, p));

        k = 2;
        p = 1;
        expected += 1;
        Assert.AreEqual(expected, Program.Series(k, p));

        k = 3;
        p = 1;
        expected += (BigRational)1 / (BigRational)2;
        Assert.AreEqual(expected, Program.Series(k, p));

        k = 1;
        p = 2;
        expected = 1;
        Assert.AreEqual(expected, Program.Series(k, p));

        k = 2;
        p = 2;
        expected += 2;
        Assert.AreEqual(expected, Program.Series(k, p));
    }

    [TestMethod]
    public void TestP()
    {
        int n = 1;
        int k = 2;
        int p = 1;
        // P(0) = 1 / (2 + 1/(2*(1 - 1/2))) = 1/3
        // P(1) = (1/(1/2 * 2)) * P(0) = P(0) = 1/3 
        BigRational expected = 1;
        expected /= 3;
        Assert.AreEqual(expected, Program.ComputeP(k, n, p));

        n = 2;
        k = 2;
        p = 1;
        // P(2) = (1/(1*2)) * P(0) = 1/6
        expected = 1;
        expected /= 6;
        Assert.AreEqual(expected, Program.ComputeP(k, n, p));
    }
}

Incidentally, the P(n) result with the updated program for your input values for n, p and k is now:

顺便说一下,P(n)的结果是为n, P和k的输入值更新的程序现在:

0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000593109980769066916025972569398424267669807629726200017375290861590898269902277869938365969961320969473356001666906480007119114830921839913623591124192047955091318951831902550404167336054683697071654765071519020060437129398945035521954738463786221029427589397688847246112810536958194364039693387170592425527136243952416704526069736811587380688876091926255908361275575249492845970903676492429684929779402600032481018886875698972533534890841796034626337674846620462046294537488580901129338625628349474358946962065227890599744775562637784553656488649841148591533557896418988044457914999854241038974478576578909626765823565817758792682480009619613438867365912697996527957775248350987801430141776875171808382272960426476953742528769626555642957093028553993908356226007570404005591174451216846471710162760343

0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000593109980769066916025972569398424267669807629726200017375290861590898269902277869938365969961320969473356001666906480007119114830921839913623591124192047955091318951831902550404167336054683697071654765071519020060437129398945035521954738463786221029427589397688847246112810536958194364039693387170592425527136243952416704526069736811587380688876091926255908361275575249492845970903676492429684929779402600032481018886875698972533534890841796034626337674846620462046294537488580901129338625628349474358946962065227890599744775562637784553656488649841148591533557896418988044457914999854241038974478576578909626765823565817758792682480009619613438867365912697996527957775248350987801430141776875171808382272960426476953742528769626555642957093028553993908356226007570404005591174451216846471710162760343


NOTE: You should add to the unit-tests with more results you've checked by hand, and also check any of my working here in interpreting the algebra as code to ensure this is correct.

注意:您应该在单元测试中添加更多您已经手动检查的结果,并检查我在这里的任何工作,将代数解释为代码,以确保这是正确的。

#1


5  

Console.WriteLine(pNResult); calls BigRational.ToString() under-the-hood, which prints the number in the form numerator/denominator.

Console.WriteLine(pNResult);调用bigration. tostring()底层,它打印出分子/分母的数字。

It's easy to miss the / in the output given how large the numerator and denominator both are in this case.

考虑到分子和分母都有多大,很容易忽略输出。

BigRational supports conversions to decimal and to double. The result is too small to fit in a decimal in this case though. Converting to a double, gives the result 7.89682541396914E-177.

BigRational支持转换为decimal和double。结果是太小了,不能在这个例子中放入小数。转换为double,结果为7.89682541396914E-177。

If you need better precision, you'll need a custom conversion to a decimal-format string, like the one in this * answer.

如果您需要更好的精度,您将需要一个自定义转换到一个十进制格式的字符串,就像这个*的答案一样。

Using that custom conversion routine to get the result to 1000 decimal places -

使用这个自定义转换例程将结果计算到1000位小数。

Console.WriteLine(pNResult.ToDecimalString(1000));

- gives the result as:

-给出的结果为:

0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000078968254139691306770128897137459492828971170349380336740935269651539684650525033676003134593283361305530675112470528408219177025044254116462798561450442318290046626248451723040397770263675109107145461310779641705093156106311143727608208629473359566457461384474633112850335950017209558136575135801388668687571284492241030561019606955986265585636660304889792027894460104216176719717671500843399685686146432982358441225578366059001576682388503227237202077881334695352338638383337717103303153521108812750644260562351186866587629456292506971252525125976755540274041651740194108430555751648707933592643410475214924394223640168857340953563111097979394441303100701008120008166339365089771585037880235325673143152814510586536335380671360865230428857049658368242543653234599817430185879648427434216378356518036776477170130227628307039

0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000078968254139691306770128897137459492828971170349380336740935269651539684650525033676003134593283361305530675112470528408219177025044254116462798561450442318290046626248451723040397770263675109107145461310779641705093156106311143727608208629473359566457461384474633112850335950017209558136575135801388668687571284492241030561019606955986265585636660304889792027894460104216176719717671500843399685686146432982358441225578366059001576682388503227237202077881334695352338638383337717103303153521108812750644260562351186866587629456292506971252525125976755540274041651740194108430555751648707933592643410475214924394223640168857340953563111097979394441303100701008120008166339365089771585037880235325673143152814510586536335380671360865230428857049658368242543653234599817430185879648427434216378356518036776477170130227628307039


To check that your calculation code is operating correctly, you can add unit-tests for the different functions (Factorial, Series and the computation of P itself).

为了检查您的计算代码是否正确运行,您可以为不同的函数添加单元测试(Factorial、Series和P本身的计算)。

An approach that is practical here is to calculate the results by hand for certain small values of k, n and p and check that your functions compute the same results.

这里的实用方法是用手工计算出k、n和p的小值,并检查函数是否计算相同的结果。

If you're using Visual Studio, you can use this MSDN page as a starting point for creating a unit-test project. Note that the functions under test must be visible to the unit-test project, and your unit-test project will need to have a reference added to your existing project where you're doing the computation, as explained in the link.

如果您正在使用Visual Studio,您可以使用这个MSDN页面作为创建单元测试项目的起点。请注意,测试中的函数必须对单元测试项目可见,而您的单元测试项目需要在您的现有项目中添加一个引用,在这个项目中,您正在进行计算,正如链接所解释的那样。

Starting with Factorial, which is the easiest to check, you could add a test like this:

从阶乘开始,这是最容易检查的,您可以添加这样的测试:

[TestClass]
public class UnitTestComputation
{
    [TestMethod]
    public void TestFactorial()
    {
        Assert.AreEqual(1, Program.Factorial(0));
        Assert.AreEqual(1, Program.Factorial(1));
        Assert.AreEqual(2, Program.Factorial(2));
        Assert.AreEqual(6, Program.Factorial(3));
        Assert.AreEqual(24, Program.Factorial(4));
    }
}

The code in your question passes that test.

您的问题中的代码通过了这个测试。

You can then add a test method for your Series function:

然后,您可以为您的系列函数添加一个测试方法:

[TestMethod]
public void TestSeries()
{
    int k = 1;
    int p = 1;
    BigRational expected = 1;
    Assert.AreEqual(expected, Program.Series(k, p));

    k = 2;
    p = 1;
    expected += 1;
    Assert.AreEqual(expected, Program.Series(k, p));

    k = 3;
    p = 1;
    expected += (BigRational)1 / (BigRational)2;
    Assert.AreEqual(expected, Program.Series(k, p));

    k = 1;
    p = 2;
    expected = 1;
    Assert.AreEqual(expected, Program.Series(k, p));

    k = 2;
    p = 2;
    expected += 2;
    Assert.AreEqual(expected, Program.Series(k, p));
}

This showed up some problems in your code:

这在你的代码中出现了一些问题:

  • n shouldn't actually be a parameter to this function, because in this context n isn't the parameter to function P, but actually just the "index-of-summation". n's local value in this function is represented by your i variable.
  • n不应该是这个函数的一个参数,因为在这个上下文中n不是函数P的参数,而是“求和的指数”。n在这个函数中的局部值由i变量表示。
  • This then means that your Factorial(n) call needs to change to Factorial(i)
  • 这意味着您的Factorial(n)调用需要更改为Factorial(i)
  • The loop is also off-by-one, because the Sigma notation for the summation is inclusive of the number at the top of the Sigma, so you should have <= fin (or you could also have written this simply as < k).
  • 这个循环也是不一样的,因为求和的符号是包含在Sigma顶端的数字,所以你应该有<= fin(或者你也可以把它写成< k)。

This is the updated Series function:

这是更新后的系列函数:

// CHANGED: Removed n as parameter (n just the index of summation here)
public static BigRational Series(int k, int p)
{
    BigRational series = new BigRational(0.0);
    var fin = k - 1;
    // CHANGED: Should be <= fin (i.e. <= k-1, or < k) because it's inclusive counting
    for (int i = 0; i <= fin; i++)
    {
        var power = BigRational.Pow(p, i);
        // CHANGED: was Factorial(n), should be factorial of n value in this part of the sequence being summed.
        var factorialN = Factorial(i);
        var sum = BigRational.Divide(power, factorialN);
        series += sum;
    }
    return series;
}

To test the P(n) calculation you can move that out into its own function to test (I've called it ComputeP here):

为了测试P(n)的计算,您可以将其移到它自己的函数中进行测试(我将它称为ComputeP):

[TestMethod]
public void TestP()
{
    int n = 1;
    int k = 2;
    int p = 1;
    // P(0) = 1 / (2 + 1/(2*(1 - 1/2))) = 1/3
    // P(1) = (1/(1/2 * 2)) * P(0) = P(0) = 1/3 
    BigRational expected = 1;
    expected /= 3;
    Assert.AreEqual(expected, Program.ComputeP(k, n, p));

    n = 2;
    k = 2;
    p = 1;
    // P(2) = (1/(1*2)) * P(0) = 1/6
    expected = 1;
    expected /= 6;
    Assert.AreEqual(expected, Program.ComputeP(k, n, p));
}

This showed up a problem with calculating P(n) - you had a cast to double in there which shouldn't have been present (the result is inaccurate then - you need to keep all the intermediate results in BigRational). There's no need for the cast, so just removing it fixes this problem.

这在计算P(n)时出现了一个问题——你有一个在不应该出现的情况下的替身(结果是不准确的——你需要保留所有中间结果的BigRational)。不需要cast,所以只要移除它就可以解决这个问题。

Here is the updated ComputeP function:

这里是更新后的ComputeP函数:

public static BigRational ComputeP(int k, int n, int p)
{
    // the P(n) equation
    BigRational pnNumerator = BigRational.Pow(p, n);
    BigRational pnDenominator = BigRational.Pow(k, (n - k)) * Factorial(k);


    // the P(0) equation

    //---the right side of "+" sign in Denominator
    BigRational pk = BigRational.Pow(p, k);
    BigRational factorialK = Factorial(k);
    // CHANGED: Don't cast to double here (loses precision)
    BigRational lastPart = (BigRational.Subtract(1, BigRational.Divide(p, k)));
    BigRational factorialAndLastPart = BigRational.Multiply(factorialK, lastPart);
    BigRational fullRightSide = BigRational.Divide(pk, factorialAndLastPart);
    //---the left side of "+" sign in Denominator
    BigRational series = Series(k, p);


    BigRational p0Denominator = series + fullRightSide;
    BigRational p0Result = BigRational.Divide(1, p0Denominator);

    BigRational pNResult = BigRational.Divide((pnNumerator * p0Result), pnDenominator);
    return pNResult;
}

For avoidance of confusion, here is the whole updated calculation program:

为避免混淆,这里是整个更新的计算程序:

using System;
using System.Numerics;
using System.Text;
using Numerics;

public class Program
{
    public static BigRational ComputeP(int k, int n, int p)
    {
        // the P(n) equation
        BigRational pnNumerator = BigRational.Pow(p, n);
        BigRational pnDenominator = BigRational.Pow(k, (n - k)) * Factorial(k);


        // the P(0) equation

        //---the right side of "+" sign in Denominator
        BigRational pk = BigRational.Pow(p, k);
        BigRational factorialK = Factorial(k);
        // CHANGED: Don't cast to double here (loses precision)
        BigRational lastPart = (BigRational.Subtract(1, BigRational.Divide(p, k)));
        BigRational factorialAndLastPart = BigRational.Multiply(factorialK, lastPart);
        BigRational fullRightSide = BigRational.Divide(pk, factorialAndLastPart);
        //---the left side of "+" sign in Denominator
        BigRational series = Series(k, p);


        BigRational p0Denominator = series + fullRightSide;
        BigRational p0Result = BigRational.Divide(1, p0Denominator);

        BigRational pNResult = BigRational.Divide((pnNumerator * p0Result), pnDenominator);
        return pNResult;
    }

    // CHANGED: Removed n as parameter (n just the index of summation here)
    public static BigRational Series(int k, int p)
    {
        BigRational series = new BigRational(0.0);
        var fin = k - 1;
        // CHANGED: Should be <= fin (i.e. <= k-1, or < k) because it's inclusive counting
        for (int i = 0; i <= fin; i++)
        {
            var power = BigRational.Pow(p, i);
            // CHANGED: was Factorial(n), should be factorial of n value in this part of the sequence being summed.
            var factorialN = Factorial(i);
            var sum = BigRational.Divide(power, factorialN);
            series += sum;
        }
        return series;
    }

    public static BigRational Factorial(int k)
    {
        if (k <= 1)
            return 1;
        else return BigRational.Multiply(k, Factorial(k - 1));
    }

    static void Main(string[] args)
    {
        var k = 40;
        var n = 235;
        var p = 5;
        var result = ComputeP(k, n, p);
        Console.WriteLine(result.ToDecimalString(1000));
        Console.ReadKey();
    }
}

// From https://*.com/a/10359412/4486839
public static class BigRationalExtensions
{
    public static string ToDecimalString(this BigRational r, int precision)
    {
        var fraction = r.GetFractionPart();

        // Case where the rational number is a whole number
        if (fraction.Numerator == 0 && fraction.Denominator == 1)
        {
            return r.GetWholePart() + ".0";
        }

        var adjustedNumerator = (fraction.Numerator
                                           * BigInteger.Pow(10, precision));
        var decimalPlaces = adjustedNumerator / fraction.Denominator;

        // Case where precision wasn't large enough.
        if (decimalPlaces == 0)
        {
            return "0.0";
        }

        // Give it the capacity for around what we should need for 
        // the whole part and total precision
        // (this is kinda sloppy, but does the trick)
        var sb = new StringBuilder(precision + r.ToString().Length);

        bool noMoreTrailingZeros = false;
        for (int i = precision; i > 0; i--)
        {
            if (!noMoreTrailingZeros)
            {
                if ((decimalPlaces % 10) == 0)
                {
                    decimalPlaces = decimalPlaces / 10;
                    continue;
                }

                noMoreTrailingZeros = true;
            }

            // Add the right most decimal to the string
            sb.Insert(0, decimalPlaces % 10);
            decimalPlaces = decimalPlaces / 10;
        }

        // Insert the whole part and decimal
        sb.Insert(0, ".");
        sb.Insert(0, r.GetWholePart());

        return sb.ToString();
    }
}

And here is the whole unit-test program:

这是整个单元测试程序:

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Numerics;


[TestClass]
public class UnitTestComputation
{
    [TestMethod]
    public void TestFactorial()
    {
        Assert.AreEqual(1, Program.Factorial(0));
        Assert.AreEqual(1, Program.Factorial(1));
        Assert.AreEqual(2, Program.Factorial(2));
        Assert.AreEqual(6, Program.Factorial(3));
        Assert.AreEqual(24, Program.Factorial(4));
    }

    [TestMethod]
    public void TestSeries()
    {
        int k = 1;
        int p = 1;
        BigRational expected = 1;
        Assert.AreEqual(expected, Program.Series(k, p));

        k = 2;
        p = 1;
        expected += 1;
        Assert.AreEqual(expected, Program.Series(k, p));

        k = 3;
        p = 1;
        expected += (BigRational)1 / (BigRational)2;
        Assert.AreEqual(expected, Program.Series(k, p));

        k = 1;
        p = 2;
        expected = 1;
        Assert.AreEqual(expected, Program.Series(k, p));

        k = 2;
        p = 2;
        expected += 2;
        Assert.AreEqual(expected, Program.Series(k, p));
    }

    [TestMethod]
    public void TestP()
    {
        int n = 1;
        int k = 2;
        int p = 1;
        // P(0) = 1 / (2 + 1/(2*(1 - 1/2))) = 1/3
        // P(1) = (1/(1/2 * 2)) * P(0) = P(0) = 1/3 
        BigRational expected = 1;
        expected /= 3;
        Assert.AreEqual(expected, Program.ComputeP(k, n, p));

        n = 2;
        k = 2;
        p = 1;
        // P(2) = (1/(1*2)) * P(0) = 1/6
        expected = 1;
        expected /= 6;
        Assert.AreEqual(expected, Program.ComputeP(k, n, p));
    }
}

Incidentally, the P(n) result with the updated program for your input values for n, p and k is now:

顺便说一下,P(n)的结果是为n, P和k的输入值更新的程序现在:

0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000593109980769066916025972569398424267669807629726200017375290861590898269902277869938365969961320969473356001666906480007119114830921839913623591124192047955091318951831902550404167336054683697071654765071519020060437129398945035521954738463786221029427589397688847246112810536958194364039693387170592425527136243952416704526069736811587380688876091926255908361275575249492845970903676492429684929779402600032481018886875698972533534890841796034626337674846620462046294537488580901129338625628349474358946962065227890599744775562637784553656488649841148591533557896418988044457914999854241038974478576578909626765823565817758792682480009619613438867365912697996527957775248350987801430141776875171808382272960426476953742528769626555642957093028553993908356226007570404005591174451216846471710162760343

0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000593109980769066916025972569398424267669807629726200017375290861590898269902277869938365969961320969473356001666906480007119114830921839913623591124192047955091318951831902550404167336054683697071654765071519020060437129398945035521954738463786221029427589397688847246112810536958194364039693387170592425527136243952416704526069736811587380688876091926255908361275575249492845970903676492429684929779402600032481018886875698972533534890841796034626337674846620462046294537488580901129338625628349474358946962065227890599744775562637784553656488649841148591533557896418988044457914999854241038974478576578909626765823565817758792682480009619613438867365912697996527957775248350987801430141776875171808382272960426476953742528769626555642957093028553993908356226007570404005591174451216846471710162760343


NOTE: You should add to the unit-tests with more results you've checked by hand, and also check any of my working here in interpreting the algebra as code to ensure this is correct.

注意:您应该在单元测试中添加更多您已经手动检查的结果,并检查我在这里的任何工作,将代数解释为代码,以确保这是正确的。