有效地获取符合标准的一些子集

时间:2018-03-22 08:36:15

标签: ruby algorithm math combinatorics

鉴于从1n的一组连续数字,我试图找到不包含连续数字的子集数量。

例如,对于集合[1, 2, 3],一些可能的子集是[1, 2][1, 3]。前者不会计算,而后者则不计算,因为1和3不是连续的数字。

这就是我所拥有的:

def f(n)
  consecutives = Array(1..n)
  stop = (n / 2.0).round
  (1..stop).flat_map { |x|
    consecutives.combination(x).select { |combo|
      consecutive = false
      combo.each_cons(2) do |l, r|
        consecutive = l.next == r
        break if consecutive
      end
      combo.length == 1 || !consecutive
    }
  }.size
end

它有效,但我需要它在n <= 75的12秒内更快地工作。如何优化此方法,以便我可以处理高n值的汗水?

我看了看:

和其他一些人。我似乎无法找到答案。

建议重复是Count the total number of subsets that don't have consecutive elements,虽然这个问题略有不同,因为我在Ruby中要求这个优化,我不想在我的答案中使用空子集。如果我最初发现那个问题,这个问题会非常有用!但SergGr的答案正是我所寻找的。

2 个答案:

答案 0 :(得分:4)

虽然@ user3150716的想法是正确的,但细节是错误的。特别是你可以看到n = 3有4个子集:[1][2][3][1,3],而他的公式只给出3.这是因为他错过了子集[3](即仅由[i]组成的子集),并且错误累积为较大的n。此外,我认为如果您从1开始而不是n,则更容易思考。所以正确的公式将是

f(1) = 1
f(2) = 2
f(n) = f(n-1) + f(n-2) + 1

这些公式很容易使用恒定空间和O(n)速度的简单循环进行编码:

def f(n)
  return 1 if n == 1
  return 2 if n == 2

  # calculate 
  # f(n) = f(n-1) + f(n - 2) + 1
  # using simple loop
  v2 = 1
  v1 = 2
  i = 3
  while i <= n do
     i += 1
     v1, v2 = v1 + v2 + 1, v1
  end 
  v1
end

您可以在线查看此原始代码here

对于任何n <= 75来说,这应该非常快。对于更大的n,您可能需要一些额外的技巧,例如注意f(n)实际上比Fibonacci number

少一个

f(n) = Fib(n+2) - 1

并且斐波纳契数的闭合公式理论上可以更快地计算出大n

答案 1 :(得分:1)

让{i ... n}没有连续数字的子集数为f(i),则f(i)为以下总和:

1)f(i + 1),这些子集中没有i的数量。

2)f(i + 2)+ 1,其中包含i的子集的数量(因此从子集中省略i + 1)

所以,

f(i)=f(i+1)+f(i+2)+1
f(n)=1
f(n-1)=2

f(1)将是你的答案。 您可以在O(logn)时间使用矩阵求幂(http://zobayer.blogspot.in/2010/11/matrix-exponentiation.html)来解决它。