Longest repeating cycle of numbers
I am trying to find a number less than 1000 that produces the longest string of repeating numbers when it divides 1. I have a list of decimal numbers and need to find the ones with the longest repeating sequence.
That's what I have so far
numbers = [*2..999]
decimal_representations = numbers.map { |number| 1.to_f/number }
decimal_representations.map!(&:to_s)
I can create a 3D array using regex. Regex /(.+)\1+/
creates an array of duplicate substrings. I want to find the longest substring, so I used an enumerated function max_by
.
decimal_representations.map! { |decimal| decimal.scan(/(.+)\1+/).max_by(&:length) }.flatten
I need to compress my array in order to remove elements nil
decimal_representations.compact!
Then I can find out which length was the longest.
decimal_representations.max_by(&:length)
I get 0090009009
, but can't figure out which number this decimal value has because I removed nil elements from my array.
Any ideas?
source to share
You can do it like this.
code
def longest_repeating_decimal(last)
n = (3..last).max_by { |i| find_repeating(i).size }
end
def find_repeating(n)
num = 1
a = []
remainders = {}
loop do
d,r = num.divmod(n)
return '' if r.zero?
a << d
return a[remainders[r]..-1].join if remainders.key?(r)
remainders[r] = a.size
num = 10*r
end
end
<strong> Examples
n = longest_repeating_decimal(999)
#=> 983
find_repeating(n).size
#=> 982
find_repeating(n)
#=> "00101729399796541200...54323499491353"
require 'bigdecimal'
BigDecimal.new(Rational(1,n),990).to_s('990F')
#=> "0.00101729399796541200...5432349949135300101729..."
# |repeating->
n = longest_repeating_decimal(9_999)
#=> 9967
find_repeating(n).size
#=> 9966
n = longest_repeating_decimal(99_999)
#=> 99989 (took several minutes)
find_repeating(n).size
#=> 99988
Hmmm. An interesting picture.
Here are the numbers between 3
and 30
that repeat the decimal places:
def display(n)
(3..n).each do |i|
repeat = find_repeating(i)
(puts "%2d %9s %23.20f" % [i, repeat, 1.0/i]) unless repeat.empty?
end
end
display(30)
n repeating 1.0/n
3 3 0.33333333333333331483
6 6 0.16666666666666665741
7 142857 0.14285714285714284921
9 1 0.11111111111111110494
11 90 0.09090909090909091161
12 3 0.08333333333333332871
13 769230 0.07692307692307692735
14 714285 0.07142857142857142461
15 6 0.06666666666666666574
17 8 0.05882352941176470507
18 5 0.05555555555555555247
19 52631 0.05263157894736841813
21 476190 0.04761904761904761640
22 45 0.04545454545454545581
23 43 0.04347826086956521618
24 6 0.04166666666666666435
26 384615 0.03846153846153846367
27 370 0.03703703703703703498
28 571428 0.03571428571428571230
29 4 0.03448275862068965469
30 3 0.03333333333333333287
Explanation
When you make a long split and come across a remnant you had earlier, you know that the sequence from the earlier remainder will repeat forever, so you stop there and mark the repeating sequence. This is what it does find_repeating
. If 1.0/n
( n
, which is an argument find_repeating
) it repeats digits, a string of repeating digits is returned. If 1.0/n
finite, an empty string is returned.
Besides
You asked, "I get 009009009, but I can't figure out which number this decimal value has ...". (You had an extra zero in the middle, which I assume was a typo.) Here's how to get the number.
1/n = 0.009009... 10**3 * (1/n) = 9.009009... 10**3 * (1/n) - 1/n = 9 (10**3 - 1)/n = 9 n = (10**3 - 1)/9 = 111
Confirm:
1.0/111 #=> 0.009009...
You will need to use BigDecimal for longer repetition of decimal places.
source to share
Float
cannot accurately represent most decimal numbers, so using (1.to_f/number).to_s
probably won't give you the expected result. This means your whole algorithm is wrong.
You need a different algorithm. Here's a hint: 1.0 / 7
in math, it produces a decimal character 0.142857142857...
, a repeated sequence 142857
. If you use this division, you will notice what 142857
is the divisor 999999
, which is not a coincidence.
Numbers that are multiples of 2
or 5
need extra attention. The trick is that, for example, 14
( 7 * 2
) or 35
( 7 * 5
) have the same number of repeating sequences in their decimal representation 1.0 / n
.
It's a bit difficult to explain this idea without code. I solved this project Euler problem , but hope you can solve it without looking at my source code .
source to share
The corresponding number is 111. I figured it out using the response answer:
1 / 0.009009009009009 = 111
By the way, I am not claiming that your answer is correct, I am simply helping you interpret the answer you received. 1/7 digits have a longer repeating sequence.
Real solution to the problem
I actually solved this problem for real. Here is the code / notes I wrote to solve this problem. If you've just read my comments before looking at my code, you should write the code yourself to see what's going on here. If you want to be screwed up, just go to the very bottom to find out what the answer is.
# First, we need to figure out how digits in a decimal representation
# of a fraction get calculated. We will write a method that prints
# the first 50 decimal digits after the decimal point for the decimal
# representation of a/b, where a and b are integers.
def print_digits_after_decimal(a, b)
raise ArgumentError if !a.is_a?(Integer)
raise ArgumentError if !b.is_a?(Integer)
# We only care about what happening after the decimal point.
r = a % b
print "#{a}/#{b} = ."
50.times do
r *= 10
digit = r / b
print digit
r = r % b
end
puts
end
print_digits_after_decimal(1, 7)
print_digits_after_decimal(11, 28)
# Results:
# 1/7 = .14285714285714285714285714285714285714285714285714
# 11/28 = .39285714285714285714285714285714285714285714285714
# The only thing that changes on each iteration of that loop is r.
# Observe that r is always within the finite range 0..(b-1). So
# eventually r MUST be equal to a value that it already had before.
# We will write a method to find that first repeated r value. This
# represents the state of the digit-generating state machine for the
# first state where the decimal digits start a repeating sequence.
def first_repeated_r(a, b)
raise ArgumentError if !a.is_a?(Integer)
raise ArgumentError if !b.is_a?(Integer)
r = a % b
log = []
while true do
return r if log.include?(r)
log << r
r = (r * 10) % b
end
end
# Now use that r value to generate the digits that repeat. We need to
# call the first_repeated_r method above, and generate digits until we
# see that r value again, and then return those digits as an array.
def repeating_decimal_sequence(a, b)
r_start = r = first_repeated_r(a, b)
digits = []
while true
r *= 10
digits << r / b
r = r % b
break if r == r_start
end
digits
end
# Keep in mind that each r value corresponds to a digit we print out,
# but many different r values can correspond to the same digit. We
# have not proved that the sequence returned by
# repeating_decimal_sequence doesn't contain a repeated sequence
# inside itself. We will write a method that takes an array of digits
# and shortens it to the smallest repeating sequence.
def decimal_sequence_deduplicate(digits)
(1..digits.size).each do |n|
subsequence = digits[0, n]
q, r = digits.size.divmod(n)
if r == 0 && subsequence * q == digits
return subsequence
end
end
raise "Impossible!" # math broke
end
p decimal_sequence_deduplicate([1, 5, 8]) # => [1, 5, 8]
p decimal_sequence_deduplicate([1, 5, 1, 5]) # => [1, 5]
# Now we can put these pieces together to answer your question.
answer = (1...1000).max_by do |n|
decimal_sequence_deduplicate(repeating_decimal_sequence(1, n)).size
end
puts answer # => 983
# And now we should feel kind of dumb because 983 is simply the
# largest prime number less than 1000, and Euler probably knew that
# without doing this much work!
source to share