如何在Ruby中使用循环输出所有可能的组合?
我刚开始学习编程,正在尝试编写一个输出所有可能组合的函数。到目前为止,我已经能够找到所有可能的大小2的组合,但我不知道如何让代码开放,以处理更大的大小组合。某种递归会有用吗 我知道我可以使用内置的组合方法,但我只是想知道如何从头开始编写它。任何建议都将不胜感激。谢谢如何在Ruby中使用循环输出所有可能的组合?,ruby,loops,combinations,Ruby,Loops,Combinations,我刚开始学习编程,正在尝试编写一个输出所有可能组合的函数。到目前为止,我已经能够找到所有可能的大小2的组合,但我不知道如何让代码开放,以处理更大的大小组合。某种递归会有用吗 我知道我可以使用内置的组合方法,但我只是想知道如何从头开始编写它。任何建议都将不胜感激。谢谢 def two_combos(input) list = [] for index1 in (0...input.length) for index2 in (0...input.length)
def two_combos(input)
list = []
for index1 in (0...input.length)
for index2 in (0...input.length)
if input[index1] != input[index2]
if list.include?([input[index2],input[index1]])==false
list << [input[index1],input[index2]]
end
end
end
end
return list
end
two_combos(["A","B","C"])
#outputs
=> [["A", "B"], ["A", "C"], ["B", "C"]]
#Missing
["A","B","C"]
def两个_组合(输入)
列表=[]
对于index1英寸(0…输入长度)
对于index2英寸(0…输入长度)
如果输入[index1]!=输入[index2]
if list.include?([input[index2],input[index1]])==false
列表[“A”、“B”]、[“A”、“C”]、[“B”、“C”]]
#失踪
[“A”、“B”、“C”]
这里是递归算法
def combinations(array, size)
fail "size is too big" if size > array.size
combination([], [], array, size)
end
def combination(result, step, array, size)
steps = size - step.size
array[0..-steps].each_with_index do |a, i|
next_step = step + [a]
if next_step.size < size
combination(result, next_step, array[i+1..-1], size)
else
result << next_step
end
end
result
end
a = ("A".."E").to_a
p combinations(a, 1)
# [["A"], ["B"], ["C"], ["D"], ["E"]]
p combinations(a, 2)
# [["A", "B"], ["A", "C"], ["A", "D"], ["A", "E"], ["B", "C"], ["B", "D"], ["B", "E"], ["C", "D"], ["C", "E"], ["D", "E"]]
p combinations(a, 3)
# [["A", "B", "C"], ["A", "B", "D"], ["A", "B", "E"], ["A", "C", "D"], ["A", "C", "E"], ["A", "D", "E"], ["B", "C", "D"], ["B", "C", "E"], ["B", "D", "E"], ["C", "D", "E"]]
p combinations(a, 4)
# [["A", "B", "C", "D"], ["A", "B", "C", "E"], ["A", "B", "D", "E"], ["A", "C", "D", "E"], ["B", "C", "D", "E"]]
def组合(阵列、大小)
如果大小>array.size,则“大小太大”失败
组合([]、[]、阵列、大小)
结束
def组合(结果、步长、阵列、大小)
步长=大小-步长.size
数组[0..-steps]。每个_都有_索引do | a,i|
下一步=步骤+[a]
如果下一步的步长 结果我可以想出一种不使用递归计算给定大小的组合的方法,但它不是特别有效。但是,如果您想要获得所有大小的所有组合(有时称为“功率”),那么它是非常有效的[编辑:显然不是。请参阅基准。]我的理解是,问题涉及后者,但我将给出每个问题的方法
如果索引
包含n个
元素,则每个组合都可以由n个
元素数组表示,该数组的元素为零或一,1
表示该组合包含该索引处的元素,“0”(或前导空格)表示不包含该元素。因此,我们可以通过简单地生成长度为n
的所有二进制数,将每个二进制数从其字符串表示(带前导零)转换为“0”和“1”的数组来生成所有大小的所有组合集,用它们的索引位置替换“1”
,删除“0”
,并在给定索引位置提取索引的元素
代码
def all_combos(sz)
[*(0..2**sz-1)].map { |i| ("%0#{sz}b" % i).chars }
.map { |a| a.each_with_index
.select { |n,ndx| n=="1" }.map(&:last) }
end
def combos(input, n, all_combos)
all_combos.select { |c| c.size == n }.map { |c| input.values_at(*c) }
end
def power(input, all_combos)
all_combos.map { |c| input.values_at(*c) }
end
input = %w{b e a r s}
#=> ["b", "e", "a", "r", "s"]
ac = all_combos(input.size)
#=> [[], [4], [3], [3, 4], [2], [2, 4], [2, 3], [2, 3, 4],
# [1], [1, 4], [1, 3], [1, 3, 4], [1, 2], [1, 2, 4], [1, 2, 3],
# [1, 2, 3, 4], [0], [0, 4], [0, 3], [0, 3, 4], [0, 2], [0, 2, 4],
# [0, 2, 3], [0, 2, 3, 4], [0, 1], [0, 1, 4], [0, 1, 3], [0, 1, 3, 4],
# [0, 1, 2], [0, 1, 2, 4], [0, 1, 2, 3], [0, 1, 2, 3, 4]]
(0..input.size).each { |i| puts "size #{i}"; p combos(input, i, ac) }
# size 0
# [[]]
# size 1
# [["s"], ["r"], ["a"], ["e"], ["b"]]
# size 2
# [["r", "s"], ["a", "s"], ["a", "r"], ["e", "s"], ["e", "r"],
# ["e", "a"], ["b", "s"], ["b", "r"], ["b", "a"], ["b", "e"]]
# size 3
# [["a", "r", "s"], ["e", "r", "s"], ["e", "a", "s"], ["e", "a", "r"],
# ["b", "r", "s"], ["b", "a", "s"], ["b", "a", "r"], ["b", "e", "s"],
# ["b", "e", "r"], ["b", "e", "a"]]
# size 4
# [["e", "a", "r", "s"], ["b", "a", "r", "s"], ["b", "e", "r", "s"],
# ["b", "e", "a", "s"], ["b", "e", "a", "r"]]
# size 5
# [["b", "e", "a", "r", "s"]]
power(input, ac)
#=> [[], ["s"], ["r"], ["r", "s"], ["a"], ["a", "s"], ["a", "r"],
# ["a", "r", "s"], ["e"], ["e", "s"], ["e", "r"], ["e", "r", "s"],
# ["e", "a"], ["e", "a", "s"], ["e", "a", "r"], ["e", "a", "r", "s"],
# ["b"], ["b", "s"], ["b", "r"], ["b", "r", "s"], ["b", "a"],
# ["b", "a", "s"], ["b", "a", "r"], ["b", "a", "r", "s"], ["b", "e"],
# ["b", "e", "s"], ["b", "e", "r"], ["b", "e", "r", "s"], ["b", "e", "a"],
# ["b", "e", "a", "s"], ["b", "e", "a", "r"], ["b", "e", "a", "r", "s"]]
include Methods
@methods = Methods.public_instance_methods(false)
#=> [:ruby, :todd, :fl00r, :cary]
require 'benchmark'
@indent = methods.map { |m| m.to_s.size }.max
[10, 15, 20].each do |n|
puts "\nn = #{n}"
arr = test_array(n)
Benchmark.bm(@indent) do |bm|
@methods.each do |m|
bm.report m.to_s do
compute(arr, m).size
end
end
end
end
示例
def all_combos(sz)
[*(0..2**sz-1)].map { |i| ("%0#{sz}b" % i).chars }
.map { |a| a.each_with_index
.select { |n,ndx| n=="1" }.map(&:last) }
end
def combos(input, n, all_combos)
all_combos.select { |c| c.size == n }.map { |c| input.values_at(*c) }
end
def power(input, all_combos)
all_combos.map { |c| input.values_at(*c) }
end
input = %w{b e a r s}
#=> ["b", "e", "a", "r", "s"]
ac = all_combos(input.size)
#=> [[], [4], [3], [3, 4], [2], [2, 4], [2, 3], [2, 3, 4],
# [1], [1, 4], [1, 3], [1, 3, 4], [1, 2], [1, 2, 4], [1, 2, 3],
# [1, 2, 3, 4], [0], [0, 4], [0, 3], [0, 3, 4], [0, 2], [0, 2, 4],
# [0, 2, 3], [0, 2, 3, 4], [0, 1], [0, 1, 4], [0, 1, 3], [0, 1, 3, 4],
# [0, 1, 2], [0, 1, 2, 4], [0, 1, 2, 3], [0, 1, 2, 3, 4]]
(0..input.size).each { |i| puts "size #{i}"; p combos(input, i, ac) }
# size 0
# [[]]
# size 1
# [["s"], ["r"], ["a"], ["e"], ["b"]]
# size 2
# [["r", "s"], ["a", "s"], ["a", "r"], ["e", "s"], ["e", "r"],
# ["e", "a"], ["b", "s"], ["b", "r"], ["b", "a"], ["b", "e"]]
# size 3
# [["a", "r", "s"], ["e", "r", "s"], ["e", "a", "s"], ["e", "a", "r"],
# ["b", "r", "s"], ["b", "a", "s"], ["b", "a", "r"], ["b", "e", "s"],
# ["b", "e", "r"], ["b", "e", "a"]]
# size 4
# [["e", "a", "r", "s"], ["b", "a", "r", "s"], ["b", "e", "r", "s"],
# ["b", "e", "a", "s"], ["b", "e", "a", "r"]]
# size 5
# [["b", "e", "a", "r", "s"]]
power(input, ac)
#=> [[], ["s"], ["r"], ["r", "s"], ["a"], ["a", "s"], ["a", "r"],
# ["a", "r", "s"], ["e"], ["e", "s"], ["e", "r"], ["e", "r", "s"],
# ["e", "a"], ["e", "a", "s"], ["e", "a", "r"], ["e", "a", "r", "s"],
# ["b"], ["b", "s"], ["b", "r"], ["b", "r", "s"], ["b", "a"],
# ["b", "a", "s"], ["b", "a", "r"], ["b", "a", "r", "s"], ["b", "e"],
# ["b", "e", "s"], ["b", "e", "r"], ["b", "e", "r", "s"], ["b", "e", "a"],
# ["b", "e", "a", "s"], ["b", "e", "a", "r"], ["b", "e", "a", "r", "s"]]
include Methods
@methods = Methods.public_instance_methods(false)
#=> [:ruby, :todd, :fl00r, :cary]
require 'benchmark'
@indent = methods.map { |m| m.to_s.size }.max
[10, 15, 20].each do |n|
puts "\nn = #{n}"
arr = test_array(n)
Benchmark.bm(@indent) do |bm|
@methods.each do |m|
bm.report m.to_s do
compute(arr, m).size
end
end
end
end
此实现类似于二进制递归计数:
def combinations(items)
return [] unless items.any?
prefix = items[0]
suffixes = combinations(items[1..-1])
[[prefix]] + suffixes + suffixes.map {|item| [prefix] + item }
end
> combinations(%w(a b c))
=> [["a"], ["b"], ["c"], ["b", "c"], ["a", "b"], ["a", "c"], ["a", "b", "c"]]
在每个阶段,组合都是以下各项的串联:
- 仅第一个元素
- 以下元素的组合(元素1..n-1)
- 第一个元素与以下元素的组合组合
先生们,启动你们的引擎
比较的方法
module Methods
def ruby(array)
(0..array.size).each_with_object([]) { |i,a|
a.concat(array.combination(i).to_a) }
end
测试数据
def test_array(n)
[*1..n]
end
助手
def compute(arr, meth)
send(meth, arr)
end
def compute_sort(arr, meth)
compute(arr, meth).map(&:sort).sort
end
arr = test_array(8)
a = compute_sort(arr, @methods.first)
puts @methods[1..-1].all? { |m| a == compute_sort(arr, m) }
#=> true
n = 10
user system total real
ruby 0.000000 0.000000 0.000000 ( 0.000312)
todd 0.000000 0.000000 0.000000 ( 0.001611)
fl00r 0.000000 0.000000 0.000000 ( 0.002675)
cary 0.010000 0.000000 0.010000 ( 0.010026)
n = 15
user system total real
ruby 0.010000 0.000000 0.010000 ( 0.010742)
todd 0.070000 0.010000 0.080000 ( 0.081821)
fl00r 0.080000 0.000000 0.080000 ( 0.076030)
cary 0.430000 0.020000 0.450000 ( 0.450382)
n = 20
user system total real
ruby 0.310000 0.040000 0.350000 ( 0.350484)
todd 2.360000 0.130000 2.490000 ( 2.487493)
fl00r 2.320000 0.090000 2.410000 ( 2.405377)
cary 21.420000 0.620000 22.040000 ( 22.053303)
包括模块
def all_combos(sz)
[*(0..2**sz-1)].map { |i| ("%0#{sz}b" % i).chars }
.map { |a| a.each_with_index
.select { |n,ndx| n=="1" }.map(&:last) }
end
def combos(input, n, all_combos)
all_combos.select { |c| c.size == n }.map { |c| input.values_at(*c) }
end
def power(input, all_combos)
all_combos.map { |c| input.values_at(*c) }
end
input = %w{b e a r s}
#=> ["b", "e", "a", "r", "s"]
ac = all_combos(input.size)
#=> [[], [4], [3], [3, 4], [2], [2, 4], [2, 3], [2, 3, 4],
# [1], [1, 4], [1, 3], [1, 3, 4], [1, 2], [1, 2, 4], [1, 2, 3],
# [1, 2, 3, 4], [0], [0, 4], [0, 3], [0, 3, 4], [0, 2], [0, 2, 4],
# [0, 2, 3], [0, 2, 3, 4], [0, 1], [0, 1, 4], [0, 1, 3], [0, 1, 3, 4],
# [0, 1, 2], [0, 1, 2, 4], [0, 1, 2, 3], [0, 1, 2, 3, 4]]
(0..input.size).each { |i| puts "size #{i}"; p combos(input, i, ac) }
# size 0
# [[]]
# size 1
# [["s"], ["r"], ["a"], ["e"], ["b"]]
# size 2
# [["r", "s"], ["a", "s"], ["a", "r"], ["e", "s"], ["e", "r"],
# ["e", "a"], ["b", "s"], ["b", "r"], ["b", "a"], ["b", "e"]]
# size 3
# [["a", "r", "s"], ["e", "r", "s"], ["e", "a", "s"], ["e", "a", "r"],
# ["b", "r", "s"], ["b", "a", "s"], ["b", "a", "r"], ["b", "e", "s"],
# ["b", "e", "r"], ["b", "e", "a"]]
# size 4
# [["e", "a", "r", "s"], ["b", "a", "r", "s"], ["b", "e", "r", "s"],
# ["b", "e", "a", "s"], ["b", "e", "a", "r"]]
# size 5
# [["b", "e", "a", "r", "s"]]
power(input, ac)
#=> [[], ["s"], ["r"], ["r", "s"], ["a"], ["a", "s"], ["a", "r"],
# ["a", "r", "s"], ["e"], ["e", "s"], ["e", "r"], ["e", "r", "s"],
# ["e", "a"], ["e", "a", "s"], ["e", "a", "r"], ["e", "a", "r", "s"],
# ["b"], ["b", "s"], ["b", "r"], ["b", "r", "s"], ["b", "a"],
# ["b", "a", "s"], ["b", "a", "r"], ["b", "a", "r", "s"], ["b", "e"],
# ["b", "e", "s"], ["b", "e", "r"], ["b", "e", "r", "s"], ["b", "e", "a"],
# ["b", "e", "a", "s"], ["b", "e", "a", "r"], ["b", "e", "a", "r", "s"]]
include Methods
@methods = Methods.public_instance_methods(false)
#=> [:ruby, :todd, :fl00r, :cary]
require 'benchmark'
@indent = methods.map { |m| m.to_s.size }.max
[10, 15, 20].each do |n|
puts "\nn = #{n}"
arr = test_array(n)
Benchmark.bm(@indent) do |bm|
@methods.each do |m|
bm.report m.to_s do
compute(arr, m).size
end
end
end
end
确认方法返回相同的值
def compute(arr, meth)
send(meth, arr)
end
def compute_sort(arr, meth)
compute(arr, meth).map(&:sort).sort
end
arr = test_array(8)
a = compute_sort(arr, @methods.first)
puts @methods[1..-1].all? { |m| a == compute_sort(arr, m) }
#=> true
n = 10
user system total real
ruby 0.000000 0.000000 0.000000 ( 0.000312)
todd 0.000000 0.000000 0.000000 ( 0.001611)
fl00r 0.000000 0.000000 0.000000 ( 0.002675)
cary 0.010000 0.000000 0.010000 ( 0.010026)
n = 15
user system total real
ruby 0.010000 0.000000 0.010000 ( 0.010742)
todd 0.070000 0.010000 0.080000 ( 0.081821)
fl00r 0.080000 0.000000 0.080000 ( 0.076030)
cary 0.430000 0.020000 0.450000 ( 0.450382)
n = 20
user system total real
ruby 0.310000 0.040000 0.350000 ( 0.350484)
todd 2.360000 0.130000 2.490000 ( 2.487493)
fl00r 2.320000 0.090000 2.410000 ( 2.405377)
cary 21.420000 0.620000 22.040000 ( 22.053303)
基准代码
def all_combos(sz)
[*(0..2**sz-1)].map { |i| ("%0#{sz}b" % i).chars }
.map { |a| a.each_with_index
.select { |n,ndx| n=="1" }.map(&:last) }
end
def combos(input, n, all_combos)
all_combos.select { |c| c.size == n }.map { |c| input.values_at(*c) }
end
def power(input, all_combos)
all_combos.map { |c| input.values_at(*c) }
end
input = %w{b e a r s}
#=> ["b", "e", "a", "r", "s"]
ac = all_combos(input.size)
#=> [[], [4], [3], [3, 4], [2], [2, 4], [2, 3], [2, 3, 4],
# [1], [1, 4], [1, 3], [1, 3, 4], [1, 2], [1, 2, 4], [1, 2, 3],
# [1, 2, 3, 4], [0], [0, 4], [0, 3], [0, 3, 4], [0, 2], [0, 2, 4],
# [0, 2, 3], [0, 2, 3, 4], [0, 1], [0, 1, 4], [0, 1, 3], [0, 1, 3, 4],
# [0, 1, 2], [0, 1, 2, 4], [0, 1, 2, 3], [0, 1, 2, 3, 4]]
(0..input.size).each { |i| puts "size #{i}"; p combos(input, i, ac) }
# size 0
# [[]]
# size 1
# [["s"], ["r"], ["a"], ["e"], ["b"]]
# size 2
# [["r", "s"], ["a", "s"], ["a", "r"], ["e", "s"], ["e", "r"],
# ["e", "a"], ["b", "s"], ["b", "r"], ["b", "a"], ["b", "e"]]
# size 3
# [["a", "r", "s"], ["e", "r", "s"], ["e", "a", "s"], ["e", "a", "r"],
# ["b", "r", "s"], ["b", "a", "s"], ["b", "a", "r"], ["b", "e", "s"],
# ["b", "e", "r"], ["b", "e", "a"]]
# size 4
# [["e", "a", "r", "s"], ["b", "a", "r", "s"], ["b", "e", "r", "s"],
# ["b", "e", "a", "s"], ["b", "e", "a", "r"]]
# size 5
# [["b", "e", "a", "r", "s"]]
power(input, ac)
#=> [[], ["s"], ["r"], ["r", "s"], ["a"], ["a", "s"], ["a", "r"],
# ["a", "r", "s"], ["e"], ["e", "s"], ["e", "r"], ["e", "r", "s"],
# ["e", "a"], ["e", "a", "s"], ["e", "a", "r"], ["e", "a", "r", "s"],
# ["b"], ["b", "s"], ["b", "r"], ["b", "r", "s"], ["b", "a"],
# ["b", "a", "s"], ["b", "a", "r"], ["b", "a", "r", "s"], ["b", "e"],
# ["b", "e", "s"], ["b", "e", "r"], ["b", "e", "r", "s"], ["b", "e", "a"],
# ["b", "e", "a", "s"], ["b", "e", "a", "r"], ["b", "e", "a", "r", "s"]]
include Methods
@methods = Methods.public_instance_methods(false)
#=> [:ruby, :todd, :fl00r, :cary]
require 'benchmark'
@indent = methods.map { |m| m.to_s.size }.max
[10, 15, 20].each do |n|
puts "\nn = #{n}"
arr = test_array(n)
Benchmark.bm(@indent) do |bm|
@methods.each do |m|
bm.report m.to_s do
compute(arr, m).size
end
end
end
end
测试(秒)
我只得出一个明确的结论。使用数组置换如何?对不起,我没有全部读过。我知道你想写一个自定义的。祝你好运您可能必须使用递归方法。微小的遗漏,很容易修复:no[]
。将删除此注释。@CarySwoveland您能想出一种方法在结果中包含[]
,而不使代码量增加一倍吗?我不能!一个简单的方法是像我在添加的基准测试中所做的那样,在这里我创建了一个调用置换
的方法,然后将[]
添加到返回的数组中。顺便说一句,permutations
在这里不是一个好名字,因为您正在计算组合。(如果[1,2,3]
是排列的一个元素,那么[1,3,2]
,[2,1,3]
,[2,3,1]
,[3,1,2]
和[3,2,1]
)@CarySwoveland关于排列与组合的好观点——高中数学太早了!关于[]
:我正在寻找一种方法来改变算法,使其自然地包含[]
,但我仍然无法找到它。您需要一个小的修复:p组合(a,0)=>[[“a”]]
做得好!了解不同方法的性能总是很有趣的。对于GRIN,与数组#组合的本机实现相比如何?(你可能需要增加n来注册一些东西。)好主意。我加上去了。