This one was some real work for me.
I went long and pondered how to get the required number of decimals needed. The Decimal-class only held a maximum of 18 decimals and there is no larger type to use. There is no class corresponding to BigInteger for decimals either in .NET or C#. There is a BigDecimals class for J# but I didn’t want to use that as I decided that using .NET libraries for C# is fine but other libraries is not and especially not third-party products.
This had a much simpler solution than I could have thought.
I’ll give an example using 1/7 that my friend used to explain to me:
1/7 = 0.142857… % 10 = 0
10/7 = 1.42857… % 10 = 1
100/7 = 14.2857… % 10 = 4
1000/7 = 142.857… % 10 = 2
Stupidly easy.
So I made a class that gives me one decimal each time I iterate which looks like this:
private static IEnumerable GetDecimalsReversed(int numerator, int denominator, int numberOfDecimals)
{
int exponent = numberOfDecimals;
while (exponent > 0)
{
yield return int.Parse((((BigInteger.Pow(10, --exponent) * 1) / denominator) % (BigInteger)10).ToString());
}
}
and is used like this:
foreach int fractionDecimal in GetDecimalsReversed(1, divideBy, divideBy * 2));
A small note here is that this class gives me the decimals starting from numberOfDecimals and walks toward the decimal point, the reason for this will be explained, I will also explain why I use divideBy * 2 as numberOfDecimals.
Now, the next problem I encountered was how to find the loop. There could be decimals in the start that isn’t a part of the loop. After some searching around I found out that the length of the recurring cycle a/b will always be shorter than b. Now I decided to get rid of the problem with the possible irrelevant decimals directly after the decimal point and instead start from behind, here’s the reason for getting the decimals reversed and also for numberOfDecimals being divideBy * 2.
My friend helped me find the algorithm for the loop. I’ll give a brief explanation (using the way my program calculates):
1/7 = 0.14285714285714
4 != 1
57 != 14
285 != 714
1428 != 5714
57142 != 85714
285714 == 285714
When I find a sequence that matches I know that I have the recurrance cycle. One problem here is that possibly there could be a sequence that ends with the same two numbers (e.g. 0.112233112233) where this will be wrong. This never happened in problem 26 so I decided not to guard against it.
For opmimization I caclulate only prime numbers as per mathematical evidence, those have the longest recurring cycles.
Read Full Post »