【问题标题】:Algorithm to generate all possible (unordered) assignments of one set to another生成一组到另一组的所有可能(无序)分配的算法
【发布时间】:2020-06-19 15:57:46
【问题描述】:

给定:

  1. 一套颜色独特的蜡笔(x 号)。
  2. 一组孩子。
  3. 所有蜡笔都必须分配给孩子们。
  4. 一个孩子可能有零到 x 支蜡笔。
  5. 每支蜡笔最终应该正好有 1 个孩子。蜡笔不能分配给 2 个或更多孩子。

如何找到所有可能的作业组合?

例如:

class Crayon {
    String color;

    public Crayon(String color) {
        this.color = color;
    }

    public String getColor() {
        return color;
    }

    @Override
    public String toString() {
        return color;
    }
}


class Child {

    String name;
    Set<Crayon> crayons;

    public Child(String name) {
        this.name = name;
        crayons = new HashSet<Crayon>();
    }

    public void addCrayon(Crayon crayon) {
        crayons.add(crayon);
    }

    public Set<Crayon> getCrayons() {
        return crayons;
    }

    @Override
    public String toString() {
        return "Child [name=" + name + ", crayons=" + crayons + "]";
    }
}

public class DistributeCrayons {

    public static void main(String[] args) {

        Set<Crayon> crayons = new HashSet<>();
        crayons.add(new Crayon("red"));
        crayons.add(new Crayon("blue"));
        crayons.add(new Crayon("green"));
        crayons.add(new Crayon("orange"));
        crayons.add(new Crayon("brown"));
        crayons.add(new Crayon("yellow"));
        crayons.add(new Crayon("purple"));

        Child bob = new Child("bob");
        Child amy = new Child("amy");
        Child tom = new Child("tom");

        for(??) {
            for(??) {
                ??
                    System.out.println(bob +" "+ amy +" "+ tom);
                ??
            }
        }
    }
}

这应该输出所有可能的赋值组合,例如:

孩子 [name=bob, crayons=[green, blue]] 孩子 [name=amy, crayons=[brown, red]] 孩子 [name=tom, crayons=[yellow, Purple, orange]]

孩子 [name=bob, crayons=[]] 孩子 [name=amy, crayons=[brown, red, blue]] 孩子 [name=tom, crayons=[yellow, Purple, orange, green]]

孩子 [name=bob, crayons=[red]] 孩子 [name=amy, crayons=[brown, green, Purple, orange]] 孩子 [name=tom, crayons=[blue, yellow]]

等等

更新

感谢大家的宝贵反馈,并感谢 גלעד ברקן 提供有效的 js 解决方案。 (抱歉,由于声誉原因,我无法投票或接受答案)。

我现在可以理解它了,这是我的解决方案版本:

我将蜡笔和儿童转换为列表而不是集合。 对于 7 支蜡笔(红色、蓝色、绿色、橙色、棕色、黄色、紫色)的列表,我的目标是生成三个孩子 bob (id="b")、amy (id="a ") 和 tom (id="t") 的 7 个字符形式。 例如:像“tbbbtat”这样的词表示 Tom 得到红色、棕色和紫色的蜡笔,bob 得到蓝色、绿色和橙色蜡笔,艾米得到黄色的。

public class DistributeCrayons {

    public static void main(String[] args) {

        List<Crayon> crayons = new ArrayList<>();
        crayons.add(new Crayon("red"));
        crayons.add(new Crayon("blue"));
        crayons.add(new Crayon("green"));
        crayons.add(new Crayon("orange"));
        crayons.add(new Crayon("brown"));
        crayons.add(new Crayon("yellow"));
        crayons.add(new Crayon("purple"));

        List<Child> children = new ArrayList<>();
        children.add(new Child("b"));
        children.add(new Child("a"));
        children.add(new Child("t"));

        List<String> assignments = null;
        for(int i = 0; i < crayons.size(); i++)
            assignments = addCrayonCombos(assignments, children);

        System.out.println(assignments);

    }

    static List<String> addCrayonCombos(List<String> assignments, List<Child> children) {
        if(assignments == null) {
            assignments = new ArrayList<String>();
            for(Child c: children)
                assignments.add(c.getId());
            return assignments;
        } else {
            List<String> updatedAssignments = new ArrayList<String>();
            for(String assignment: assignments) {
                for(Child c: children)
                    //append next permutations for a new crayon to existing "words"
                    updatedAssignments.add(assignment+c.getId());
            }
            return updatedAssignments;
        }
    }
}

这会生成预期的分配词列表(准确地说是 2187 个词),因为 7 支蜡笔中的每支都有 3 种可能性(即 3^7 = 2187)。

【问题讨论】:

  • 您可以生成由x 唯一字符和y-1 管道组成的单词排列。说红色、绿色、蓝色的 1、2、3。然后 1|23|对应一个分布。确保字符是有序的,并且你得到了每个排列的分布。

标签: java algorithm loops collections


【解决方案1】:

如果我正确理解您的问题,这将比一对嵌套的 for 循环方式更复杂。

我会给你一个问题陈述和一些伪代码,也许你可以从中实现它。

你有两个集合:

  1. 蜡笔套装。我们称之为C
  2. 人员设置。我们称之为P

在你的例子中,

C = {"red", "orange", "yellow", "green", "blue", "purple", "brown"}
P = {"bob", "amy", "tom"}

所以你需要实现一个函数DistributeCrayons(Set&lt;People&gt; P, Set&lt;Crayons&gt; C),它可以找到所有方法将C 中的蜡笔分发给P 中的人们,我们可以在P 中为每个人提供零个或多个蜡笔对于任何给定的分布。如果P 中的至少一个人在A 中拥有他们在B 中没有的蜡笔,那么两个分布AB 是不同的

我将给出一个递归实现,因为这将是最容易考虑的:

DistributeCrayons(Set<People> P, Set<Crayons> C):
    for subset C' of C:
        for person P' in P:
            assign C' to P'

            if (P - P') == {}:
                return assignments
            else:
                DistributeCrayons(C - C', P - P')

地点:

for subset C' of C 遍历C 的所有可能子集,并在给定的迭代中将C' 分配给其中一个。

for person P' in P: 只需遍历 P--在每次迭代中获取一个人。

assign C' to P' 将蜡笔组C' 分配给人员P'(意味着人员P' 获得了组C' 中的所有蜡笔。)

{} 是空集。因此,if 语句:if (P - P') == {} 只是检查 P' 是否是传递给 DistributeCrayons(...) 的一组人 P 中的唯一一个人。

注意:我实际上并没有处理作业以及如何编写它们。这将比我在伪代码中显示的方式更复杂,因为每次调用 DistributeCrayons(...)

时都必须重置分配

我会继续思考这个问题,看看我是否能想出一个实际的代码 sn-p 给你。

更新:看起来你自己解决了。不错!

【讨论】:

    【解决方案2】:

    孩子们有点诡计。如果我们遍历蜡笔并将它们依次分配到不同的盒子,我们可以获得分配它们的所有方法。然后我们可以随意给这些盒子贴上标签(或者按顺序给孩子们)。

    JavaScript 代码:

    function f(cr, ch, x, comb=[], i=0){
      if (ch.length * x < cr.length)
        return [];
        
      if (i == cr.length)
        return [comb];
        
      let result = [];
      
      for (let j=0; j<ch.length; j++){
        let _comb = ch.map((_, idx) =>
          comb[idx] ? comb[idx].slice() : []);
        
        if (_comb[j].length == x)
          continue;
    
        _comb[j].push(cr[i]);
        
        result = result.concat((f(cr, ch, x, _comb, i + 1)));
      }
    
      return result;
    }
    
    var crayons = ["red", "blue", "green", "orange", "brown", "yellow"];
    
    var children = ["bob", "amy", "tom"];
    
    var str = "";
    
    for (let comb of f(crayons, children, 3))
      str += JSON.stringify(comb) + "\n";
    
    console.log(str);

    【讨论】:

      【解决方案3】:

      有趣的问题。想到的解决方案结合了排列和分区。首先,获取蜡笔列表的每个排列。有一些众所周知的置换算法,搜索应该会找到它们的法定人数。

      对于列表的每个排列,将列表划分为三个部分,并将每个分区中的蜡笔分配给三个孩子。这应该会生成将蜡笔分配给孩子的所有可能方式。

      要将您的七支蜡笔列表分成三部分,您需要将其分为两个位置。第一个将是从列表中元素 0 之前到元素 6 之后的任何地方,包括元素 0。第二个分区将位于与第一个分区相同的位置,直到列表末尾(元素 6 之后),包括在内。最好将这些中断视为发生在 列表的两个元素之间。

      分区可以通过嵌套循环来完成。

      有 5,040 个排列,我相信每个排列有 36 个分区,所以如果我没记错的话,总共应该有 181,440 种分配蜡笔的方式。我不认为这种方法会有任何重复,但我还没有证明这一点。

      【讨论】:

        猜你喜欢
        • 2012-03-16
        • 2012-08-23
        • 2022-01-18
        • 2016-01-07
        • 1970-01-01
        相关资源
        最近更新 更多