2

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

更具体地考虑 a Map<String, List<WordGroupAndScore> baseMap,其中:

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()is 变量,意味着映射中可以有任意数量的s StringbaseMap同样对于,中的每个元素baseMap.get(i).size()都是可变的。但baseMap不能包含空列表。

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

(示例数据与示例中的数据不是一一对应的,实际上是这样WordGroupAndScore,但我会用Strings或BigDecimals来表示示例中的数据)

baseMap(值和键对)严格(A和对)的示例数据List<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"))

所以不知何故,我需要遍历 full baseMap,记住/创建基于 every 的所有组合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

4 回答 4

1

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

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

00000
10000
20000
01000
11000
21000
02000
...

到现在为止,我希望您意识到我们实际上只是在数数(准确地说是以 3 为底数)。

因此,我们只需要将每个元素递增到其自身的限制,而不是以 3 为基数。

为了使我的代码简单,我只使用了 aString[][]而不是 a 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));
}
于 2013-10-07T14:04:09.003 回答
1

使用递归函数的示例伪代码。每一级递归通过一个接一个地获取所有元素,将它们放入输出变量并递归调用自身来处理下一个迭代级别来处理一个列表。

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(input) 嵌套循环。

您使用以下方法调用它:

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);
于 2013-10-07T09:41:59.157 回答
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*是一个函数,它给出了一个可能的基本映射列表和一个键对多值对的列表,返回所有可能的键值对输入基本映射的组合。这是以递归方式完成的。如果key-to-multi-values 对的列表是空的,那么我们不会将任何东西与base maps相关联,而是将它们原封不动地返回。否则,如果有任何对,那么我们采用第一个键对多值对,以及其中的所有值以及所有基本映射作为输入,我们创建了如何将这些键值添加到基本地图的所有组合。修改后的基本映射组合列表将用作递归调用的新基本映射列表combinations*,剩余的键对多值对作为第二个参数。我们进行组合和修改基本映射的递归,直到我们用完键对多值对。此时,如上所述,我们将未修改的基本映射作为解决方案返回,并将它们与递归其他分支的解决方案连接在一起。为了初始化函数来解决我们的问题,我们必须使用一个空地图的单例列表作为基础地图,这是在combinations功能。它唯一的参数是一个多映射,它拆分成一个键到多值对的向量来调用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 代码中调用它,就像这里解释的那样。也可以在这里测试上面的代码,不用在本地搭建 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 版本相比,此代码微不足道。它的优雅、性能、可重用性被严重削弱。没有惰性或可流性,没有对持久数据结构、可组合性等的优化……而且它是如此冗长和冗长!当我写完它时,我忘记了开头是什么。

HTH。

于 2013-10-07T10:55:44.320 回答
0

好的,这是我自己的尝试:不过,我仍然需要对其进行测试,并且要等到以后才能这样做:

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:答案现在是正确的,经过测试并且可以正常工作。

于 2013-10-07T09:38:11.017 回答