【问题标题】:Biggest prime factor of a number in RubyRuby中数字的最大素数
【发布时间】:2019-01-05 07:33:36
【问题描述】:

早上好,

我编写了以下代码,它适用于 small-is 数字,以找到一个数字的最大素数。我不能使用Prime,我需要想出一个手动解决方案。

def is_prime?(number)
  list = (2..(Math.sqrt(number))).to_a
  list.each do |i|
    return false if number % i == 0 
  end 
  true 
end 

def biggest_prime(number)
  list = (2..((number))).to_a
  divisors = list.select{|i| is_prime?(i)}
  divisors.select{|i| number % i == 0 }.max
end 

13195 的质因数是 5、7、13 和 29。

biggest_prime(13195) => 29

但是,当我尝试使用 biggest_prime(600851475143) 的边缘情况时,系统会冻结。

谁能告诉我如何重构我的代码以提高效率?

非常感谢!

【问题讨论】:

    标签: ruby primes factors


    【解决方案1】:

    您的代码存在许多问题,导致其效率极低:

    • 在您的biggest_prime 方法中,您在内存中构造一个Array,以存储小于目标数字的每个数字,而不是仅遍历范围。对于600851475143Array 在内存中的大小约为 4 TiByte。你有 4 TiByte 的 RAM 吗?如果没有,您的系统将创建一个巨大的交换文件并不断交换。请改用Range!事实上,您已经在使用Range,然后毫无理由地将其转换为Array
    • is_prime? 方法中也会发生同样的情况。这个Array 要小得多(最大只有大约 6 MiByte),但你一遍又一遍地创建它,600 十亿 次!创建 n 个数字的 Array 需要 O(n) 时间,因此创建 n 次 sqrt(n) 数字的 Array 需要 O(n * sqrt(n)) 时间。总共需要 SUM(sqrt(n), n = 1 TO 600851475143) 步,大约是 310498000000000000 步。
    • 这也是整个算法所需的步数。即使如果您有一个 10 GHz CPU,并且这个 CPU 有 100 个内核,并且您的问题是完全可并行化的,并且 em> 你可以在一条 CPU 指令中执行整个算法迭代,仍然需要 3.5 天才能得到结果。由于您没有使用并行性,可能没有 10 GHz CPU,并且一次迭代需要 10 秒或数百个 CPU 周期,更现实的数字是至少 100 年。

    因此,您需要做的是大量减少迭代次数和内存量。

    后者最简单:使用Ranges 而不是Arrays。

    前者需要一些思考。这是一个想法:你一遍又一遍地检查相同的数字。那没有必要。一旦你确定了一个数是一个除数,你就已经知道它是一个除数,你不需要再去检查了。

    同样,您一遍又一遍地检查同一个数字的素数。但是一旦你确定一个数是素数,你就不需要再检查了,毕竟它不太可能改变。

    但首先,让我们看看高效的解决方案是什么样的:

    require 'prime'
    
    def biggest_prime(number)
      number.prime_division.last.first
    end
    

    【讨论】:

    • 谢谢大家,不幸的是我还需要想出一个不涉及使用prime的解决方案。
    • @Sergi 也许您可以在问题中指定!
    • 或者查看文档中prime 的来源。它是用 Ruby 编写的。
    【解决方案2】:

    我个人会为此使用内置函数,而不是尝试重新发明轮子。

    require 'prime'
    number = 13195
    Prime.each(number).select { |n| number % n == 0 }
    

    这将导致[5, 7, 13, 29] 的预期输出。 最后一个值总是最大的,所以...

    Prime.each(number).select { |n| number % n == 0 }.last
    

    提供您正在寻找的29。这显然可以稍微清理一下,但会给你这个想法。

    这会以完全相同的数字“冻结”,这很可能会进入 Bignum 并超出 32 位整数范围(实际上 Ruby 为 31 位)。不过,我将不得不深入研究 C 代码以查看冻结的全部内容,或者使用其他一些 64 位和更高的数字进行测试以查看结果是否可重复。

    【讨论】:

      【解决方案3】:

      当我们认为问题应该在论文中解决时,我们可以使用 prime_division。它为您提供了一组素数对,您的数字除以。它适用于大数字。

      p (Prime.prime_division(number))[-1].max
      

      【讨论】:

        【解决方案4】:

        您可以通过更改除以它的最大数来做到这一点,直到您没有更多的素数可以除以。这样你就不用关心中间素数并大大减少迭代:)

            def top_prime(n) 
              max = n
              lower = 2
        
              while lower < max
                 while max % lower == 0 && max != lower
                   max = max / lower
                 end
              lower = lower+1
              end
              max
            end
        
            puts top_prime(600851475143)
        

        【讨论】:

          猜你喜欢
          • 2017-01-13
          • 2016-03-25
          • 2019-09-03
          • 1970-01-01
          • 2021-05-12
          • 2022-08-21
          • 1970-01-01
          • 1970-01-01
          • 2018-09-20
          相关资源
          最近更新 更多