Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
给定n,如何在ruby?中找到将n写为1,3,4之和的不同方法的数目;_Ruby_Arrays_Algorithm_Data Structures - Fatal编程技术网

给定n,如何在ruby?中找到将n写为1,3,4之和的不同方法的数目;

给定n,如何在ruby?中找到将n写为1,3,4之和的不同方法的数目;,ruby,arrays,algorithm,data-structures,Ruby,Arrays,Algorithm,Data Structures,问题:给定n,找出将n写成1,3,4之和的不同方法的数目 例:对于n=5,答案是6 5=1+1+1+1+1 5=1+1+3 5=1+3+1 5=3+1+1 5=1+4 5=4+1 我尝试过排列法,但它的效率很低,有没有更有效的方法呢?我认为这个问题应该分两步解决 def add_next sum, a1, a2 residue = a1.inject(sum, :-) residue.zero? ? [a1] : a2.reject{|x| residue < x}.

问题:给定n,找出将n写成1,3,4之和的不同方法的数目 例:对于n=5,答案是6

 5=1+1+1+1+1
 5=1+1+3
 5=1+3+1
 5=3+1+1
 5=1+4
 5=4+1

我尝试过排列法,但它的效率很低,有没有更有效的方法呢?

我认为这个问题应该分两步解决

def add_next sum, a1, a2
  residue = a1.inject(sum, :-)
  residue.zero? ? [a1] : a2.reject{|x| residue < x}.map{|x| a1 + [x]}
end

a = [[]]
until a == (b = a.flat_map{|a| add_next(5, a, [1, 3, 4])})
  a = b
end
第一步

第一步是确定与给定数字相加的
1
s、
3
s和
4
s的不同数目。对于
n=5
,我们只能写3个:

[[5,0,0], [2,1,0], [1,0,1]]
这三个元素分别被解释为“五个1、零3和零4”、“两个1、一个3和零4”以及“一个1、零3和一个4”

为了有效地计算这些组合,我首先只使用1来计算可能的组合,即0到5之间的每个数字的总和(这当然是微不足道的)。这些值保存在散列中,散列的键是求和数,值是求和到键的值所需的1的数目:

h0 = { 0 => 0, 1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5 }
(如果第一个数字是2,而不是1,则为:

h0 = { 0 => 0, 2 => 1, 4 => 2 }
因为无法将2相加为等于1或3。)

接下来,我们考虑使用1和3来将每个值与0和5相加。使用的3的数量只有两种选择,零或一。这就产生了散列:

h1 = { 0 => [[0,0]], 1 => [[1,0]], 2 => [[2,0]], 3 => [[3,0], [0,1]],
  4 => [[4,0], [1,1]], 5 => [[5,0], [2,1]] }
h2 = { 5 => [[5,0,0], [2,1,0], [1,0,1]] }
例如,这表明:

  • 只有一种方法可以使用1和3求和为1:
    1=>[1,0]
    ,表示1和0
  • 求和有两种方式:
    4=>[[4,0],[1,1]]
    ,意思是四个1和零个3或一个1和一个3
类似地,当1、3和4都可以使用时,我们得到散列:

h1 = { 0 => [[0,0]], 1 => [[1,0]], 2 => [[2,0]], 3 => [[3,0], [0,1]],
  4 => [[4,0], [1,1]], 5 => [[5,0], [2,1]] }
h2 = { 5 => [[5,0,0], [2,1,0], [1,0,1]] }
由于该散列对应于所有三个数字1、3和4的使用,因此我们只关注总和为5的组合

在构造h2时,我们可以使用零4s或一个4。如果我们使用零4s,我们将使用一个1s和3,总和为5。我们从
h1
中看到有两种组合:

5 => [[5,0], [2,1]]
对于
h2
,我们将其写成:

[[5,0,0], [2,1,0]]
如果使用一个4,则使用总计为5-1*4=1的1和3。从
h1
中,我们看到只有一种组合:

1 => [[1,0]]
对于
h2
我们写为

[[1,0,1]]
所以

h2
中键
5
的值为:

[[5,0,0], [2,1,0]] + [[1,0,1]] = [[5,0,0], [2,1,0]], [1,0,1]]  
旁白:由于我选择了散列的形式来表示散列
h1
h2
,因此将
h0
表示为:

h0 = { 0 => [[0]], 1 => [[1]],..., 5 => [[5]] }  
显然,这种顺序方法可以用于任何组合要求和的整数集合

步骤2

在步骤1中生成的每个阵列的不同排列数
[n1,n3,n4]
等于:

(n1+n3+n4)!/(n1!n3!n4!)
请注意,如果
n
中的一个为零,则这些将是二项式系数。如果事实上,这些是来自的系数,这是二项式分布的推广。理由很简单。分子给出了所有数字的排列数。
n1
1s可以排列
n1每个不同排列的方式,因此我们除以
n1。对于
n3
n4

对于求和到
5
的示例,有:

  • 5/5! = 1
    [5,0,0]
  • (2+1)/(2!1!)=3
    针对
    [2,1,0]
  • (1+1)/(1!1!)=2
    针对
    [1,0,1]
    的不同安排,总共:
1+3+2=6
数字5的不同排列

代码

def count_combos(arr, n)
  a = make_combos(arr,n)
  a.reduce(0) { |tot,b| tot + multinomial(b) }
end

def make_combos(arr, n)
  arr.size.times.each_with_object([]) do |i,a|
    val = arr[i]
    if i.zero?
      a[0] = (0..n).each_with_object({}) { |t,h|
        h[t] = [[t/val]] if (t%val).zero? }
    else
      first = (i==arr.size-1) ? n : 0 
      a[i] = (first..n).each_with_object({}) do |t,h|
        combos = (0..t/val).each_with_object([]) do |p,b|
          prev = a[i-1][t-p*val]
          prev.map { |pr| b << (pr +[p]) } if prev
        end
        h[t] = combos unless combos.empty?
      end    
    end
  end.last[n]
end

def multinomial(arr)
  (arr.reduce(:+)).factorial/(arr.reduce(1) { |tot,n|
    tot * n.factorial })
end  
(为了便于显示,我将示例的结果(一个长数字)分成两部分。)

count\u组合([1,3,4],500)
计算大约需要2秒;其他的基本上是瞬间的

@sawa的方法和我的方法对6到9之间的
n
给出了相同的结果,因此我相信它们都是正确的。sawa的求解时间随着
n
的增加要比我的快得多,因为他在计算然后计算所有的置换


编辑:@Karole,他刚刚发布了一个答案,我所有的测试(包括最后一个)都得到了相同的结果。我更喜欢哪个答案?嗯,让我想想。)

我认为这个问题应该分两步解决

第一步

第一步是确定与给定数字相加的
1
s、
3
s和
4
s的不同数目。对于
n=5
,我们只能写3个:

[[5,0,0], [2,1,0], [1,0,1]]
这三个元素分别被解释为“五个1、零3和零4”、“两个1、一个3和零4”以及“一个1、零3和一个4”

为了有效地计算这些组合,我首先只使用1来计算可能的组合,即0到5之间的每个数字的总和(这当然是微不足道的)。这些值保存在散列中,散列的键是求和数,值是求和到键的值所需的1的数目:

h0 = { 0 => 0, 1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5 }
(如果第一个数字是2,而不是1,则为:

h0 = { 0 => 0, 2 => 1, 4 => 2 }
因为无法将2相加为等于1或3。)

接下来,我们考虑使用1和3来将每个值与0和5相加。使用的3的数量只有两种选择,零或一。这就产生了散列:

h1 = { 0 => [[0,0]], 1 => [[1,0]], 2 => [[2,0]], 3 => [[3,0], [0,1]],
  4 => [[4,0], [1,1]], 5 => [[5,0], [2,1]] }
h2 = { 5 => [[5,0,0], [2,1,0], [1,0,1]] }
例如,这表明:

  • 只有一种方法可以使用1和3求和为1:
    1=>[1,0]
    ,表示1和0
  • 求和有两种方式:
    4=>[[4,0],[1,1]]
    ,意思是四个1和零个3或一个1和一个3
类似地,当1、3和4都可以使用时,我们得到散列:

h1 = { 0 => [[0,0]], 1 => [[1,0]], 2 => [[2,0]], 3 => [[3,0], [0,1]],
  4 => [[4,0], [1,1]], 5 => [[5,0], [2,1]] }
h2 = { 5 => [[5,0,0], [2,1,0], [1,0,1]] }
由于该散列对应于所有三个数字1、3和4的使用,因此我们只关注总和为5的组合

在建设中<