Skip to content

Commit 845347f

Browse files
authored
Merge pull request aalhour#75 from nickntg/master
Proposed fixes for catalan numbers
2 parents 32e8336 + ba80fa8 commit 845347f

File tree

3 files changed

+97
-46
lines changed

3 files changed

+97
-46
lines changed
Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,33 @@
1-
using System;
1+
using System.Collections.Generic;
2+
using System.Numerics;
23

34
namespace Algorithms.Numeric
45
{
56
public static class BinomialCoefficients
67
{
8+
private static readonly Dictionary<uint, BigInteger> Cache = new Dictionary<uint, BigInteger>();
9+
710
/// <summary>
811
/// Calculate binomial coefficients, C(n, k).
912
/// </summary>
10-
public static ulong Calculate(uint n, uint k)
13+
/// <param name="n"></param>
14+
/// <returns></returns>
15+
public static BigInteger Calculate(uint n)
1116
{
12-
ulong result = 1;
13-
14-
// Since C(n, k) = C(n, n-k)
15-
if (k > n - k)
16-
k = n - k;
17+
return Factorial(2 * n) / (Factorial(n) * Factorial(n + 1));
18+
}
1719

18-
// Calculate value of [n*(n-1)*---*(n-k+1)] / [k*(k-1)*---*1]
19-
for (int i = 0; i < k; ++i)
20+
private static BigInteger Factorial(uint n)
21+
{
22+
if (n <= 1)
23+
return 1;
24+
if (Cache.ContainsKey(n))
2025
{
21-
result *= Convert.ToUInt64(n - i);
22-
result /= Convert.ToUInt64(i + 1);
26+
return Cache[n];
2327
}
24-
25-
return result;
28+
var value = n * Factorial(n - 1);
29+
Cache[n] = value;
30+
return value;
2631
}
27-
2832
}
29-
30-
}
33+
}

Algorithms/Numeric/CatalanNumbers.cs

Lines changed: 32 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
* Wikipedia: https://en.wikipedia.org/wiki/Catalan_number
55
*/
66

7-
using System;
87
using System.Diagnostics;
98
using System.Collections.Generic;
9+
using System.Numerics;
1010

1111
namespace Algorithms.Numeric
1212
{
@@ -16,26 +16,28 @@ public static class CatalanNumbers
1616
/// Internal cache.
1717
/// By default contains the first two catalan numbers for the ranks: 0, and 1.
1818
/// </summary>
19-
private static Dictionary<uint, ulong> _catalanNumbers = new Dictionary<uint, ulong>() { { 0, 1 }, { 1, 1 } };
19+
private static readonly Dictionary<uint, BigInteger> CachedCatalanNumbers = new Dictionary<uint, BigInteger> { { 0, 1 }, { 1, 1 } };
2020

2121
/// <summary>
22-
/// Helper function.
22+
/// Helper method.
2323
/// </summary>
24-
private static ulong _recursiveHelper(uint rank)
24+
/// <param name="rank"></param>
25+
/// <returns></returns>
26+
private static BigInteger _recursiveHelper(uint rank)
2527
{
26-
if (_catalanNumbers.ContainsKey(rank))
27-
return _catalanNumbers[rank];
28+
if (CachedCatalanNumbers.ContainsKey(rank))
29+
return CachedCatalanNumbers[rank];
2830

29-
ulong number = 0;
30-
uint lastRank = rank - 1;
31+
BigInteger number = 0;
32+
var lastRank = rank - 1;
3133

3234
for (uint i = 0; i <= lastRank; ++i)
3335
{
34-
ulong firstPart = _recursiveHelper(i);
35-
ulong secondPart = _recursiveHelper(lastRank - i);
36+
var firstPart = _recursiveHelper(i);
37+
var secondPart = _recursiveHelper(lastRank - i);
3638

37-
if (!_catalanNumbers.ContainsKey(i)) _catalanNumbers.Add(i, firstPart);
38-
if (!_catalanNumbers.ContainsKey(lastRank - i)) _catalanNumbers.Add(lastRank - i, secondPart);
39+
if (!CachedCatalanNumbers.ContainsKey(i)) CachedCatalanNumbers.Add(i, firstPart);
40+
if (!CachedCatalanNumbers.ContainsKey(lastRank - i)) CachedCatalanNumbers.Add(lastRank - i, secondPart);
3941

4042
number = number + (firstPart * secondPart);
4143
}
@@ -44,44 +46,46 @@ private static ulong _recursiveHelper(uint rank)
4446
}
4547

4648
/// <summary>
47-
/// Public API
49+
/// Public API.
4850
/// </summary>
49-
public static ulong GetNumber(uint rank)
51+
/// <param name="rank"></param>
52+
/// <returns></returns>
53+
public static BigInteger GetNumber(uint rank)
5054
{
5155
// Assert the cache is not empty.
52-
Debug.Assert(_catalanNumbers.Count >= 2);
56+
Debug.Assert(CachedCatalanNumbers.Count >= 2);
5357

5458
return _recursiveHelper(rank);
5559
}
5660

5761
/// <summary>
5862
/// Calculate the number using the Binomial Coefficients algorithm
5963
/// </summary>
60-
public static ulong GetNumberByBinomialCoefficients(uint rank)
64+
/// <param name="rank"></param>
65+
/// <returns></returns>
66+
public static BigInteger GetNumberByBinomialCoefficients(uint rank)
6167
{
62-
// Calculate value of 2nCn
63-
var catalanNumber = BinomialCoefficients.Calculate(2 * rank, rank);
64-
65-
// return 2nCn/(n+1)
66-
return Convert.ToUInt64(catalanNumber / (rank + 1));
68+
// Calculate by binomial coefficient.
69+
return BinomialCoefficients.Calculate(rank);
6770
}
6871

6972
/// <summary>
70-
/// Return the list of catalan numbers between two ranks, inclusive.
73+
/// Return the list of catalan numbers between two ranks, inclusive
7174
/// </summary>
72-
public static List<ulong> GetRange(uint fromRank, uint toRank)
75+
/// <param name="fromRank"></param>
76+
/// <param name="toRank"></param>
77+
/// <returns></returns>
78+
public static List<BigInteger> GetRange(uint fromRank, uint toRank)
7379
{
74-
List<ulong> numbers = new List<ulong>();
80+
var numbers = new List<BigInteger>();
7581

7682
if (fromRank > toRank)
7783
return null;
7884

79-
for (uint i = fromRank; i <= toRank; ++i)
85+
for (var i = fromRank; i <= toRank; ++i)
8086
numbers.Add(GetNumber(i));
8187

8288
return numbers;
8389
}
84-
8590
}
86-
87-
}
91+
}

UnitTest/AlgorithmsTests/CatalanNumbersTest.cs

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Collections.Generic;
2+
using System.Numerics;
23
using Algorithms.Numeric;
34
using Xunit;
45

@@ -7,10 +8,10 @@ namespace UnitTest.AlgorithmsTests
78
public class CatalanNumbersTest
89
{
910
[Fact]
10-
public static void DoTest()
11+
public void DoTest()
1112
{
1213
var list = CatalanNumbers.GetRange(0, 100);
13-
var list2 = new List<ulong>();
14+
var list2 = new List<BigInteger>();
1415

1516
// TRY CALCULATING FROM Bin.Coeff.
1617
for (uint i = 0; i < list.Count; ++i)
@@ -21,5 +22,48 @@ public static void DoTest()
2122
Assert.True(list[(int)i] == list2[(int)i], "Wrong calculation.");
2223
}
2324
}
25+
26+
// Values retrieved from https://oeis.org/A000108/list.
27+
[Theory]
28+
[InlineData(0, "1")]
29+
[InlineData(1, "1")]
30+
[InlineData(2, "2")]
31+
[InlineData(3, "5")]
32+
[InlineData(4, "14")]
33+
[InlineData(5, "42")]
34+
[InlineData(6, "132")]
35+
[InlineData(7, "429")]
36+
[InlineData(8, "1430")]
37+
[InlineData(9, "4862")]
38+
[InlineData(10, "16796")]
39+
[InlineData(11, "58786")]
40+
[InlineData(12, "208012")]
41+
[InlineData(13, "742900")]
42+
[InlineData(14, "2674440")]
43+
[InlineData(15, "9694845")]
44+
[InlineData(16, "35357670")]
45+
[InlineData(17, "129644790")]
46+
[InlineData(18, "477638700")]
47+
[InlineData(19, "1767263190")]
48+
[InlineData(20, "6564120420")]
49+
[InlineData(21, "24466267020")]
50+
[InlineData(22, "91482563640")]
51+
[InlineData(23, "343059613650")]
52+
[InlineData(24, "1289904147324")]
53+
[InlineData(25, "4861946401452")]
54+
[InlineData(26, "18367353072152")]
55+
[InlineData(27, "69533550916004")]
56+
[InlineData(28, "263747951750360")]
57+
[InlineData(29, "1002242216651368")]
58+
[InlineData(30, "3814986502092304")]
59+
public void ManuallyVerifyCatalanNumber(uint rank, string value)
60+
{
61+
// This conversion seems to be necessary because as of this
62+
// writing xunit doesn't behave well with BigInteger inline
63+
// data values.
64+
var bigint = BigInteger.Parse(value);
65+
66+
Assert.True(CatalanNumbers.GetNumber(rank) == bigint);
67+
}
2468
}
2569
}

0 commit comments

Comments
 (0)