【问题标题】:How can I find all prime numbers below a given input value?如何找到给定输入值以下的所有素数?
【发布时间】:2021-01-31 21:03:26
【问题描述】:

我的任务是在 Ruby 中查找所有素数(不使用素数方法)

以下是我的逻辑、问题、代码和输出。

我的逻辑:

所有素数只有 2 个不同的除数。因此,如果我们计算一个数的除数并且它大于 2,那么它不是素数(除了 1,我还没有处理)

因此我制作了一个数组,用 1 到 10 的所有数字填充它(我稍后会做 1000),并删除所有有超过 2 个除数的数字

问题:

下面的代码似乎没有检查 5,7 或 9。

我的代码(红宝石)

#Create an array that will be your list of prime numbers
primes = [] 
 
#create variables
x = 1
counter = 0
y=1  
 
#fill array with numbers (I'm using 10 rather than 1000 for now)
for x in 1..10 # 1..10 is inclusive in ruby
primes.push(x)  #put x into primes array
end
 
primes.each do |prime_number|  for y in 1..10 do #for each x and each y, do the following
      puts "#{prime_number} , #{y}" #puts is print with a new line
      if prime_number % y == 0   #check if element in array divided by numbers 1-10 is mod 0
        counter +=1   #is so increase counter by 1
        if counter > 2
          primes.delete(prime_number)
          counter = 0   #if counter get's above 2 then number cannot be prime. hence remove it from array
        end
      end
    end
end
 
puts primes  #print out primes to check result
sum_of_primes = primes.inject(:+)  #sum of all primes
puts sum_of_primes #print answer

这是输出:

1 , 1
1 , 2
1 , 3
1 , 4
1 , 5
1 , 6
1 , 7
1 , 8
1 , 9
1 , 10
2 , 1
2 , 2
2 , 3
2 , 4
2 , 5
2 , 6
2 , 7
2 , 8
2 , 9
2 , 10
4 , 1
4 , 2
4 , 3
4 , 4
4 , 5
4 , 6
4 , 7
4 , 8
4 , 9
4 , 10
6 , 1
6 , 2
6 , 3
6 , 4
6 , 5
6 , 6
6 , 7
6 , 8
6 , 9
6 , 10
8 , 1
8 , 2
8 , 3
8 , 4
8 , 5
8 , 6
8 , 7
8 , 8
8 , 9
8 , 10
10 , 1
10 , 2
10 , 3
10 , 4
10 , 5
10 , 6
10 , 7
10 , 8
10 , 9
10 , 10
[1, 3, 5, 7, 9]
25 

【问题讨论】:

  • “找到所有的素数”——这可能需要一段时间 ;-)
  • 您可能对Sieve of Eratosthenes 感兴趣 - 一种古老的算法,用于查找给定范围内的所有素数,无需除法/模数。
  • @Stefan 好呼唤。请注意,Ruby 已经在标准库中实现了Prime::EratosthenesSieve
  • 唯一的偶数是2。2以上只需要检查奇数。这将使您的工作量大致减半。

标签: ruby loops counter primes


【解决方案1】:

下面的代码似乎没有检查 5,7 或 9

那是因为你在遍历数组时要从数组中删除元素。

作为一种解决方法,您可以使用reverse_each 以相反的顺序遍历数组:

primes = (2..20).to_a

primes.reverse_each do |prime_number|
  counter = 0
  1.upto(20) do |y|
    if prime_number % y == 0
      counter += 1
      primes.delete(prime_number) if counter > 2
    end
  end
end

primes
#=> [2, 3, 5, 7, 11, 13, 17, 19]

或者您可以使用delete_if,如果该块的计算结果为true,它将原地删除当前元素:

primes = (2..20).to_a

primes.delete_if do |prime_number|
  counter = 0
  (1..20).find do |y|
    counter += 1 if prime_number % y == 0
    counter > 2
  end
end

primes
#=> [2, 3, 5, 7, 11, 13, 17, 19]

【讨论】:

    【解决方案2】:

    TL;DR

    您可以重构迭代器以避免显式循环和计数器。即使您仍在迭代,利用核心方法也可以更快、更轻松地进行调试。

    为了完整起见,我还提供了一个使用标准库中 Ruby 的 Prime 模块的示例。这可能是最正确的解决方案,但使用模块是一种在很大程度上完全避开了迭代问题的解决方案,至少从实用的角度来看是这样。

    简化使用迭代器

    如果您的问题是关于功课的,那么我的回答可能无法帮助您了解老师希望您从课程中学到的任何东西。其他答案可能会解释为什么您当前的代码无法按预期工作;相反,我将专注于利用更多 Ruby 核心功能的替代方法。

    这是找到所有正素数直到给定最大值的一种方法:

    # Use a Range object to check each Integer between 2 and
    # (int - 1) to see if there's a remainder. If not, the value
    # of i is added to the anonymous array returned by #map. The
    # int is prime if there are no elements in the array.
    def prime? int 
      (2...int).none? { |i| int.modulo(i).zero? }
    end
    
    # Iterate from 2 to the maximum value, using #select to
    # return the subset of values passed to the block where the
    # return value of #prime? is truthy.
    #
    # NB: 1 isn't prime, which is why we start from 2. Reference:
    # <https://en.wikipedia.org/wiki/Prime_number#Primality_of_one>
    def find_primes max_value
      2.upto(max_value).select { |i| prime? i }
    end
    
    find_primes 10
    #=> [2, 3, 5, 7]
    

    当然还有其他方法可以做到这一点,但利用 Array#none?Array#select 等内置迭代器同时避免计数器对我来说似乎是一种净赢。您的里程可能会有所不同。

    使用标准库

    作为进一步的简化,您可以简单地使用 Ruby 标准库中的 Prime 模块。例如,Prime#each 返回一个可枚举的Prime::EratosthenesGenerator。然后您可以调用Enumerable#to_a 将结果转换为数组。例如:

    require 'prime'
    
    def primes max_value
      Prime.each(max_value).to_a
    end
    
    primes 10
    #=> [2, 3, 5, 7]
    

    利用核心和标准库类通常比实现您自己的例程更快(并且可能更不容易出错),但这对于教育目的来说可能是“过分的桥梁”。尽管如此,我还是将其包含在此处是为了完整起见并帮助未来的访问者。

    【讨论】:

    • 或者使用惰性枚举器:D
    • @CarySwoveland #any?和#none?仍然需要迭代,但我同意在这种情况下使用它们比 select {}.none? 更好。感谢您的建议。
    • 我建议,例如,(2...int).all? { |i| int % i &gt; 0 },它的效率与(2...int).none? 相当,但使用none? 阅读效果更好。主要是如果找到iint % i #=&gt; 0 对应,则退出枚举。
    • @ajm 如果您想发布一些显示性能或语义改进的代码,我很乐意考虑。但是,#prime?即使具有像100_000_000_000_103 这样的非常大的值,也几乎立即返回。另一方面,#find_primes 必须遍历所有值以返回预期结果,因此由于迭代器无法提前返回,因此不清楚惰性枚举器会为您带来什么。
    猜你喜欢
    • 1970-01-01
    • 2019-08-09
    • 2016-01-31
    • 2014-01-06
    • 1970-01-01
    • 1970-01-01
    • 2013-02-03
    • 2021-07-22
    • 1970-01-01
    相关资源
    最近更新 更多