有效地抓取一些符合条件的子集

Efficiently grab some subsets that meet criteria

给定一组从 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 值?

我看过:

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

建议的重复项是 ,尽管这个问题略有不同,因为我在 Ruby 中要求进行此优化并且我不希望我的答案中出现空子集。如果我最初发现了那个问题,这个问题会非常有帮助!但是 SergGr 的回答正是我要找的。

设{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)解决它。

虽然@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

这些公式很容易使用常数 space 和 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