【问题标题】:Longest recurring cycle of digits最长循环数字
【发布时间】:2015-05-17 16:37:35
【问题描述】:

我试图找到小于 1000 的数字,当它除以 1 时产生最长的重复数字字符串。我有一个十进制数字列表,必须找到具有最长重复序列的数字。

这是我目前所拥有的

numbers = [*2..999]
decimal_representations = numbers.map { |number| 1.to_f/number }
decimal_representations.map!(&:to_s)

我可以使用正则表达式生成一个三维数组。正则表达式/(.+)\1+/ 生成一个重复子字符串数组。我想找到最长的子串,所以我使用了 enumerable 的max_by 函数。

decimal_representations.map! { |decimal| decimal.scan(/(.+)\1+/).max_by(&:length) }.flatten

我必须压缩我的数组以删除 nil 元素

decimal_representations.compact!

然后我可以找出哪个长度最长。

decimal_representations.max_by(&:length)

我得到了0090009009,但我无法确定哪个数字具有该十进制值,因为我从数组中删除了 nil 元素。

有什么想法吗?

【问题讨论】:

  • decimal_representations
  • 这个问题比表面上看到的要多。有趣的! @sawa 回来了?再来一次?

标签: ruby substring decimal longest-substring


【解决方案1】:

Float 不能精确地表示大多数十进制数字,因此使用(1.to_f/number).to_s 可能不会给您预期的结果。这意味着你的整个算法是不正确的。


您需要不同的算法。这里有一个提示:1.0 / 7 在数学中产生一个小数0.142857142857...,重复的序列是142857。如果你使用这个除法,你会注意到142857999999 的除数,这不是巧合。

25 的倍数需要特别注意。诀窍在于,例如,14 (7 * 2) 或 35 (7 * 5) 在其 1.0 / n 十进制表示中具有相同数量的重复序列。

没有代码来解释这个想法有点困难。我已经解决了this Project Euler problem,但希望你不用先看my source code也能解决。

【讨论】:

  • 我跑了your source code 并且可以确认它给出了正确的答案。 (它与我在答案中得出的结论相符。)
【解决方案2】:

对应的数字是111。我是用你答案的倒数计算出来的:

1/0.009009009009009 = 111

顺便说一句,我并不是说你的答案是正确的,我只是在帮助你解释你得到的答案。 1/7的数字有更长的重复序列。

真正解决问题

我真的解决了这个问题。这是我为解决它而编写的代码/注释。如果您只是在查看我的代码之前阅读了我的 cmets,那么您应该能够自己编写代码以了解这里发生了什么。如果你想被宠坏,就去最底层看看答案是什么。

# 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!

【讨论】:

  • 很好,不敢相信我错过了
  • 我添加了关于如何确定正确答案的完整说明。如果你滚动到我的代码底部,你会看到答案。
  • 不错的答案,大卫。建议你把raise "Should not happen."改成raise "Impossible!"
  • 我想了想,发现你根本不需要使用decimal_sequence_deduplicate。如果两个不同的 r 值产生相同的十进制序列,则意味着每个值的分数 r/b 相同,因此这两个 r 值必须相同。
【解决方案3】:

你可以这样做。

代码

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

示例

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

嗯。有趣的模式。

以下是330 之间的小数重复数字:

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

说明

当您进行长除法并遇到之前的余数时,您知道从较早的余数开始的序列将永远重复,因此您停在那里并标记重复序列。这正是find_repeating 所做的。如果1.0/nnfind_repeating 的参数)有重复数字,则返回重复数字的字符串。如果1.0/n 有一个有限值,则返回一个空字符串。

一边

您问:“我得到 009009009,但我不知道哪个数字具有该十进制值,...”。 (中间有一个额外的零,我认为这是一个错字。)这是获取数字的方法。

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

确认:

1.0/111 #=> 0.009009...    

对于更长的重复小数,您必须使用 BigDecimal

【讨论】:

    猜你喜欢
    • 2017-04-24
    • 2011-05-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-12
    • 1970-01-01
    • 1970-01-01
    • 2015-11-17
    相关资源
    最近更新 更多