Ruby中数字的最大素数

时间:2018-07-28 10:27:47

标签: ruby primes factors

早上好

我编写了以下代码,该代码适用于小数数字,以找到数字的最大质数。我不能使用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)的边缘情况时,系统将冻结。

任何人都可以告诉我如何重构代码以使其更有效率吗?

非常感谢!

4 个答案:

答案 0 :(得分:8)

您的代码有很多问题,使其效率大大降低:

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

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

后者最简单:使用Range而不是Array

前者需要一些思考。这是一个主意:您一次又一次检查相同的数字。没必要一旦确定数字是除数,就已经知道它是除数,您无需再次检查。

同样,您一次又一次地检查相同的数字的素性。但是,一旦确定数字是质数,就不需要再次检查,毕竟改变的可能性很小。

但是首先,让我们看一下有效解决方案的外观:

require 'prime'

def biggest_prime(number)
  number.prime_division.last.first
end

答案 1 :(得分:3)

我个人将只使用内置功能,而不是尝试重新发明轮子。

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位及更高的数字进行测试以查看结果是否可重复。

答案 2 :(得分:1)

当我们看到论文中应该解决的问题时,我们可以使用prime_division。它为您提供一组您的数字除以的素数对。它可以快速处理大数字。

// 'targetMessage' contains the message ID
// 'targetReaction' contains the reaction to be added
// 'role' contains the ID of the role, the user should receive
// 'mongo()' simply connects to the database

await mongo().then(async mongoose => {
    try {
        await messageReactionSchema.findOneAndUpdate({
            _id: guild.id,
            message: {
                $elemMatch: {
                    msgId: targetMessage,
                },
            },
        }, {
            _id: guild.id,
            $push: {
                message: [{
                    msgId: targetMessage,
                    reactionRole: [{
                        reaction: targetReaction,
                        role,
                    }],
                }],
            },
        }, {
            upsert: true,
        });
    }
    finally {
        mongoose.connection.close();
    }
});

答案 3 :(得分:0)

您可以通过更改除以的最大数来进行操作,直到没有更多的质数可用于划分。这样,您就不必在乎中间质数,而可以大大减少迭代次数:)

    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)