在条件下的N长度序列对

时间:2017-09-25 21:44:59

标签: arrays ruby algorithm graph-theory graph-algorithm

我有以下2d水果阵列:

var camera, scene, renderer;
var geometry, material, mesh;

init();
animate();

function init() {

camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.01, 10 );
camera.position.set(20,0,20);

scene = new THREE.Scene();

// Instantiate a loader
var loader = new THREE.GLTFLoader();

// Load a glTF resource
loader.load( 'Box.gltf', function ( gltf ) {
    scene.add( gltf.scene );
    gltf.animations; // Array<THREE.AnimationClip>
    gltf.scene;      // THREE.Scene
    gltf.scenes;     // Array<THREE.Scene>
    gltf.cameras;    // Array<THREE.Camera>
} );

var light = new THREE.AmbientLight(0xffffff);
scene.add(light);

renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );

}

function animate() {
  requestAnimationFrame( animate );
  renderer.render( scene, camera );

}

任务是在条件之后找到N长度的对序列:

  1. 第一对和最后一对应包含fruits = [["apple", "lemon"],["apple", "cucumber"],["carrot", "lemon"],["carrot", "cucumber"],["peach", "cucumber"],["lemon", "melon"],["grape", "cucumber"],["lime", "lemon"],["lime", "cucumber"],["apricot", "lemon"],["apricot", "cucumber"],["avocado", "cucumber"],["banana", "lemon"],["banana", "cucumber"],["blackberry", "lemon"],["blackberry", "cucumber"],["blackberry", "prune"],["blueberry", "lemon"],["blueberry", "melon"],["blueberry", "cucumber"],["cherry", "lemon"],["cherry", "cucumber"],["papaya", "lemon"],["plum", "lemon"],["plum", "cucumber"],["feijoa", "lemon"],["fig", "lemon"],["fig", "cucumber"],["eggplant", "lemon"],["eggplant", "cucumber"],["raspberry", "lemon"],["raspberry", "cucumber"],["cranberry", "lemon"],["cranberry", "cucumber"],["pomelo", "lemon"],["pomelo", "cucumber"],["orange", "lemon"],["orange", "cucumber"],["olive", "cucumber"],["gooseberry", "lemon"],["guava", "lemon"],["guava", "blueberry"],["guava", "cucumber"],["redcurrant", "lemon"],["redcurrant", "cucumber"],["pomergranate", "lemon"],["pomergranate", "cucumber"],["nectarine", "cucumber"],["mulberry", "lemon"],["mulberry", "cucumber"],["dragonfruit", "lemon"],["dragonfruit", "cucumber"],["pear", "lemon"],["cucumber", "lemon"],["cucumber", "blueberry"],["cucumber", "salmonberry"],["cucumber", "melon"],["prune", "lemon"],["prune", "guava"],["prune", "cucumber"],["kiwi", "cucumber"],["mangosteen", "cucumber"],["jujube", "lemon"],["jujube", "cucumber"],["clementine", "lemon"],["clementine", "blueberry"],["clementine", "cucumber"],["tangerine", "lemon"],["tangerine", "cucumber"],["pea", "lemon"],["pea", "cucumber"],["tomato", "cucumber"],["yuzu", "cucumber"]]
  2. 第一个和最后一个之间的对不应包含"lemon"
  3. 每两个连续两对应包含一个共同元素。
  4. 每个连续的N-1对,其中N不是3,不应包含所有这些对中共有的任何元素。
  5. N = 4的例子:

    "lemon"

    我为N = 3和4编写了这段代码,

    [["apple","lemon"], ["apple","cucumber"], ["clementine","cucumber"], ["clementine","lemon"]]
    [["lime","lemon"], ["lime","cucumber"], ["pomelo","cucumber"], ["pomelo","lemon"]]
    [["banana","lemon"], ["banana","cucumber"], ["pomergranate","cucumber"], ["pomergranate","lemon"]]
    

    但我觉得这不是最好的方法。是否有任何元编程技巧可以使其适用于任何N长度?

4 个答案:

答案 0 :(得分:1)

以下将强制执行n不受限制的所有规则(未检查n&lt; 3)。

第一条和第二条规则很简单。第三和第四条规则的大部分工作都是由each_cons完成的,它将获得所需的数组,以便独立于n。

pairs.permutation(n).select do |ar|
  lemon_bracketed = (ar.first & ar.last).include?('lemon')
  no_squeeze = !ar[1..-2].include?('lemon')
  consecutive_pair = ar.each_cons(2).map { |x| x }.none { |first, last| (first & last).empty? }
  consecutive_n = n == 3 || 
                  ar.each_cons(n-1)
                    .map { |x| x }
                    .all? { |group| group[1..-1].reduce(group[0]) { |combination, additional| combination & additional } }

  lemon_bracketed && no_squeeze && consecutive_pair && consecutive_n
end

虽然这有效但速度很慢。但是pairs.permutation(n)部分的表现似乎丢失了,所以这里没有太多可以做的。

答案 1 :(得分:1)

是的,您可以为任意length参数执行强力解决方案:

class FruitCombinations

  PAIRS = [["apple", "lemon"],["apple", "cucumber"],["carrot", "lemon"],["carrot", "cucumber"],["peach", "cucumber"],["lemon", "melon"],["grape", "cucumber"],["lime", "lemon"],["lime", "cucumber"],["apricot", "lemon"],["apricot", "cucumber"],["avocado", "cucumber"],["banana", "lemon"],["banana", "cucumber"],["blackberry", "lemon"],["blackberry", "cucumber"],["blackberry", "prune"],["blueberry", "lemon"],["blueberry", "melon"],["blueberry", "cucumber"],["cherry", "lemon"],["cherry", "cucumber"],["papaya", "lemon"],["plum", "lemon"],["plum", "cucumber"],["feijoa", "lemon"],["fig", "lemon"],["fig", "cucumber"],["eggplant", "lemon"],["eggplant", "cucumber"],["raspberry", "lemon"],["raspberry", "cucumber"],["cranberry", "lemon"],["cranberry", "cucumber"],["pomelo", "lemon"],["pomelo", "cucumber"],["orange", "lemon"],["orange", "cucumber"],["olive", "cucumber"],["gooseberry", "lemon"],["guava", "lemon"],["guava", "blueberry"],["guava", "cucumber"],["redcurrant", "lemon"],["redcurrant", "cucumber"],["pomergranate", "lemon"],["pomergranate", "cucumber"],["nectarine", "cucumber"],["mulberry", "lemon"],["mulberry", "cucumber"],["dragonfruit", "lemon"],["dragonfruit", "cucumber"],["pear", "lemon"],["cucumber", "lemon"],["cucumber", "blueberry"],["cucumber", "salmonberry"],["cucumber", "melon"],["prune", "lemon"],["prune", "guava"],["prune", "cucumber"],["kiwi", "cucumber"],["mangosteen", "cucumber"],["jujube", "lemon"],["jujube", "cucumber"],["clementine", "lemon"],["clementine", "blueberry"],["clementine", "cucumber"],["tangerine", "lemon"],["tangerine", "cucumber"],["pea", "lemon"],["pea", "cucumber"],["tomato", "cucumber"],["yuzu", "cucumber"]]

  def self.list_valid(length)
    PAIRS.permutation(length).select do |pair_permutation|
      first = pair_permutation[0]
      middle = pair_permutation[1..-2]
      last = pair_permutation[-1]
      first & last == ['lemon'] &&
          middle.none? { |pair| pair.include?('lemon') } &&
          !(first & middle[0]).empty? &&
          !(middle[-1] & last).empty? &&
          middle.each_cons(2).none? { |pair_of_pairs| (pair_of_pairs[0] & pair_of_pairs[-1]).empty? }
    end
  end
end

请注意,对于长度== 4,这是非常慢的,对于更长的长度,它会更慢。有人指出,递归解决方案会更有效率。

答案 2 :(得分:1)

这是一个递归解决方案,它直接构造所需的数组,而不是构造更大的排列数组,然后删除不满足所有规则的元素。

<强>代码

def combos(n, fruits)
  with_lemon, without_lemon = fruits.partition { |a| a.include?("lemon") }
  return [] if n < 2 || with_lemon.size < 2 || without_lemon.size < n - 2
  with_lemon.each_with_object([]) do |pair, arr|
    recurse(n, n-1, with_lemon - [pair], without_lemon, pair, pair, fruits.flatten).
      each { |a| arr << ([pair] + a) }
  end
end

def recurse(n, n_left, with_lemon, without_lemon, last, in_all_from_1st, in_all_from_2nd)
  if n_left == 1
    with = with_lemon.select { |pair| (last & pair).any? }
    with.select! { |pair| (in_all_from_2nd & pair).empty? } unless n <= 3
    with.map { |pair| [pair] }
  else
    without = without_lemon.select { |pair| (last & pair).any? }
    return [] if without.empty?
    without.each_with_object([]) do |pair, arr|
      if n <= 3 || n_left > 2 || (in_all_from_1st & pair).empty?
        recurse(n, n_left-1, with_lemon, without_lemon-[pair],
          pair, in_all_from_1st, in_all_from_2nd & pair).
          each { |a| arr << ([pair] + a) unless a.empty? }
      end
    end
  end
end

<强>实施例

为了使示例更易于管理,我在OP给出的示例中选择了73对中的8对。

fruits = [["tomato", "cucumber"], ["fig", "lemon"], ["tomato", "lemon"],
          ["cucumber", "fig"], ["fig", "tomato"], ["lemon", "cucumber"], 
          ["pomergranate", "cucumber"], ["lemon", "fig"]]

首先,我要定义一个帮助器。此方法返回一对[f,n],其中f是水果,nf出现的2元组的数量,n是最大的数字任何水果的外观。

def max_nbr_appearances(n, fruits)
  combos(n, fruits).map do |arr|
    arr.map(&:uniq).flatten.group_by(&:itself).
        transform_values { |v| v.size }.max_by(&:last)
  end.max_by(&:last)
end

n = 2

combos(2, fruits)
  #=> [[["fig", "lemon"], ["tomato", "lemon"]],
  #    [["fig", "lemon"], ["lemon", "cucumber"]],
  #    [["fig", "lemon"], ["lemon", "fig"]], 
  #    [["tomato", "lemon"], ["fig", "lemon"]],
  #    [["tomato", "lemon"], ["lemon", "cucumber"]],
  #    [["tomato", "lemon"], ["lemon", "fig"]],
  #    [["lemon", "cucumber"], ["fig", "lemon"]],
  #    [["lemon", "cucumber"], ["tomato", "lemon"]],
  #    [["lemon", "cucumber"], ["lemon", "fig"]],
  #    [["lemon", "fig"], ["fig", "lemon"]],
  #    [["lemon", "fig"], ["tomato", "lemon"]],
  #    [["lemon", "fig"], ["lemon", "cucumber"]]]

n = 3

combos(n, fruits)
  #=> [[["fig", "lemon"], ["cucumber", "fig"], ["lemon", "cucumber"]],
  ##   [["fig", "lemon"], ["cucumber", "fig"], ["lemon", "fig"]],
  #    [["fig", "lemon"], ["fig", "tomato"], ["tomato", "lemon"]],
  ##   [["fig", "lemon"], ["fig", "tomato"], ["lemon", "fig"]],
  #    [["tomato", "lemon"], ["tomato", "cucumber"], ["lemon", "cucumber"]],
  #    [["tomato", "lemon"], ["fig", "tomato"], ["fig", "lemon"]], 
  #    [["tomato", "lemon"], ["fig", "tomato"], ["lemon", "fig"]], \
  #    [["lemon", "cucumber"], ["tomato", "cucumber"], ["tomato", "lemon"]],
  #    [["lemon", "cucumber"], ["cucumber", "fig"], ["fig", "lemon"]], 
  #    [["lemon", "cucumber"], ["cucumber", "fig"], ["lemon", "fig"]], 
  ##   [["lemon", "fig"], ["cucumber", "fig"], ["fig", "lemon"]],
  #    [["lemon", "fig"], ["cucumber", "fig"], ["lemon", "cucumber"]],
  #    [["lemon", "fig"], ["fig", "tomato"], ["fig", "lemon"]],
  #    [["lemon", "fig"], ["fig", "tomato"], ["tomato", "lemon"]]]

开头**以上的行有一个水果出现在所有三个2元组中(始终为"fig")。这是允许的,因为n = 3 <= 3

n = 4

combos(n, fruits)
  #=> [[["fig", "lemon"], ["cucumber", "fig"], ["tomato", "cucumber"],
  #     ["tomato", "lemon"]],
  #    [["fig", "lemon"], ["fig", "tomato"], ["tomato", "cucumber"],
  #     ["lemon", "cucumber"]],
  #    [["tomato", "lemon"], ["tomato", "cucumber"], ["cucumber", "fig"],
  #     ["fig", "lemon"]],
  #    [["tomato", "lemon"], ["tomato", "cucumber"], ["cucumber", "fig"],
  #     ["lemon", "fig"]],
  #    [["tomato", "lemon"], ["fig", "tomato"], ["cucumber", "fig"],
  #     ["lemon", "cucumber"]],
  #    [["lemon", "cucumber"], ["tomato", "cucumber"], ["fig", "tomato"],
  #     ["fig", "lemon"]],
  #    [["lemon", "cucumber"], ["tomato", "cucumber"], ["fig", "tomato"],
  #     ["lemon", "fig"]],
  #    [["lemon", "cucumber"], ["cucumber", "fig"], ["fig", "tomato"],
  #     ["tomato", "lemon"]],
  #    [["lemon", "fig"], ["cucumber", "fig"], ["tomato", "cucumber"],
  #     ["tomato", "lemon"]],
  #    [["lemon", "fig"], ["fig", "tomato"], ["tomato", "cucumber"],
  #     ["lemon", "cucumber"]]]
combos(n, fruits).size
  #=> 10
max_nbr_appearances(n, fruits)
  #=> ["fig", 2]    

最后的计算显示&#34;无花果&#34;在(至少)一个排列中出现两次并且没有水果(包括&#34;无花果&#34;)在排列中出现两次以上。 (关于外观最多的关系很常见。我只显示了一个。)因此最后一个条件得到满足。

n = 5

combos(n, fruits)
  #=> [[["fig", "lemon"], ["cucumber", "fig"], ["fig", "tomato"],
  #     ["tomato", "cucumber"], ["tomato", "lemon"]],
  #    [["fig", "lemon"], ["cucumber", "fig"], ["fig", "tomato"],
  #     ["tomato", "cucumber"], ["lemon", "cucumber"]],
  #    ...
  #    [["lemon", "fig"], ["fig", "tomato"], ["cucumber", "fig"],
  #     ["pomergranate", "cucumber"], ["lemon", "cucumber"]]]
combos(n,fruits).size
  #=> 36
max_nbr_appearances(n, fruits)
  #=> ["fig", 3]

n = 6

combos(n,fruits)
  #=> [[["fig", "lemon"], ["cucumber", "fig"], ["fig", "tomato"], 
  #     ["tomato", "cucumber"], ["pomergranate", "cucumber"], ["lemon", "cucumber"]],
  #    [["fig", "lemon"], ["fig", "tomato"], ["tomato", "cucumber"],
  # ["cucumber", "fig"], ["pomergranate", "cucumber"], ["lemon", "cucumber"]],
  #    ...
  #    [["lemon", "fig"], ["fig", "tomato"], ["cucumber", "fig"],
  # ["pomergranate", "cucumber"], ["tomato", "cucumber"], ["lemon", "cucumber"]]]
combos(n,fruits).size
  #=> 28
max_nbr_appearances(n, fruits)
  #=> ["cucumber", 4]

n = 7

combos(7,fruits)
  #=> []

对于问题中给出的fruits fruits.size #=> 73,获得了以下结果。

require 'time'

def ops_fruits(n, fruits)
  t = Time.now
  puts "\ncombos(#{n}, fruits).size = %d, elapsed seconds = %d" %
    [combos(n, fruits).size, Time.now - t]
  puts "max_nbr_appearances(#{n}, fruits) = #{max_nbr_appearances(n, fruits)}"
end

ops_fruits(3, fruits)
combos(3, fruits).size = 64, elapsed seconds = 0
max_nbr_appearances(3, fruits) = ["apple", 2]

ops_fruits(4, fruits)
combos(4, fruits).size = 736, elapsed seconds = 0
max_nbr_appearances(4, fruits) = ["apple", 2]

ops_fruits(5, fruits) 
combos(5, fruits).size = 27822, elapsed seconds = 1
max_nbr_appearances(5, fruits) = ["cucumber", 3]

ops_fruits(6, fruits)
combos(6, fruits).size = 952504, elapsed seconds = 60
max_nbr_appearances(6, fruits) = ["cucumber", 4]

答案 3 :(得分:1)

摘要:

数据结构是一个图形 - 每个水果都是一个顶点,数组中的对是它们之间的边。

要解决此问题,请执行深度优先搜索。

  

第一对和最后一对应该包含“柠檬”   第一个和最后一个之间的对不应该包含“柠檬”。

  • DFS应植根于"lemon"节点(级别0)。
  • DFS的下降速度不应超过N
  • DFS应再次访问"lemon"节点,除非它处于N级别。
  

每两个连续两对应包含一个共同元素。

  • 这是图数据结构的隐含。
  

每个连续的N-1对,其中N不是3,不应包含所有这些对中共有的任何元素。

  • 在顶点是否已被访问的每个顶点(果实)上保持一个标志 - 如果没有,则DFS可以遍历边缘并将其标记为已访问(并且一旦您回溯以找到其他路径,则标记它没有访问,所以这些路径可以访问它。)