I already know when a fraction is repeating decimals. Here is the function.
public bool IsRepeatingDecimal
{
get
{
if (Numerator % Denominator == 0)
return false;
var primes = MathAlgorithms.Primes(Denominator);
foreach (int n in primes)
{
if (n != 2 && n != 5)
return true;
}
return false;
}
}
Now, I'm trying to get the repeated number. I'm checking this web site: http://en.wikipedia.org/wiki/Repeating_decimal
public decimal RepeatingDecimal()
{
if (!IsRepeatingDecimal) throw new InvalidOperationException("The fraction is not producing repeating decimals");
int digitsToTake;
switch (Denominator)
{
case 3:
case 9: digitsToTake = 1; break;
case 11: digitsToTake = 2; break;
case 13: digitsToTake = 6; break;
default: digitsToTake = Denominator - 1; break;
}
return MathExtensions.TruncateAt((decimal)Numerator / Denominator, digitsToTake);
}
But I really realized, that some numbers has a partial decimal finite and later infinite. For example: 1/28
Do you know a better way to do this? Or an Algorithm?
A very simple algorithm is this: implement long division. Record every intermediate division you do. As soon as you see a division identical to the one you've done before, you have what's being repeated.
Example: 7/13.
The algorithm gives us 538461 as the repeating part. My calculator says 7/13 is 0.538461538. Looks right to me! All that remains are implementation details, or to find a better algorithm!
One way would be to repeat the way that you do long division by hand, and keep note of the remainder at each stage. When the remainder repeats, the rest of the process must repeat as well. E.g. the digits of 1.0/7 are 0.1 remainder 3 then 0.14 remainder 2 then 0.142 remainder 6 then 0.1428 remainder 4 then 0.14285 remainder 5 then 0.142857 remainder 1 which is the 1 that starts it off again amd so you get 0.1428571 remainder 3 and it repeats again from there.
If you have a (positive) reduced fraction
numerator / denominator
, the decimal expansion of the fraction terminates if and only ifdenominator
has no prime factor other than 2 or 5. If it has any other prime factor, the decimal expansion will be periodic. However, the cases where the denominator is divisible by at least one of 2 and 5 and where it isn't give rise to slightly different behaviour. We have three cases:denominator = 2^a * 5^b
, then the decimal expansion terminatesmax {a, b}
digits after the decimal point.denominator = 2^a * 5^b * m
wherem > 1
is not divisible by 2 or by 5, then the fractional part of the decimal expansions consists of two parts, the pre-period of lengthmax {a, b}
and the period, whose length is determined bym
and independent of the numerator.denominator > 1
is not divisible by 2 or by 5, then the decimal expansion is purely periodic, meaning the period starts immediately after the decimal point.The treatment of cases 1. and 2. has a common part, let
c = max {a, b}
, thenwhere
m = 1
for case 1. Note that one of the factors2^(c-a)
and5^(c-b)
with which we multiply the numerator is 1. Then you get the decimal expansion by expandingand shifting the decimal point
c
places to the left. In the first case (m = 1
) that part is trivial.The treatment of cases 2. and 3. also has a common part, the calculation of a fraction
where
n
andm
have no common prime factor (andm > 1
). We can writen = q*m + r
with0 <= r < m
(division with remainder,r = n % m
), q is the integral part of the fraction and rather uninteresting.Since the fraction was assumed reduced, we have
r > 0
, so we want to find the expansion of a fractionr / m
where0 < r < m
andm
is not divisible by 2 or by 5. As mentioned above, such an expansion is purely periodic, so finding the period means finding the complete expansion.Let's go about finding the period heuristically. So let
k
be the length of the (shortest) period andp = d_1d1_2...d_k
the period. SoThe last term is a geometric series,
1 + q + q^2 + q^3 + ...
which, for|q| < 1
has the sum1/(1-q)
. In our case,0 < q = 1/(10^k) < 1
, so the sum is1 / (1 - 1/(10^k)) = 10^k / (10^k-1)
. Thus we have seen thatSince
r
andm
have no common factor, that means there is ans
with10^k - 1 = s*m
andp = s*r
. If we knowk
, the length of the period, we can simply find the digits of the period by calculatingand padding with leading zeros until we have
k
digits. (Note: it is that simple only ifk
is sufficiently small or a big integer type is available. To calculate the period of for example 17/983 with standard fixed-width integer types, use long division as explained by @Patrick87.)So it remains to find the length of the period. We can revert the reasoning above and find that if
m
divides10^u - 1
, then we can writeand
r/m
has a period of lengthu
. So the length of the shortest period is the minimal positiveu
such thatm
divides10^u - 1
, or, put another way, the smallest positiveu
such that10^u % m == 1
.We can find it in O(m) time with
Now, finding the length of the period that way is not more efficient than finding the digits and length of the period together with long division, and for small enough
m
that is the most efficient method.That works as long as
10*(denominator - 1)
doesn't overflow, of courseint
could be a 32-bit or 64-bit integer as needed.But for large denominators, that is inefficient, one can find the period length and also the period faster by considering the prime factorisation of the denominator. Regarding the period length,
m = p^k
, the period length ofr/m
is a divisor of(p-1) * p^(k-1)
a
andb
are coprime andm = a * b
, the period length ofr/m
is the least common multiple of the period lengths of1/a
and1/b
.Taken together, the period length of
r/m
is a divisor ofλ(m)
, whereλ
is the Carmichael function.So to find the period length of
r/m
, find the prime factorisation ofm
and for all prime power factorsp^k
, find the period of1/(p^k)
- equivalently, the multiplicative order of 10 modulop^k
, which is known to be a divisor of(p-1) * p^(k-1)
. Since such numbers haven't many divisors, that is quickly done. Then find the least common multiple of all these.For the period itself (the digits), if a big integer type is available and the period isn't too long, the formula
is a quick way to compute it. If the period is too long or no big integer type is available, efficiently computing the digits is messier, and off the top of my head I don't remember how exactly that is done.
The long division algorithm is pretty good, so I have nothing to add there.
But note that your algorithm IsRepeatingDecimal may not work and is inneficient.
It will not work if your fraction is not irreductible, that is if there exists an integer larger than 1 that divides both your numerator and your denominator. For example, if you feed 7/14 then your algorithm will return true when it should return false.
To reduce your fraction, find the gcd between both numerator and denominator and divide both by this gcd.
If you assume that the fraction is irreducible, then your test
can simply be replaced with
But that is still unnecessary since if Denominator is 1, then your list 'primes' is going to be empty and your algorithm will return false anyway.
Finally, calling MathAlgorithms.Primes(Denominator) is going to be expensive for large numbers and can be avoided. Indeed, all you need to do is divide your denominator by 5 (respectively 2) untill it is no longer divisible by 5 (resp. 2). If the end result is 1, then return false, otherwise return true.