17
\$\begingroup\$

We all know that whenever a rational number is written in decimal, the result is either terminating or (eventually) periodic. For example, when 41/42 is written in decimal, the result is

0.9 761904 761904 761904 761904 761904 761904 761904 ...

with an initial sequence of digits 0.9 followed by the sequence 761904 repeated over and over again. (A convenient notation for this is 0.9(761904) where the parentheses surround the block of repeating digits.)

Your goal in this challenge is to take a positive rational number, delete the first digit that's part of the repeating sequence, and return the resulting rational number. For example, if we do this to 41/42, we get

0.9  61904 761904 761904 761904 761904 761904 761904 ...

or 0.9(619047) for short, which is 101/105.

If the rational number has a terminating decimal expansion, like 1/4 = 0.25 does, nothing should happen. You can think of 1/4 either as 0.250000000... or as 0.249999999... but in either case, deleting the first digit of the repeating part leaves the number unchanged.

Details

  • The input is a positive rational number, either as a pair of positive integers representing the numerator and denominator, or (if your language of choice allows it and you want to) as some sort of rational-number object.
  • The output is also a rational number, also in either form. If the result is an integer, you may return the integer instead of a rational number.
  • If taking a pair of numbers as input, you may assume they're relatively prime; if producing a pair of numbers as output, you must make them be relatively prime.
  • Be careful that you find the first digit that starts a repeating block. For example, one could write 41/42 as 0.97(619047) but that doesn't make 2041/2100 (with the decimal expansion 0.97(190476)) a valid answer.
  • You may assume that in the input you get, the first periodic digit is after the decimal point, making 120/11 = 10.909090909... invalid input: (its first periodic digit could be considered the 0 in 10). You may do anything you like on such input.
  • This is : the shortest solution wins.

Test cases

41/42 => 101/105
101/105 => 193/210
193/210 => 104/105
104/105 => 19/21
1/3 => 1/3
1/4 => 1/4
2017/1 => 2017/1
1/7 => 3/7
1/26 => 11/130
1234/9999 => 2341/9999
\$\endgroup\$
  • \$\begingroup\$ Can we return 2017 instead of 2017/1? \$\endgroup\$ – JungHwan Min Dec 17 '17 at 1:13
  • \$\begingroup\$ Yes, if you're doing the rational number thing. (If you're doing the pair-of-integers thing, then I'm not sure what else you'd return except the pair (2017,1).) \$\endgroup\$ – Misha Lavrov Dec 17 '17 at 1:19
  • \$\begingroup\$ May the input be reducible (not fully simplified)? For example, can 2/4 happen in the input? \$\endgroup\$ – user202729 Dec 17 '17 at 7:25
  • 1
    \$\begingroup\$ If input is 120/11 is the correct answer 111/11 or 210/11? \$\endgroup\$ – kasperd Dec 17 '17 at 13:04
  • 2
    \$\begingroup\$ @kasperd Huh, that's a case I hadn't thought of... I'd want to say 111/11 except that the most highly upvoted answer at the moment returns 210/11, so I will let you pick to avoid invalidating existing answers. \$\endgroup\$ – Misha Lavrov Dec 17 '17 at 15:14
13
\$\begingroup\$

Wolfram Language (Mathematica), 59 bytes

FromDigits@MapAt[RotateLeft@*List@@#&,RealDigits@#,{1,-1}]&

Try it online!

Explanation

RealDigits@#

Find the decimal digits of the input.

MapAt[RotateLeft@*List@@#&, ..., {1,-1}]

If there are repeating digits, RotateLeft them. (List@@# prevents the code from attempting to rotate the last decimal digit if the rational number is terminating).

FromDigits@

Convert to rational.

\$\endgroup\$
  • \$\begingroup\$ Very clever indeed! \$\endgroup\$ – DavidC Dec 17 '17 at 1:47
6
\$\begingroup\$

Jelly, 36 32 31 30 bytes

-1 byte thanks to Erik the Outgolfer!

ọ2,5Ṁ⁵*©×Ɠ÷µ×⁵_Ḟ$+Ḟ,®×³+.Ḟ÷g/$

Try it online!

Should be correct. Floating point imprecision add 3 bytes for +.Ḟ.

Relies on the input being irreducible.


Explanation

This relies on:

  • Let the numerator be n/d in its simplest form. Then the link ọ2,5Ṁ applied to d will give the number of non-periodic digit after the radix point.

ọ2,5Ṁ⁵*©×Ɠ÷µ×⁵_Ḟ$+Ḟ,®×³+.Ḟ÷g/$     Main link (monad take d as input)

    Ṁ                              Maximum
ọ                                  order of
 2,5                               2 or 5 on d
     ⁵*                            10 power
       ©                           Store value to register ®.
        ×Ɠ                         Multiply by eval(input()) (n)
          ÷                        Divide by first argument (d).
                                   Now the current value is n÷d×®.
           µ                       With that value,
            ×⁵                     Multiply by ⁵ = 10
              _Ḟ$                  subtract floor of self
                 +Ḟ                add floor or value (above)
                                   Given 123.45678, will get 123.5678
                                   (this remove first digit after `.`)
                   ,®              Pair with ®.
                     ׳            Scale
                       +.Ḟ         Round to integer
                          ÷g/$     Simplify fraction
\$\endgroup\$
5
\$\begingroup\$

Python 2, 237 235 214 bytes

-21 bytes thanks to Mr. Xcoder

from fractions import*
F=Fraction
n,d=input()
i=n/d
n%=d
R=[]
D=[]
while~-(n in R):R+=n,;n*=10;g=n/d;n%=d;D+=g,
x=R.index(n)
r=D[x+1:]+[D[x]]
print i+F(`r`[1::3])/F('9'*len(r))/10**x+F("0."+"".join(map(str,D[:x])))

Try it online!

Input is done as a tuple (numerator, denominator); output is a fractions.Fraction object.

This uses a long-division-style method to get the starting and repeating digits of the answer, then moves the first repeating digit to the end and uses string manipulation and fraction.Fraction to convert it back to a ratio.

Ungolfed version:

import fractions

num, denom = input()
integer_part, num = divmod(num, denom)

remainders = []
digits = []
current_remainder = num
while current_remainder not in remainders:
    remainders.append(current_remainder)
    current_remainder *= 10
    digit, current_remainder = divmod(current_remainder, denom)
    digits.append(digit)

remainder_index = remainders.index(current_remainder)
start_digits = digits[:remainder_index]
repeated_digits = digits[remainder_index:]

repeated_digits.append(repeated_digits.pop(0))

start_digits_str = "".join(map(str, start_digits))
repeated_digits_str = "".join(map(str, repeated_digits))

print(integer_part+int(repeated_digits_str)/fractions.Fraction('9'*(len(repeated_digits_str)))/10**len(start_digits_str)+fractions.Fraction("0."+start_digits_str))
\$\endgroup\$
5
\$\begingroup\$

Python 3, 177 173 169 bytes

from fractions import*
def f(n,d):
 i=1;r=[]
 while~-(i%d in r):r+=[i%d];i*=10
 r=10**r.index(i%d);u=i-r;v=i//r-1;t=u//d*n
 return Fraction(t-t%v+t%v*10//v+t%v*10%-~v,u)

Try it online!

\$\endgroup\$
  • \$\begingroup\$ @Mr.Xcoder edited \$\endgroup\$ – Leaky Nun Dec 17 '17 at 8:56
2
\$\begingroup\$

Wolfram Language (Mathematica), 70 67 bytes

Thanks to this tip (now deleted) for -3 byte!

(x=10^Max@IntegerExponent[#,{2,5}];(Floor@#+Mod[10#,1])/x&[x#2/#])&

Try it online!

A port of my Jelly answer. Longer than the existing Mathematica answer by 8 bytes...

The function take 2 input [denominator, numerator], such that GCD[denominator, numerator] == 1.

\$\endgroup\$
1
\$\begingroup\$

Perl 6, 102 bytes

{$/=.base-repeating;(+$0//$0~0)+([~]([$1.comb].rotate)/(9 x$1.chars)*.1**(($0~~/\.<(.*/).chars)if $1)}

Try it

Takes a Rational number and returns a Rational or Int number.

Expanded:

{  # bare block lambda with implicit Rational parameter 「$_」

  $/ = .base-repeating; # store in 「$/」 the two strings '0.9' '761904'

    # handle the non-repeating part
    (
      +$0        # turn into a number
      // $0 ~ 0  # if that fails append 0 (handle cases like '0.')
    )

  +

    # handle the repeating part
    (
          [~]( [$1.comb].rotate ) # rotate the repeating part
        /
          ( 9 x $1.chars )        # use a divisor that will result in a repeating number

        *

         # offset it an appropriate amount

         .1 ** (
           ( $0 ~~ / \. <( .* / ).chars # count the characters after '.'
         )

      if $1  # only do the repeating part if there was a repeating part
    )
}

Note will handle denominators upto uint64.Range.max to handle larger denominators use FatRat(9 x$1.chars) Try it.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.