优化查找和计算红宝石中的适当除数

时间:2016-05-23 19:26:40

标签: ruby-on-rails ruby

此代码需要在7000毫秒或者超时运行,我正在尝试学习ruby,所以我在这里看看是否有任何想法可以优化此代码。或者,如果您可以让我知道此代码中的哪些功能需要花费最多的时间,那么我可以专注于最有效的部分。

要解决的问题是,你必须判断任何数量的除数是奇数还是偶数。

对于n = 12,除数为[1,2,3,4,6,12] - “偶数”

对于n = 4,除数为[1,2,4] - “奇数”

非常感谢任何帮助,

感谢。

def oddity(n)
  div(n) % 2 == 0 ? (return 'even'): (return 'odd')
end

def div(num)
  divs = []
  (1..num).each{|x| if (num % x == 0) then divs << x end}
  return divs.length
end

4 个答案:

答案 0 :(得分:1)

这里的关键观察是你只需要数量的除数,而不是除数本身。因此,一个相当简单的解决方案是将数字分解为素数,并检查我们可以形成多少组合。

require 'mathn'

def div(num)
  num.prime_division.inject(1){ |prod, n| prod *= n[1] + 1 } 
end

prime_division返回一对对列表,其中第一个是素数,第二个是指数。 E.g:

12.prime_division
=> [[2, 2], [3, 1]]

我们简单地将指数相乘,每个指数加1,以说明未采用此素数的情况。

答案 1 :(得分:1)

由于性能是一个问题,让我们将OP的解决方案与@ standelaune&@ dimid&#39进行比较。

require 'prime'
require 'fruity'

n = 100_000
m = 20
tst = m.times.map { rand(n) }
  #=> [30505, 26103, 53968, 24108, 78302, 99141, 22816, 67504, 10149, 28406,
  #    18294, 92203, 73157, 5444, 24928, 65154, 24850, 64219, 68310, 64951]

def op(num) # Alex
  divs = []
  (1..num).each { |x| if (num % x == 0) then divs << x end }
  divs.length
end

def test_op(tst) # Alex
  tst.each { |n| op(n) }
end

def pd(num) # divid
  num.prime_division.inject(1){ |prod, n| prod *= n[1] + 1 } 
end

def test_pd(tst) #divid
  tst.each { |n| nfacs_even?(n) }
end

def div(num) # standelaune
  oddity = false
  (1..num).each{|x| if (num % x == 0) then oddity = !oddity end}
  oddity ? "odd" : "even"
end

def test_div(tst) # standelaune
  tst.each { |n| div(n) }
end

compare do
  _test_op  { test_op tst  } 
  _test_div { test_div tst }
  _test_pd  { test_pd tst  }
end

Running each test 16 times. Test will take about 56 seconds.
_test_pd is faster than _test_div by 480x ± 100.0
_test_div is similar to _test_op

divid方法吸引其他方法,我并不感到惊讶,因为prime_division使用默认素数生成器Prime::Generator23(的一个实例),生成器用C编码,并且相对于Prime子类中的其他生成器快。

答案 2 :(得分:0)

您可以通过优化算法来解决此问题。

您不必检查您正在检查的号码下面的所有数字。将您的数字分成主要组成部分就足够了。然后,组合数据的一个简单问题就是确定有多少可能的除数。

获得所有主要组件的一种方法可能是:

PRIME_SET = [2,3,5,7,11,13,17,19]
def factorize(n)
  cut_off = Math.sqrt(n)
  parts = []
  PRIME_SET.each do |p|
    return parts if p > cut_off
    if n % p == 0
      n = n/p
      parts << p
      redo
    end
  end
  raise 'To large number for current PRIME_SET'
end

然后计算可能的数量可以通过多种不同的方式完成,并且有可能在不计算它们的情况下完成它。但这是一个天真的实现。

def count_possible_divisors(factors)
  divisors = Set.new
  (1..factors.length-1).each do |i|
    factors.combination(i).each do |comb|
      divisors.add(comb.reduce(1, :*))
    end
  end
  divisors.length + 2 # plus 2 for 1 and n
end

这应该会比你正在做的工作少。但对于大数字来说,这是一项艰巨的任务。

答案 3 :(得分:0)

如果你想坚持你的算法,这是一个优化。

def div(num)
  oddity = false
  (1..num).each{|x| if (num % x == 0) then oddity = !oddity end}
  oddity ? "odd" : "even"
end