Maybe I didn't understood how to use those two types: BigInteger/BigRational, but generally speaking I want to implement this equations:
也许我不知道如何使用这两种类型: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 contextn
isn't the parameter to functionP
, but actually just the "index-of-summation".n
's local value in this function is represented by youri
variable. - n不应该是这个函数的一个参数,因为在这个上下文中n不是函数P的参数,而是“求和的指数”。n在这个函数中的局部值由i变量表示。
- This then means that your
Factorial(n)
call needs to change toFactorial(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 contextn
isn't the parameter to functionP
, but actually just the "index-of-summation".n
's local value in this function is represented by youri
variable. - n不应该是这个函数的一个参数,因为在这个上下文中n不是函数P的参数,而是“求和的指数”。n在这个函数中的局部值由i变量表示。
- This then means that your
Factorial(n)
call needs to change toFactorial(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.
注意:您应该在单元测试中添加更多您已经手动检查的结果,并检查我在这里的任何工作,将代数解释为代码,以确保这是正确的。