部分笛卡尔积(确保每组中的一个值)

时间:2014-01-25 05:24:03

标签: ruby cartesian-product

给定一组项目[z,a,b,c]我希望找到“笛卡尔幂”(笛卡尔积与 n 次)但只有那些{{1>的结果在他们中。例如:

z

我可以通过旋转完整集并跳过没有特殊值的条目来实现这一点,但我想知道是否有更简单的方法。最好是允许我只懒得评估所需条目,而不浪费时间计算不需要的条目。

normal_values = ["a","b","c"]
p limited_cartesian( normal_values, "z", 2 )
#=> [
#=>   ["z", "z"]
#=>   ["z", "a"]
#=>   ["z", "b"]
#=>   ["z", "c"]
#=>   ["a", "z"]
#=>   ["b", "z"]
#=>   ["c", "z"]
#=> ]

1 个答案:

答案 0 :(得分:2)

编辑:使用v3.0,我终于有了一些可敬的东西。在这种情况下,关键是以正确的方式看待问题。在我看来,我可以反复置换normal_values << specialpower - 1次,然后对于每个排列,还有一个要添加的元素。如果排列包含至少一个special,则可以添加normal_values << special的任何元素;否则,必须添加special

def limited_cartesian( values, special, power )
  all_vals = values + [special]
  all_vals.repeated_permutation(power-1).map do |p|
    if p.include?(special)
      *all_vals.each_with_object([]) { |v,a| a << (p + [v]) }
    else
      p + [special]
    end
  end       
end

limited_cartesian( values, 'z', 1 )
  # [["z"]]

limited_cartesian( values, 'z', 2 )
  # => [["a", "z"], ["b", "z"], ["c", "z"],
  #     ["z", "a"], ["z", "b"], ["z", "c"],
  #     ["z", "z"]] 

limited_cartesian( values, 'z', 3 )
  # => [["a", "a", "z"], ["a", "b", "z"], ["a", "c", "z"],
  #     ["a", "z", "a"], ["a", "z", "b"], ["a", "z", "c"],
  #     ["a", "z", "z"], ["b", "a", "z"], ["b", "b", "z"],
  #     ["b", "c", "z"], ["b", "z", "a"], ["b", "z", "b"],
  #     ["b", "z", "c"], ["b", "z", "z"], ["c", "a", "z"],
  #     ["c", "b", "z"], ["c", "c", "z"], ["c", "z", "a"],
  #     ["c", "z", "b"], ["c", "z", "c"], ["c", "z", "z"],
  #     ["z", "a", "a"], ["z", "a", "b"], ["z", "a", "c"],
  #     ["z", "a", "z"], ["z", "b", "a"], ["z", "b", "b"],
  #     ["z", "b", "c"], ["z", "b", "z"], ["z", "c", "a"],
  #     ["z", "c", "b"], ["z", "c", "c"], ["z", "c", "z"],
  #     ["z", "z", "a"], ["z", "z", "b"], ["z", "z", "c"],
  #     ["z", "z", "z"]] 

这是我的v2.1,它有效但不漂亮。我会把它记录下来。

def limited_cartesian( values, special, power )
  ndx = Array(0...power)
  ndx[1..-1].each_with_object( [[special]*power] ) do |i,a|
    ndx.combination(i).to_a.product(values.repeated_permutation(power-i).to_a)
       .each { |pos, val| a << stuff_special(special, pos, val.dup) }
  end
end

def stuff_special( special, pos, vals )
  pos.each_with_object(Array.new(pos.size + vals.size)) {|j,r|
    r[j] = special }.map {|e| e.nil? ? vals.shift : e }
end
  # e.g., stuff_special( 'z', [1,4], ["a","b","c"]) => ["a","z","b","c","z"]