有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

Map<A,List<B>>元素的Java组合算法

(注:我只是重写了这个问题,因为我以为它是在处理排列,但实际上它是在处理组合。)

更具体地考虑一个^ {CD1>},用:

private static class WordGroupAndScore {
    public final WordGroup wordGroup;
    public final int score;

    public WordGroupAndScore(final WordGroup wordGroup, final int score) {
        this.wordGroup = wordGroup;
        this.score = score;
    }
}

baseMap.size()是可变的,这意味着映射中可以有任意数量的String。同样,对于baseMap中的每个元素,baseMap.get(i).size()是可变的。但是baseMap不能包含空列表

现在我正试图找到所有可能的组合。代码本身用于检查发票中的数据,但并非发票上的所有数据都可用,因此baseMap.size()的金额是可变的。而且baseMap中每个元素的列表是可变的,因为找到的数据量取决于它是哪个发票

(示例数据与示例中的数据不一一对应,因为实际上它是WordGroupAndScore,但我将使用StringBigDecimal来表示示例中的数据)

baseMap(值和键对)的示例数据严格(AList<B>对):

  • ("invoiceNumber", ["0001", "0002"])
  • ("invoiceDate", ["2013-10-07"])
  • ("priceExclVAT, [new BigDecimal("10.00")])
  • ("highVAT, [new BigDecimal("2.10")])
  • ("priceInclVAT, [new BigDecimal("12.10"), new BigDecimal("14.10")])

我想生成所有可能的数据组合

示例输出,一个(“第一”)组合(值和单个键对)严格(AB对):

  • ("invoiceNumber", "0001")
  • ("invoiceDate", "2013-10-07"])
  • ("priceExclVAT, new BigDecimal("10.00"))
  • ("highVAT, new BigDecimal("2.10"))
  • ("priceInclVAT, new BigDecimal("12.10"))

示例输出,一个(“最后”)组合(值和单个键对)严格(AB对):

  • ("invoiceNumber", "0002")
  • ("invoiceDate", "2013-10-07")
  • ("priceExclVAT, new BigDecimal("10.00"))
  • ("highVAT, new BigDecimal("2.10"))
  • ("priceInclVAT, new BigDecimal("14.10"))

因此,不知何故,我需要迭代整个baseMap,记住/创建基于每个baseMap.get(i).size()的所有组合,但我几乎不知道从哪里开始。最大的问题是:我如何记住这些组合,因为我的baseMap大小不一。如果它不是可变的,那么我可以做得容易得多

我希望这个问题足够清楚

编辑:添加了我的一次尝试,但无效

//Assumes that wordGroupsAndScores does not get changed during the process
private void processWordGroupAndScores(TemplateBean template) {
    System.out.println();
    System.out.println("--wordGroupsAndScores--");
    for (Map.Entry<String, List<WordGroupAndScore>> entry : wordGroupsAndScores.entrySet()) {
        System.out.println("Attribute = " + entry.getKey());
        for (WordGroupAndScore wordGroupAndScore : entry.getValue()) {
            System.out.println("WordGroupAndScore = " + wordGroupAndScore);
        }
        System.out.println(";");
    }
    System.out.println();
    //create all possible unfinishedinvoices from wordgroupandscores
    int[] indices = new int[wordGroupsAndScores.keySet().size()];
    for (int index = 0; index < indices.length; index++) {
        indices[index] = 0;
    }
    String[] keyLocation = new String[wordGroupsAndScores.keySet().size()];
    int j = 0;
    for (String key : wordGroupsAndScores.keySet()) {
        keyLocation[j] = key;
        j++;
    }
    processWordGroupAndScoresRecursive(indices, keyLocation, template);
}

private void processWordGroupAndScoresRecursive(int[] indices, String[] keyLocation, TemplateBean template) {
    processWordGroupAndScoresWithIndices(indices, keyLocation, template);
    boolean changedIndices = false;
    for (int index = indices.length - 1; index >= 0; index--) {
        if (indices[index] < wordGroupsAndScores.get(keyLocation[index]).size() - 1) {
            indices[index]++;
            changedIndices = true;
            break;
        }
    }
    if (changedIndices) {
        processWordGroupAndScoresRecursive(indices, keyLocation, template);
    }
}

private void processWordGroupAndScoresWithIndices(int[] indices, String[] keyLocation, TemplateBean template) {
    System.out.println();
    System.out.println("--Generated combination--");
    UnfinishedInvoice unfinishedInvoice = new UnfinishedInvoice();
    for (int index = 0; index < indices.length; index++) {
        String key = keyLocation[index];
        WordGroupAndScore wordGroupAndScore = wordGroupsAndScores.get(key).get(indices[index]);
        System.out.println("Attribute = " + key);
        System.out.println("WordGroupAndScore = " + wordGroupAndScore);
        System.out.println(";");
        setUnfinishedInvoiceAttribute(key, unfinishedInvoice, Utils.joinWordGroup(wordGroupAndScore.wordGroup, " "), wordGroupAndScore.score);
    }
    System.out.println();
    unfinishedInvoice.verify();
    if (templateMap.containsKey(template)) {
        templateMap.get(template).add(unfinishedInvoice);
    }
    else {
        List<UnfinishedInvoice> list = new ArrayList<>();
        list.add(unfinishedInvoice);
        templateMap.put(template, list);
    }
}
让我们更清楚地看它产生了什么,让我们只与指数一起工作,而不是与真实数据一起工作。

假设这是输入:[1, 1, 2, 1, 0]。它是一个映射作为列表的特征,元素是原始映射中列表中元素的索引。我们从组合开始,在组合中提取地图中的最后一个元素

对于我的失败代码,我们将得到以下输出:

  • [1, 1, 2, 1, 0]
  • [1, 1, 2, 0, 0]
  • [1, 1, 1, 0, 0]
  • [1, 1, 0, 0, 0]
  • [1, 0, 0, 0, 0]
  • [0, 0, 0, 0, 0]

这是不正确的,因为缺少很多值,例如[0, 0, 0, 1, 0]缺少

这里出了什么问题


共 (4) 个答案

  1. # 1 楼答案

    以下Clojure代码以健壮、快速和功能性的方式解决了您的要求:

    (defn combinations* [acc pairs]
      (if-let [[my-key my-vals] (first pairs)]
        (mapcat
          (fn [my-val]
            (combinations*
              (for [m acc] (assoc m my-key my-val))
              (rest pairs)))
          my-vals)
        acc))
    
    (defn combinations [map]
      (combinations* [{}] (vec map)))
    

    上面的代码是一个递归解决方案。它在普通英语中的作用如下。 combinations*是一个函数,它给出一个可能的基本映射列表,以及一个键对多值对列表,返回将键值关联到输入基本映射的所有可能组合。这是以递归的方式完成的。如果键到多个值对的列表为空,则我们不会将任何内容与基本映射关联,而是返回未修改的基本映射。否则,如果有任何对,那么我们将第一个键添加到多值对,对于其中的所有值,以及作为输入提供的所有基本映射,我们将创建所有组合,以便将这些键值添加到基本映射中。此修改的基本映射组合列表将用作递归调用combinations*的新基本映射列表,剩余的多值对键作为第二个参数。我们进行这种组合和修改基本映射的递归,直到用完多个值对的键。在这一点上,如上所述,我们返回未修改的基映射作为解决方案,并将它们与来自递归的其他分支的解决方案连接在一起。为了初始化函数以解决我们的问题,我们必须使用空映射的单例列表作为基本映射,这是在combinations函数中完成的。它唯一的参数是一个multi-map,它将其拆分为一个向量key-to-multi-values-pairs,用它调用combinations*

    这就是它的名称:

    (combinations {"invoiceNumber" ["0001" "0002"]
                   "invoiceDate" ["2013-10-07"]
                   "priceExclVAT" [10.00M]
                   "highVAT" [2.10M]
                   "priceInclVAT" [12.10M 14.10M]})
    

    这是输出:

    ({"invoiceDate" "2013-10-07",
      "invoiceNumber" "0001",
      "highVAT" 2.10M,
      "priceExclVAT" 10.00M,
      "priceVAT" 12.10M}
     {"invoiceDate" "2013-10-07",
      "invoiceNumber" "0002",
      "highVAT" 2.10M,
      "priceExclVAT" 10.00M,
      "priceVAT" 12.10M}
     {"invoiceDate" "2013-10-07",
      "invoiceNumber" "0001",
      "highVAT" 2.10M,
      "priceExclVAT" 10.00M,
      "priceVAT" 14.10M}
     {"invoiceDate" "2013-10-07",
      "invoiceNumber" "0002",
      "highVAT" 2.10M,
      "priceExclVAT" 10.00M,
      "priceVAT" 14.10M})
    

    尝试将其转换为Java,或者只包含Clojure依赖项,添加Java类生成指令,并直接从Java代码调用它,如如何解释here。您还可以测试上述代码here,而无需费心在本地设置Clojure环境

    更新

    为了便于讨论和理解,我将很快添加一个Java版本

    更新2

    好了

    private static List<HashMap<String, Object>> associateInAll(
            List<HashMap<String, Object>> orig, String key, Object val) {
    
        LinkedList<HashMap<String, Object>> result =
                new LinkedList<HashMap<String, Object>>();
    
        for (HashMap<String, Object> m : orig) {
            HashMap<String, Object> mCopy = new HashMap<String, Object>(m);
            mCopy.put(key, val);
            result.add(mCopy);
        }
    
        return result;
    }
    
    private static List<HashMap<String, Object>> combinations2(
            List<HashMap<String, Object>> acc,
            List<Entry<String, List<Object>>> pairs) {
    
        if (!pairs.isEmpty()) {
    
            Entry<String, List<Object>> first = pairs.get(0);
            String myKey = first.getKey();
            List<Object> myVals = first.getValue();
    
            LinkedList<Entry<String, List<Object>>> rest =
                    new LinkedList<Entry<String, List<Object>>>(pairs);
    
            rest.removeFirst();
    
            LinkedList<HashMap<String, Object>> results =
                    new LinkedList<HashMap<String, Object>>();
    
            for (Object myVal : myVals) {
    
                List<HashMap<String, Object>> newBaseMaps =
                        associateInAll(acc, myKey, myVal);
    
                List<HashMap<String, Object>> subcombinations =
                        combinations2(newBaseMaps, rest);
    
                results.addAll(subcombinations);
            }
    
            return results;
        }
    
        return acc;
    }
    
    private static List<HashMap<String, Object>> combinations(
            HashMap<String, List<Object>> map) {
    
        LinkedList<HashMap<String, Object>> baseMaps =
                new LinkedList<HashMap<String, Object>>();
    
        baseMaps.add(new HashMap<String, Object>());
    
        LinkedList<Entry<String, List<Object>>> pairs =
                new LinkedList<Entry<String, List<Object>>>(map.entrySet());
    
        return combinations2(baseMaps, pairs);
    }
    
    public static void main(String... args) {
    
        HashMap<String, List<Object>> input =
                new HashMap<String, List<Object>>();
    
        input.put("invoiceNumber",
                Arrays.<Object>asList("0001", "0002", "0003"));
        input.put("invoiceDate",
                Arrays.<Object>asList("2013-10-07"));
        input.put("priceExclVAT",
                Arrays.<Object> asList(new BigDecimal("10.00")));
        input.put("highVAT",
                Arrays.<Object>asList(new BigDecimal("2.10")));
        input.put("priceInclVAT",
                Arrays.<Object>asList(new BigDecimal("12.10"), new BigDecimal("14.10")));
    
        List<HashMap<String, Object>> results = combinations(input);
    
        for (HashMap<String, Object> combination : results) {
            System.out.println("=============================");
            for (Entry<String, Object> entry : combination.entrySet()) {
                System.out.println(entry.getKey() + ": " + entry.getValue());
            }
        }
    }
    

    俗话说“你不可能总是得到你想要的”。现在你明白了,但我告诉你这不是你需要的。与Clojure版本相比,此代码算不了什么。它的优雅性、性能和可重用性都受到严重影响。没有惰性或可流性,没有对持久数据结构、可组合性等的优化。。。而且它又长又冗长!当我写完的时候,我忘了开头是什么

  2. # 2 楼答案

    使用递归函数的伪代码示例。每个递归级别处理一个列表,方法是逐个获取所有元素,将它们放在输出变量中,然后递归地调用自己来处理下一个迭代级别

    void allCombinations(Map<A, List<B>> input, Map<A, B> output){
       if (input not empty){
          (x, Y) = input.removeOneElement(); //removes one list from the input
          for each b in Y{
            output.insert(x, b);             //adds the element to the output
            allCombinations(input, output);  //recursively calls itself
            output.remove(x, b);             //removes the element from the output
          }
       }else{
          print(output)                      //here i print the output
       }
    }
    

    因此,这可以通过使用递归有效地创建sizeof(输入)嵌套循环

    您可以使用以下命令来调用它:

    allCombinations(input, new Map<A, B>());
    

    注意:如果不是打印输出,而是要返回输出。然后更改方法的签名:

    void allCombinations(Map<A, List<B>> input, Map<A, B> output, List<Map<A,B>> result)
    ...
    result.add(output); //instead of print(output);
    

    并使用以下命令调用它:

    List<Map<A,B>> result = new List<Map<A,B>>();
    allCombinations(input, new Map<A, B>(), result);
    
  3. # 3 楼答案

    好的,这是我自己的尝试:我仍然需要测试它,直到以后才能测试:

    Map<WordGroup, List<ValueAndScore>> wordGroupsAndScores;<;-在更早的地方被初始化

    //Assumes that wordGroupsAndScores does not get changed during the process
    private void processWordGroupAndScores() {
        //create all possible templatetoinvoices from wordgroupandscores
        int[] indices = new int[wordGroupsAndScores.keySet().size()];
        for (int index = 0; index < indices.length; index++) {
            indices[index] = 0;
        }
        String[] keyLocation = new String[wordGroupsAndScores.keySet().size()];
        int j = 0;
        for (String key : wordGroupsAndScores.keySet()) {
            keyLocation[j] = key;
            j++;
        }
        processWordGroupAndScoresRecursive(indices, keyLocation);
    }
    
    private void processWordGroupAndScoresRecursive(int[] indices, String[] keyLocation) {
        processWordGroupAndScoresWithIndices(indices, keyLocation);
        boolean changedIndices = false;
        for (int index = indices.length - 1; index >= 0; index--) {
            if (indices[index] < wordGroupsAndScores.get(keyLocation[index]).size() - 1) {
                indices[index]++;
                //reset indices to the right
                for (int resetIndex = index + 1; resetIndex < indices.length; resetIndex++) {
                    indices[resetIndex] = 0;
                }
                changedIndices = true;
                break;
            }
        }
        if (changedIndices) {
            processWordGroupAndScoresRecursive(indices, keyLocation);
        }
    }
    
    private void processWordGroupAndScoresWithIndices(int[] indices, String[] keyLocation) {
        for (int index = 0; index < indices.length; index++) {
            String key = keyLocation[index];
            WordGroupAndScore wordGroupAndScore = wordGroupsAndScores.get(key).get(indices[index]);
            //more processing
        }
        //more processing
    }
    

    这将给出地图中所有可能的索引组合,并逐一进行处理

    编辑:更新了处理函数以显示如何检索元素

    编辑2:这个答案是错误的。确实生成了一些组合,但肯定不是全部

    编辑3:答案现在是正确的,经过测试并正常工作

  4. # 4 楼答案

    让我们假设它们的大小都是3(为了便于解释)

    然后,我们需要为第二个元素打印的索引将如下所示:

    00000
    10000
    20000
    01000
    11000
    21000
    02000
    ...
    

    到现在为止,我希望你意识到我们实际上只是在计数(准确地说是在基数3)

    因此,我们不需要基数3,只需要将每个元素增加到它自己的极限

    为了保持代码简单,我只使用了String[][]而不是Map<A, List<B>>(每行的第一个元素对应于A-我使用了与您相同的数据,因此应该很容易破译)

    // some hard-coded data
    static String[][] strArr = {{"invoiceNumber", "0001", "0002"},
                                {"invoiceDate", "2013-10-07"},
                                {"priceExclVAT", "10.00"},
                                {"highVAT", "2.10"},
                                {"priceInclVAT", "12.10", "14.10"}};
    static int[] indices = new int[strArr.length];
    
    static boolean increment(int index)
    {
       // when we can simply increase the current element
       if (indices[index] < strArr[index].length-2)
       {
          indices[index]++;
          return true;
       }
       // when we need to reset this element to 0 and increase the next element
       else
       {
          if (index == strArr.length-1)
             // we reached the end of the last list, so we're done
             return false;
          indices[index] = 0;
          return increment(index+1);
       }
    }
    
    static void print()
    {
       System.out.println(Arrays.toString(indices));
       for (int i = 0; i < strArr.length; i++)
          System.out.println(strArr[i][0] + ", " + strArr[i][indices[i]+1]);
       System.out.println();
    }
    
    public static void main(String[] args)
    {
       // simply repeatedly print the output, then increment
       do
       {
          print();
       }
       while (increment(0));
    }