素数和

时间:2018-08-04 19:32:54

标签: 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

2 个答案:

答案 0 :(得分:8)

如果每个非负整数的数字的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位数字附加到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位数字数组,这些数组是通过将一个数字附加到密钥而获得的。这些值共同包括所有有效的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位数字数组,这些数组是通过将一位数字附加到键上并删除键的第一位获得的。 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(位数增加1)时将用来更新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递增1时,n就会更新。值的总和等于有效n-digit个数字。

每个有效n数字的最后四位数字构成的数字是count的键之一。每个键的值是一个数字数组,其中包括所有有效(n+1)数字的最后四位数字,这些数字是通过将数字附加到键而产生的。

例如,考虑counts的{​​{1}}的值,如下所示。

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} 并注意

2005

我们看到有@transition[2005] #=> [50, 56] 个有效的6位数字,其后四位是4,并且对于每个2005数字,通过将数字40,得出最后5位数字为620050的数字。但是,我们只需要保留数字200560050的后四位005650。因此,在为56重新计算counts时,将其称为n = 7。我们将counts7添加到4counts7[50]中。 counts7[56]中的其他键k(对于counts)可能是这样的:n=6的值包括@transition[k]50,因此它们也将为56counts7[50]做贡献。

选择性结果

让我们尝试使用counts7[50]的各种值

n

答案 1 :(得分:1)

我将通过预先计算所有允许的5位数子序列的列表来解决该问题:“ 00002”失败,而允许“ 28300”等。这可以设置为二进制数组或哈希集

有了列表,您就可以通过将5位数字框一次移到数字上来检查任何数字。

相关问题