Delete the first periodic digit

17

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

Misha Lavrov

Posted 2017-12-17T00:24:25.157

Reputation: 4 846

Can we return 2017 instead of 2017/1? – JungHwan Min – 2017-12-17T01:13:51.377

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).) – Misha Lavrov – 2017-12-17T01:19:56.643

May the input be reducible (not fully simplified)? For example, can 2/4 happen in the input? – user202729 – 2017-12-17T07:25:23.617

1If input is 120/11 is the correct answer 111/11 or 210/11? – kasperd – 2017-12-17T13:04:29.277

@user202729 You may assume that the input is a fully simplified rational number. – Misha Lavrov – 2017-12-17T15:11:03.383

2@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. – Misha Lavrov – 2017-12-17T15:14:58.580

@MishaLavrov alternatively, you could specify that all inputs will be between 0 and 1. (It should only affect the 2017/1 case) – JungHwan Min – 2017-12-17T18:39:43.270

@JungHwanMin There is an infinite number of rational numbers between 0 and 1 for which the question would also apply. I think the simplest example would be 10/11. – kasperd – 2017-12-17T21:57:40.543

@kasperd The zero (ones place) in 0.(90) isn't significant, so including it in the repeating sequence wouldn't really make sense. – JungHwan Min – 2017-12-18T00:32:41.403

Answers

13

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.

JungHwan Min

Posted 2017-12-17T00:24:25.157

Reputation: 13 290

Very clever indeed! – DavidC – 2017-12-17T01:47:27.850

6

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

user202729

Posted 2017-12-17T00:24:25.157

Reputation: 14 620

30 bytes – Erik the Outgolfer – 2017-12-17T09:22:47.940

@EriktheOutgolfer Thanks! – user202729 – 2017-12-17T09:29:07.277

5

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))

Esolanging Fruit

Posted 2017-12-17T00:24:25.157

Reputation: 13 542

5

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!

Leaky Nun

Posted 2017-12-17T00:24:25.157

Reputation: 45 011

@Mr.Xcoder edited – Leaky Nun – 2017-12-17T08:56:47.163

2

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.

user202729

Posted 2017-12-17T00:24:25.157

Reputation: 14 620

1

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.

Brad Gilbert b2gills

Posted 2017-12-17T00:24:25.157

Reputation: 12 713