Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/database/8.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
如何在javascript中将集合的元素分组为不相交的子集?_Javascript_Permutation_Combinations - Fatal编程技术网

如何在javascript中将集合的元素分组为不相交的子集?

如何在javascript中将集合的元素分组为不相交的子集?,javascript,permutation,combinations,Javascript,Permutation,Combinations,一个9人的小组可以在3个不相交的2人、3人和4人小组中以多少种方式工作?我如何通过javascript回溯生成所有的可能性 例如: Gs = group([aldo,beat,carla,david,evi,flip,gary,hugo,ida],[2,2,5]); console.log(Gs); // [[aldo,beat],[carla,david],[evi,flip,gary,hugo,ida]], ... 请注意,我不想要组成员的排列;i、 e.[aldo,beat],…]是与

一个9人的小组可以在3个不相交的2人、3人和4人小组中以多少种方式工作?我如何通过javascript回溯生成所有的可能性

例如:

Gs = group([aldo,beat,carla,david,evi,flip,gary,hugo,ida],[2,2,5]);

console.log(Gs); // [[aldo,beat],[carla,david],[evi,flip,gary,hugo,ida]], ...
请注意,我不想要组成员的排列;i、 e.[aldo,beat],…]是与[[beat,aldo],…]相同的解决方案。然而,[[aldo,beat],[carla,david],…]和[[carla,david],[aldo,beat],…]之间有区别


*请不要使用库。

我确信有更快的公式,但我的数学从来没有那么好,如果我正确理解问题,这似乎是可行的:

function combo(r, ops){
     function unq(r){return r.filter(function(a,b,c){return !this[a] && (this[a]=1);},{}); } 

     var combos={}, pairs=[];

      r.forEach(function(a,b,c){
         combos[a]=r.filter(function not(a){return a!=this && !combos[a]}, a);
      });


      Object.keys(combos).forEach(function(k){
         combos[k].forEach(function(a){  
              pairs.push([k, a]+'');
         });     
      });

      return unq(unq( 
            pairs.map(function(a){ 
                   return unq(a.split(",")).sort(); 
              })).map(function(a){
                   return a.length==ops && a;
              }).filter(Boolean))
            .sort();

}//end combo



var r="aldo,beat,carla,david,evi,flip,gary,hugo,ida".split(",");

// find groups of different lengths:

combo(r, 2) // 2 folks ==  36 combos
combo( combo(r, 2), 3) // 3 folks == 84 combos
combo( combo( combo(r, 2), 3), 4) // 4 folks == 126 combos 

我没有费心去递归化函数,因为你只需要4-in,一个lispy调用就可以了,但是如果我不得不进一步,我想写一个额外的外包装来夹心调用…

回溯算法的核心实现很简单(见下面的函数doBacktrack)。通常情况下,复杂性在于具体回溯问题的细节

下面是我为您的问题实现的回溯算法。它基于史蒂文·斯基纳(Steven Skiena)算法设计手册(或我记忆中的)中的回溯算法描述

我没有在算法中添加修剪(因为它花费的时间比我想象的要长:),但是如果您想提高它的性能,只需为函数done()添加一个合理的实现,以防止继续处理可能被推断为不可行的候选解

function backtrack() {
  var people =
      ['aldo','beat','carla','david','evi','flip','gary','hugo','ida'];
  var initial_state =
      [[], [], []];
  var groups =
      [2, 3, 4];
  var data =
      {groups: groups, people: people, people_idx_for_name: {}};
  people.forEach(function(e, i) {
    data['people_idx_for_name'][e] = i;
  });
  var solutions = [];

  doBacktrack(initial_state, solutions, data);

  return solutions;
}

function doBacktrack(candidate, solutions, data) {
//  console.log('processing: ' + candidate);
  if (isSolution(candidate, data)) {
      processSolution(candidate, solutions);
  }
  if (done(candidate, solutions, data)) {
    return;
  }
  var new_candidates = calculateNewCandidates(candidate, data);

  for (var i=0; i<new_candidates.length; i++) {
    doBacktrack(new_candidates[i], solutions, data);
  }
}

function calculateNewCandidates(candidate, data) {
  var groups = data['groups'];
  var i = 0;
  while (i<groups.length && candidate[i].length == groups[i]) { i++; }
  if (i < groups.length) {
    //determine list of not yet selected people
    var not_yet_selected = determineNotYetSelectedPeople(candidate, data, i);

    var results = [];
    for (var j=0; j<not_yet_selected.length; j++) {
      var candidate_copy = candidate.slice(0);
      for (var k=0; k<candidate_copy.length; k++) {
        candidate_copy[k] = candidate_copy[k].slice(0);
      }
      candidate_copy[i].push(not_yet_selected[j])
      results.push(candidate_copy);
    }
    return results;

  } else {
    return [];
  }
}

function determineNotYetSelectedPeople(candidate, data, group) {
  var people = data['people'];
  var people_idx_for_name = data['people_idx_for_name'];
  var selected_people = {};
  var results = [];
  var max = -Number.MAX_VALUE;
  candidate.forEach(function(candidate_group, i) {
    candidate_group.forEach(function(already_selected_person_name) {
      var already_selected_person_idx = people_idx_for_name[already_selected_person_name];
      if (max < already_selected_person_idx && i==group) { max = already_selected_person_idx; }
      selected_people[already_selected_person_name] = true;
    });
  });
  for (var i=0; i<people.length; i++) {
    if (!selected_people[people[i]] && i > max) { results.push(people[i]); }
  }
  return results;
}

function isSolution(candidate, data) {
  var groups = data['groups'];
  for (var i=0; i<groups.length; i++) {
    if (candidate[i].length != groups[i]) {return false;}
  }
  return true;
}

function processSolution(candidate, solutions) {
  var solution = [];
  candidate.forEach(function(e) {
    var l = [];
    solution.push(l);
    e.forEach(function(f) {
      l.push(f);
    });
  });
  solutions.push(solution);
}

//use this to improve performance with prunning if possible
function done() {
  return false;
}

var solutions = backtrack();
console.log(solutions);
console.log(solutions.length);
函数回溯(){
瓦尔人=
['aldo','beat','carla','david','evi','flip','gary','hugo','ida'];
初始状态=
[[], [], []];
变量群=
[2, 3, 4];
var数据=
{groups:groups,people:people,people_idx_for_name:{};
人员。forEach(功能(e,i){
数据['people_idx_for_name'][e]=i;
});
var解决方案=[];
doBacktrack(初始状态、解决方案、数据);
返回解决方案;
}
功能doBacktrack(候选、解决方案、数据){
//console.log('processing:'+候选者);
if(解决方案(候选人、数据)){
过程解决方案(候选方案、解决方案);
}
如果(完成(候选人、解决方案、数据)){
返回;
}
var new_候选者=calculateNewCandidates(候选者,数据);

对于(var i=0;i如果您只需要将一组9人分成3个子组,每组2人、3人和4人,那么使用
C
(用于计算组合数量的函数)可以轻松地进行数学计算

  • 首先你有9个人,你需要从中选择2个人。因此你需要
    C(9,2)
  • 接下来,您有7个人,您需要从中选择3个人。因此您需要
    C(7,3)
  • 最后你有4个人,你需要从中选择4个人。因此你可以
    C(4,4)
    。但是
    C(n,n)
    总是1
  • 因此,将一组9人分成3个子组(2、3和4人)的方法数量是
    C(9、2)*C(7、3)*C(4、4)
    。这可以简化为
    C(9、2)*C(7、3)
    ,即
    36*35
    1260

    我们可以编写一个函数来计算:

    function ways(n) {
        var l = arguments.length, w = 1;
    
        for (var i = 1; i < l; i++) {
            var m = arguments[i];
            w *= combinations(n, m);
            n -= m;
        }
    
        return w;
    }
    
    最后,我们需要为
    阶乘定义函数:

    function factorial(n) {
        var f = n;
        while (--n) f *= n;
        return f;
    }
    
    然后我们计算方法的数量如下:

    alert(ways(9, 2, 3)); // 1260
    
    var groups = group([
        "aldo", "beat", "carla",
        "david", "evi", "flip",
        "gary", "hugo", "ida"
    ], [2, 3]);
    
    您可以在此处看到演示:

    注意,我们不需要指定最后一个4人的子组,因为这是隐含的


    但是我相信您希望生成每种可能的方法。这是运算符最适合的类型。因此,我们要做的第一件事是用JavaScript编写
    amb
    运算符:

    function amb(options, callback) {
        var length = options.length;
    
        for (var i = 0; i < length; i++) {
            try {
                callback(options[i]);                       // try the next option
                return;                                     // no problem, quit
            } catch (e) {
                continue;                                   // problem, next
            }
        }
    
        throw new Error("amb tree exhausted");              // throw a tantrum
    }
    
    现在您可以按如下方式使用它:

    alert(ways(9, 2, 3)); // 1260
    
    var groups = group([
        "aldo", "beat", "carla",
        "david", "evi", "flip",
        "gary", "hugo", "ida"
    ], [2, 3]);
    
    再次注意,您不需要指定最后一个4人的子组,因为它是隐含的

    现在让我们看看输出是否如我们预期的那样:

    console.log(groups.length === ways(9, 2, 3)); // true
    
    好了,一共有1260种方法可以把9个人分成3个小组,每个小组由2人、3人和4人组成


    现在我知道我的
    group
    函数看起来有点吓人,但实际上非常简单。试着阅读它并理解发生了什么

    假设你是9个人的老板。你如何将他们分为3个子组,2人、3人和4人?这正是我的
    功能的工作方式

    如果您过了一段时间仍然无法理解逻辑,那么我将更新我的答案,并详细解释
    函数。祝您好运


    顺便说一句,我刚刚意识到,对于这个问题,您实际上不需要
    amb
    。您只需使用
    forEach
    。由于没有try-catch块,生成的代码会更快:

    function group(options, divisions) {
        var subgroup = [], groups = [], n = 0;
        var indices = getIndices(options.length);
        var division = divisions.shift(), remaining = divisions.length;
        indices.forEach(select);
        return groups;
    
        function select(index) {
            subgroup.push(index);
    
            if (++n < division) indices.slice(index + 1).forEach(select);
            else {
                var subgroups = pick(options, subgroup);
    
                if (remaining) {
                    var children = group(subgroups.pop(), divisions.slice());
                    var length = children.length;
                    for (var i = 0; i < length; i++)
                        groups.push(subgroups.concat(children[i]));
                } else groups.push(subgroups);
            }
    
            subgroup.pop();
            n--;
        }
    }
    
    功能组(选项、分区){
    变量子组=[],组=[],n=0;
    var指数=GetIndex(options.length);
    var division=divisions.shift(),剩余=divisions.length;
    forEach(select);
    返回组;
    功能选择(索引){
    亚组push(索引);
    如果(++n
    由于我们不再使用
    amb
    ,程序的执行时间减少了十倍。请自行查看结果:


    此外,我最终创建了上述程序的演示小提琴:

    你不想使用库,所以你基本上是在寻找算法?你尝试过什么吗?你可以从或中获得一些灵感
    console.log(groups.length === ways(9, 2, 3)); // true
    
    function group(options, divisions) {
        var subgroup = [], groups = [], n = 0;
        var indices = getIndices(options.length);
        var division = divisions.shift(), remaining = divisions.length;
        indices.forEach(select);
        return groups;
    
        function select(index) {
            subgroup.push(index);
    
            if (++n < division) indices.slice(index + 1).forEach(select);
            else {
                var subgroups = pick(options, subgroup);
    
                if (remaining) {
                    var children = group(subgroups.pop(), divisions.slice());
                    var length = children.length;
                    for (var i = 0; i < length; i++)
                        groups.push(subgroups.concat(children[i]));
                } else groups.push(subgroups);
            }
    
            subgroup.pop();
            n--;
        }
    }