ruby - 质数和

标签 ruby math primes

因此,我正在 HackerRank 上进行其中一项编程挑战,以帮助培养我的技能。 (不,这不是为了面试!我的问题是质数和。(完整描述:https://www.hackerrank.com/challenges/prime-digit-sums/problem)基本上给定一个值 n,我要找到所有满足 n 位长的数字以下三个标准:

  • 每 3 位连续数字相加为素数
  • 每 4 个连续数字相加为素数
  • 每 5 个连续数字相加为素数

  • 详分割类见链接...

    我有一个可以工作的基本功能,问题是当 n变得足够大它会破裂:
    #!/bin/ruby
    require 'prime'
    
    def isChloePrime?(num)
        num = num.to_s
        num.chars.each_cons(5) do |set|
            return false unless Prime.prime?(set.inject(0) {|sum, i| sum + i.to_i})
        end
        num.chars.each_cons(4) do |set|
            return false unless Prime.prime?(set.inject(0) {|sum, i| sum + i.to_i})
        end
        num.chars.each_cons(3) do |set|
            return false unless Prime.prime?(set.inject(0) {|sum, i| sum + i.to_i})
        end
        return true
    end
    
    def primeDigitSums(n)
        total = 0
        (10**(n-1)..(10**n-1)).each do |i| 
           total += 1 if isChloePrime?(i)
        end
        return total
    end
    
    puts primeDigitSums(6) # prints 95 as expected
    puts primeDigitSums(177779) # runtime error
    

    如果有人能指出我正确的方向,那就太棒了。不一定要寻找“这就是答案”。理想情况下会喜欢“尝试使用此功能......”。

    更新 这是第 2 版:
    #!/bin/ruby
    require 'prime'
    
    @primes = {}
    
    def isChloePrime?(num)
      num = num.to_s
      (0..num.length-5).each do |i|
        return false unless @primes[num[i,5]]
      end
      return true
    end
    
    def primeDigitSums(n)
      total = 0
      (10**(n-1)...(10**n)).each do |i|
        total += 1 if isChloePrime?(i)
      end
      return total
    end
    
    (0..99999).each do |val|
        @primes[val.to_s.rjust(5, "0")] = true if [3,4,5].all? { |n| val.digits.each_cons(n).all? { |set| Prime.prime? set.sum } }
    end
    

    最佳答案

    如果每个非负整数的 3、4 和 5 个数字序列之和形成素数,我认为每个非负整数都是有效的。

    构造相关质数集

    我们将需要确定 3 位、4 位和 5 位数字的数字之和是否为质数。因此,最大的数字不会大于 5 * 9 .构造一组这些素数(一个集合而不是一个数组来加速查找)是很方便的。

    require 'prime'
    require 'set'
    
    primes = Prime.each(5*9).to_set
      #=> #<Set: {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43}>
    

    构造转换哈希
    valid1是一个哈希,其键都是 1 位数字(所有这些都是有效的)。键值0是所有 1 位数字的数组。对于 1-9这些值是通过将数字附加到键获得的 2 位数字数组(所有这些都是有效的)。总的来说,这些值包括所有 2 位数字。
    valid1 = (0..9).each_with_object({}) { |v1,h|
      h[v1] = 10.times.map { |i| 10 * v1 + i } }
    
    valid2是将 2 位数字(全部有效)映射到有效 3 位数字数组的散列,该数组是通过将数字附加到 2 位数字而获得的。总的来说,这些值包括所有有效的 3 位数字。所有值都是非空数组。
    valid2 = (10..99).each_with_object({}) do |v2,h|
      p = 10 * v2
      b, a = v2.digits
      h[v2] = (0..9).each_with_object([]) { |c,arr|
        arr << (p+c) if primes.include?(a+b+c) }
    end
    

    请注意 Integer#digits返回一个以 1 的数字开头的数组。
    valid3是将有效的 3 位数字映射到有效 4 位数字数组的哈希,这些数字是通过将数字附加到 key 而获得的。总的来说,这些值包括所有有效的 4 位数字。 303 个值中有 152 个是空数组。
    valid3 = valid2.values.flatten.each_with_object({}) do |v3,h|
      p = 10 * v3
      c, b, a = v3.digits
      h[v3] = (0..9).each_with_object([]) do |d,arr|
        t = b+c+d
        arr << (p+d) if primes.include?(t) && primes.include?(t+a)
      end
    end
    
    valid4是将有效的 4 位数字映射到有效的 4 位数字数组的哈希,这些数字是通过将数字附加到 key 并删除 key 的第一个数字而获得的。 valid5.values.flatten.size #=> 218是有效的 5 位数字的数量。 280 个值中有 142 个是空数组。
    valid4 = valid3.values.flatten.each_with_object({}) do |v4,h|
      p = 10 * v4
      d, c, b, a = v4.digits
      h[v4] = (0..9).each_with_object([]) do |e,arr|
        t = c+d+e
        arr << ((p+e) % 10_000) if primes.include?(t) &&
          primes.include?(t += b) && primes.include?(t + a)
      end
    end
    

    我们合并这四个散列以形成一个散列 @transition .不再需要以前的哈希值。 @transition有 294 个键。
    @transition = [valid1, valid2, valid3, valid4].reduce(:merge)
      #=> {0=>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
      #    1=>[10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
      #    ...
      #    9=>[90, 91, 92, 93, 94, 95, 96, 97, 98, 99],
      #    10=>[101, 102, 104, 106], 11=>[110, 111, 113, 115, 119], 
      #    ...
      #    97=>[971, 973, 977], 98=>[980, 982, 986], 99=>[991, 995],
      #    101=>[1011], 102=>[1020], 104=>[], 106=>[], 110=>[1101], 
      #    ...
      #    902=>[9020], 904=>[], 908=>[], 911=>[9110], 913=>[], 917=>[],
      #    1011=>[110], 1020=>[200], 1101=>[], 1110=>[], 1200=>[],
      #    ...
      #    8968=>[], 9020=>[200], 9110=>[], 9200=>[]}
    

    过渡方法

    这是将用于更新 counts 的方法每次n ,数字的数量,增加一。
    def next_counts(counts)
      counts.each_with_object({}) do |(k,v),new_valid|
        @transition[k].each do |new_v|
          (new_valid[new_v] = new_valid[new_v].to_i + v) if @transition.key?(k)
        end
      end
    end
    

    prime_digit_sum方法
    def prime_digit_sum(n)
      case n
      when 1 then 10
      when 2 then 90
      when 3 then @transition.sum { |k,v| (10..99).cover?(k) ? v.size : 0 }
      else
        counts = @transition.select { |k,_| (100..999).cover?(k) }.
                             values.flatten.product([1]).to_h   
        (n - 4).times { counts = next_counts(counts) }
        counts.values.sum % (10**9 + 7)
      end
    end
    

    请注意,对于 n = 4哈希counts键是有效的 4 位数字和值都等于 1 :
    counts = @transition.select { |k,_| (100..999).cover?(k) }.
      values.flatten.product([1]).to_h   
      #=> {1011=>1, 1020=>1, 1101=>1, 1110=>1, 1200=>1, 2003=>1, 2005=>1,
      #    ...
      #    8902=>1, 8920=>1, 8968=>1, 9020=>1, 9110=>1, 9200=>1}
    
    counts.size
      #=> 280
    

    如图所示,对于 n >= 5 , counts每次更新n加一。值的总和等于有效数量 n-digit数字。

    由每个有效 n 的最后四位数字组成的数字-digit numbers 是 count 之一的 key 。每个键的值是一个数字数组,包含所有有效 (n+1) 的最后四位数字。 - 通过在 key 上附加一个数字而产生的数字。

    例如,考虑 counts 的值为 n = 6 ,发现如下。
    counts
      #=> {1101=>1, 2003=>4, 2005=>4, 300=>1, 302=>1, 304=>1, 308=>1, 320=>1,
      #    322=>1, 326=>1, 328=>1, 380=>1, 382=>1, 386=>1, 388=>1, 500=>1,
      #    502=>1, 506=>1, 508=>1, 560=>1, 562=>1, 566=>1, 568=>1, 1200=>7,
      #    3002=>9, 3020=>4, 3200=>6, 5002=>6, 9200=>4, 200=>9, 1020=>3, 20=>3,
      #    5200=>4, 201=>2, 203=>2, 205=>2, 209=>2, 5020=>2, 9020=>1}
    

    考虑 key 2005并注意
    @transition[2005]
      #=> [50, 56]
    

    我们看到有4后四位为 2005 的有效 6 位数字并且,对于每个 4数字,通过添加数字 0 产生一个有效的数字。和 6 ,结果是最后 5 位数字是 2005020056 .但是,我们只需要保留最后四位数字,00500056 ,它们是数字 5056 .因此,重新计算时countsn = 7 --调用它counts7 --我们添加4两者 counts7[50]counts7[56] .其他键 kcounts (对于 n=6 )可能是这样的 @transition[k]具有包含 50 的值和 56 ,所以他们也会为 counts7[50] 做出贡献和 counts7[50] .

    选择性结果

    让我们试试 n 的各种值
    puts "digits  nbr valid*  seconds"
    [1, 2, 3, 4, 5, 6, 20, 50, 100, 1_000, 10_000, 40_000].each do |n|
      print "%6d" % n
      t = Time.now
      print "%11d" % prime_digit_sum(n)
      puts "%10f" % (Time.now-t).round(4)
    end
    puts "\n* modulo (10^9+7)"
    
    digits  nbr valid*  seconds
         1         10  0.000000
         2         90  0.000000
         3        303  0.000200
         4        280  0.002200
         5        218  0.000400
         6         95  0.000400
        20      18044  0.000800
        50  215420656  0.001400
       100  518502061  0.002700
      1000  853799949  0.046100
     10000  590948890  0.474200
     40000  776929051  2.531600
    

    关于ruby - 质数和,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51688970/

    相关文章:

    java - 如何使用并行流在 Java 中查找第 n 个素数

    ruby-on-rails - 如何从 YAML 文件收集条目?

    ruby - 在 Ruby 中使用 2 个助手构造函数的惯用方法

    javascript - 西纳特拉/哈姆 : How to execute Ruby code inside Javascript?

    python - 在pygame中制作平滑的轨道

    c - 用 C 代码实现哥德巴赫猜想

    Python:检查(非常大的)素数时为 "long int too large to convert to float"

    ruby-on-rails - 重载 ActiveSupport 的默认 to_sentence 行为

    Java代码从四元数获取绕轴的旋转角度

    python - 模拟多个泊松过程