问题
I'm trying to find the number less than 1000 that produces the longest string of repeated numbers when it divides 1. I have a list of decimal numbers and have to find the ones which have the longest repeated sequence.
Here'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 produce a three dimensional array by using regex. Regex /(.+)\1+/
produces an array of repeated substrings. I want to find the longest substring, so I used enumerable's max_by
function.
decimal_representations.map! { |decimal| decimal.scan(/(.+)\1+/).max_by(&:length) }.flatten
I have to compact my array to remove nil
elements
decimal_representations.compact!
Then I can find out which had the longest length.
decimal_representations.max_by(&:length)
I get 0090009009
, but I can't figure out which number had that decimal value, because I removed nil elements from my array.
Any ideas?
回答1:
You can do that as follows.
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
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. Interesting pattern.
Here are the numbers between 3
and 30
that have repeating decimals:
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 are doing long-division and encounter a remainder you had previously, you know the sequence from the earlier remainder on will repeat forever, so you stop there and mark the repeating sequence. That's precisely what find_repeating
does. If 1.0/n
(n
being find_repeating
's argument) has repeating digits, a string of the repeating digits is returned. If 1.0/n
has a finite value, an empty string is returned.
Aside
You asked: "I get 009009009, but I can't figure out which number had that decimal value,...". (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 have to use BigDecimal for longer repeating decimals.
回答2:
Float
can't represent most decimal numbers precisely, so using (1.to_f/number).to_s
probably won't give you the expected result. This means your whole algorithm is incorrect.
You need a different algorithm. Here's a hint: 1.0 / 7
in math produce a decimal 0.142857142857...
, the repeated sequence is 142857
. If you do this division using, you'll notice that 142857
is a divisor of 999999
, which is not a coincidence.
Numbers that are multiples of 2
or 5
need some extra attention. The trick is that, for example, 14
(7 * 2
) or 35
(7 * 5
), have the same number of repeated sequence in their 1.0 / n
decimal representation.
It's a little difficult to explain this idea without code. I have solved this Project Euler problem, but hope that you can solve it without looking at my source code first.
回答3:
The corresponding number is 111. I calculated it by taking the reciprocal of your answer:
1/0.009009009009009 = 111
By the way, I am not claiming your answer is right, I am just helping you interpret the answer you got. The digits of 1/7 have a longer repeating sequence.
Real solution to the problem
I actually solved this problem for real. Here are the code/notes I wrote to solve it. If you just read my comments before looking at my code, you should be able to write the code yourself in order to discover what is going on here. If you want to be spoiled, just go to the very bottom to see 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's 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!
来源:https://stackoverflow.com/questions/30289341/longest-recurring-cycle-of-digits