ruby中求和为N的K个数的可能方程的个数

ruby中求和为N的K个数的可能方程的个数,ruby,algorithm,logic,Ruby,Algorithm,Logic,我必须用RubyonRails创建一个程序,这样就可以用更少的时间来解决特定的问题。现在,对于k=4,我得到的响应时间更少,但是对于k>5,响应时间更多 问题: 问题是响应时间 当k值大于5(k>5)时,响应时间对于下面的公式来说太晚了 输入:K,N(其中07或更少的数组,最多有k=>3个元素,包括一个或多个2,以及零个或多个1(这有点多,但正如我将解释的那样,它仍然不精确。) 创建由块变量f表示的初始空哈希。此哈希的默认值如下: f[k] << o 被执行之前 f[k] <

我必须用RubyonRails创建一个程序,这样就可以用更少的时间来解决特定的问题。现在,对于k=4,我得到的响应时间更少,但是对于k>5,响应时间更多

问题:

问题是响应时间

当k值大于5(k>5)时,响应时间对于下面的公式来说太晚了

输入:K,N(其中0n的个数精确地转换成
K
总和。这个数字(计算上)有点难看

思想是这样的:让
p(n,k)
成为将
n
划分为
k
非零和数的方法的数量;然后
p(n,k)=p(n-1,k-1)+p(n-k,k)
。证明:每个分区要么包含
1
,要么不包含1作为和数之一。第一种情况
p(n-1,k-1)
计算总和中存在
1
的情况数;将该
1
从总和中移除,并将剩余的
n-1
划分为现在可用的
k-1
总和。第二种情况
P(n-k,k)
考虑每个求和严格大于
1
的情况;为此,将所有
k
求和减少
1
并从那里递归。显然,对于所有
n>0
,P(n,1)=1

这可能提到,一般的
k

没有已知的封闭形式。你想要将
n
的数量精确地转化为
k
总和。这个数字(计算上)有点难看

思想是这样的:让
p(n,k)
成为将
n
划分为
k
非零和数的方法的数量;然后
p(n,k)=p(n-1,k-1)+p(n-k,k)
。证明:每个分区要么包含
1
,要么不包含1作为和数之一。第一种情况
p(n-1,k-1)
计算总和中存在
1
的情况数;将该
1
从总和中移除,并将剩余的
n-1
划分为现在可用的
k-1
总和。第二种情况
P(n-k,k)
考虑每个求和严格大于
1
的情况;为此,将所有
k
求和减少
1
并从那里递归。显然,对于所有
n>0
,P(n,1)=1


这就提到,一般的
k

没有已知的封闭形式。这里有两种方法来计算您的答案。第一种方法简单但效率不高;第二种方法依赖于优化技术,速度更快,但需要大量代码

紧凑但效率低下

这是一种紧凑的计算方法,使用以下方法:

代码

例子

评论

前两次计算耗时不到一秒,但第三次计算耗时几分钟

效率更高,但复杂性更高

代码

评论

计算
组合(1000,3)。大小
大约需要5秒,其他的都在1秒以内

解释

此方法用于计算解决方案。状态变量是用于计算大小不超过
k
且元素总和不超过
n
的数组的最大正整数。从等于1的最大整数开始。下一步是计算
k
或更少元素的所有组合,包括数字1和2,然后是1、2和3,依此类推,直到我们有
k
或更少元素的所有组合,包括数字1到
n
。然后我们从上一次计算中选择所有
k
元素的组合,这些元素的总和为
n

假设

k => 3
n => 7
然后

这意味着,仅使用数字
1
[[1]]
是求和为
1
的所有数组的数组,
[[1,1]]
是求和为
2
的所有数组的数组

im = [i]*m #=> [2]*1 => [2]  
mxi = m*i  #=>   2*1 =>  2
请注意,这不包括元素
3=>[[1,1,1]]
。这是因为,已经有
k=3
elments,if不能与任何其他元素组合,并且总和为
3<7

我们接下来执行:

enum = (2..n-k+1).each #=> #<Enumerator: 2..5:each>
正如
n=>7
一样,您可能想知道为什么此数组以
5
结尾。这是因为没有包含三个正整数的数组,其中至少有一个是
6
7
,其元素总和为
7

第一个值
enum
传递到块中,由块变量
i
表示,它是
2
。我们现在将计算一个哈希
g
,它包括所有和
n=>7
或更少的数组,最多有
k=>3个
元素,包括一个或多个
2
,以及零个或多个
1
(这有点多,但正如我将解释的那样,它仍然不精确。)

创建由块变量
f
表示的初始空哈希。此哈希的默认值如下:

f[k] << o
被执行之前

f[k] << o
(当第一个元素之后的元素被传递到块中时,哈希可能不是空的)。传递到块的第一个元素是
[1,{}]
,由块变量表示:

m => 1
f => Hash.new {|h,k| h[k]=[]}
m=>1
意味着我们将首先构造包含一个(
i=
2
的数组

im = [i]*m #=> [2]*1 => [2]  
mxi = m*i  #=>   2*1 =>  2
作为
(m==k)#=>(1==3)=>false
,我们接下来执行

f[mxi] << im if mxi + (k-m)*(i+1) <= n
  #=> f[2] << [2] if 2 + (3-1)*(1+1) <= 7
  #=> f[2] << [2] if 8 <= 7
它传递值

enum3.to_a #=> [1, 2]
在它的块中,由块变量
j
表示,它是散列
h
的键。我们在这里要做的是将一个
2
m=1
)与包含整数到
1
(即仅)的元素数组相结合
enum = (2..n-k+1).each #=> #<Enumerator: 2..5:each>
enum.to_a              #=> [2, 3, 4, 5]
enum2 = (1..[n/i,k].min).each_with_object(Hash.new {|h,k| h[k]=[]})
  #=> (1..[7/2,3].min).each_with_object(Hash.new {|h,k| h[k]=[]})
  #=> (1..3).each_with_object(Hash.new {|h,k| h[k]=[]})
f[k] << o
(f[k] |= []) << o
f[k] = []
f[k] << o
enum2.to_a #=> => [[1, {}], [2, {}], [3, {}]]
m => 1
f => Hash.new {|h,k| h[k]=[]}
im = [i]*m #=> [2]*1 => [2]  
mxi = m*i  #=>   2*1 =>  2
f[mxi] << im if mxi + (k-m)*(i+1) <= n
  #=> f[2] << [2] if 2 + (3-1)*(1+1) <= 7
  #=> f[2] << [2] if 8 <= 7
enum3 = (1..[(i-1)*(k-m), n-mxi].min).each
  #=> = (1..[2,5].min).each
  #=> = (1..2).each
  #=> #<Enumerator: 1..2:each>                                                
enum3.to_a #=> [1, 2]
h[j]              #=> [[1]]
enum4 = h[j].each #=> #<Enumerator: [[1]]:each>
enum4.to_a        #=> [[1]]
a                 #=> [1]
f[mxi+j].concat([a+im]) if
  ((a.size==k-m && mxi+j==n) || (a.size<k-m && (mxi+j+(k-m-a.size)*(i+1))<=n))
  #=> f[2+1].concat([[1]+[2]) if ((1==2 && 2+1==7) || (1<=3-1 && (2+1+(1)*(3)<=7)) 
  #=> f[3].concat([1,2])      if ((false && false) || (1<=2   && (6<=7))
  #=> f[3] = [] << [[1,2]]    if (false            || (true   && true)
  #=> f[3] = [[1,2]]          if true
a.size==k-m && mxi+j==n
([2] + f[j]).size == k && ([2] + f[j]).reduce(:+) == n 
h[j]              #=> [[1, 1]]
enum4 = h[j].each #=> #<Enumerator: [[1, 1]]:each>
enum4.to_a        #=> [[1, 1]]
a                 #=> [1, 1]

f[mxi+j].concat([a+im]) if
  ((a.size==k-m && mxi+j==n) || (a.size<k-m && (mxi+j+(k-m-a.size)*(i+1)<=n))
  #=> f[4].concat([1, 1, 2]) if ((2==(3-1) && 2+2 == 7) || (2+2+(3-1-2)*(3)<=7))
  #=> f[4].concat([1, 1, 2]) if (true      && false)    || (false && true))
  #=> f[4].concat([1, 1, 2]) if false
f={3=>[[1, 2]], 4=>[[2, 2]]}
g #=> {3=>[[1, 2]], 4=>[[2, 2]]}
g.update({ n=>[i]*k }) if i*k == n
  #=> g.update({ 7=>[2,2,2] }) if 6 == 7
h.update(g) { |k,ov,nv| ov+nv }
  #=> {}.update({3=>[[1, 2]], 4=>[[2, 2]]} { |k,ov,nv| ov+nv }
  #=> {1=>[[1]], 2=>[[1, 1]], 3=>[[1, 2]], 4=>[[2, 2]]}
 i    m    j         f

 h #=> { 1=>[[1]], 2=>[[1,1]] } 

 2    1    1   {3=>[[1, 2]]}
 2    1    2   {3=>[[1, 2]]}
 2    2    1   {3=>[[1, 2]], 4=>[[2, 2]]}
                                {3=>[[1, 2]], 4=>[[2, 2]]}
 3    1    1   {}
 3    1    2   {}
 3    1    3   {}
 3    1    4   {7=>[[2, 2, 3]]}
 3    2    1   {7=>[[2, 2, 3], [1, 3, 3]]}

 g before g.update: {7=>[[2, 2, 3], [1, 3, 3]]}
 g after  g.update: {7=>[[2, 2, 3], [1, 3, 3]]}

 h after h.update(g): {1=>[[1]],
                       2=>[[1, 1]],
                       3=>[[1, 2]],
                       4=>[[2, 2]],
                       7=>[[2, 2, 3], [1, 3, 3]]}
 4   1     1   {}
 4   1     2   {}
 4   1     3   {7=>[[1, 2, 4]]}

 g before g.update: {7=>[[1, 2, 4]]}
 g after  g.update: {7=>[[1, 2, 4]]}

 h after h.update(g): {1=>[[1]],
                       2=>[[1, 1]],
                       3=>[[1, 2]],
                       4=>[[2, 2]],
                       7=>[[2, 2, 3], [1, 3, 3], [1, 2, 4]]}
 5   1  1   {}
 5   1  2   {7=>[[1, 1, 5]]}

 g before g.update: {7=>[[1, 1, 5]]}
 g after  g.update: {7=>[[1, 1, 5]]}

 h after h.update(g): {1=>[[1]],
                       2=>[[1, 1]],
                       3=>[[1, 2]],
                       4=>[[2, 2]],
                       7=>[[2, 2, 3], [1, 3, 3], [1, 2, 4], [1, 1, 5]]}
h[n].select { |a| a.size == k }
  #=> h[7].select { |a| a.size == 3 }
  #=> [[2, 2, 3], [1, 3, 3], [1, 2, 4], [1, 1, 5]]
def combos(n,k)
  if k == 1
    return [n]
  end
  (1..n-1).flat_map do |i|
    combos(n-i,k-1).map { |r| [i, *r].sort }
  end.uniq
end
def combos(n,k,min = 1)
  if n < k || n < min
    return []
  end
  if k == 1
    return [n]
  end
  (min..n-1).flat_map do |i|
    combos(n-i,k-1, i).map { |r| [i, *r] }
  end
end
              user     system      total        real
My Solution   2.570000   0.010000   2.580000 (  2.695615)
Cary's        2.590000   0.000000   2.590000 (  2.609374)
def combos(n,k,min = 1, cache = {})
  if n < k || n < min
    return []
  end
  cache[[n,k,min]] ||= begin
    if k == 1
      return [n]
    end
    (min..n-1).flat_map do |i|
      combos(n-i,k-1, i, cache).map { |r| [i, *r] }
    end
  end
end
Benchmark.bm do |bm|
  bm.report('Uri') { combos(1000, 3) }
  bm.report('Cary') { combos_cary(1000, 3) }
end

       user     system      total        real
Uri   0.200000   0.000000   0.200000 (  0.214080)
Cary  7.210000   0.000000   7.210000 (  7.220085)