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
,但我将使用String
或BigDecimal
来表示示例中的数据)
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")])
我想生成所有可能的数据组合
示例输出,一个(“第一”)组合(值和单个键对)严格(A
和B
对):
("invoiceNumber", "0001")
("invoiceDate", "2013-10-07"])
("priceExclVAT, new BigDecimal("10.00"))
("highVAT, new BigDecimal("2.10"))
("priceInclVAT, new BigDecimal("12.10"))
示例输出,一个(“最后”)组合(值和单个键对)严格(A
和B
对):
("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]
缺少
这里出了什么问题
# 1 楼答案
以下Clojure代码以健壮、快速和功能性的方式解决了您的要求:
上面的代码是一个递归解决方案。它在普通英语中的作用如下。
combinations*
是一个函数,它给出一个可能的基本映射列表,以及一个键对多值对列表,返回将键值关联到输入基本映射的所有可能组合。这是以递归的方式完成的。如果键到多个值对的列表为空,则我们不会将任何内容与基本映射关联,而是返回未修改的基本映射。否则,如果有任何对,那么我们将第一个键添加到多值对,对于其中的所有值,以及作为输入提供的所有基本映射,我们将创建所有组合,以便将这些键值添加到基本映射中。此修改的基本映射组合列表将用作递归调用combinations*
的新基本映射列表,剩余的多值对键作为第二个参数。我们进行这种组合和修改基本映射的递归,直到用完多个值对的键。在这一点上,如上所述,我们返回未修改的基映射作为解决方案,并将它们与来自递归的其他分支的解决方案连接在一起。为了初始化函数以解决我们的问题,我们必须使用空映射的单例列表作为基本映射,这是在combinations
函数中完成的。它唯一的参数是一个multi-map,它将其拆分为一个向量key-to-multi-values-pairs,用它调用combinations*
这就是它的名称:
这是输出:
尝试将其转换为Java,或者只包含Clojure依赖项,添加Java类生成指令,并直接从Java代码调用它,如如何解释here。您还可以测试上述代码here,而无需费心在本地设置Clojure环境
更新
为了便于讨论和理解,我将很快添加一个Java版本
更新2
好了
俗话说“你不可能总是得到你想要的”。现在你明白了,但我告诉你这不是你需要的。与Clojure版本相比,此代码算不了什么。它的优雅性、性能和可重用性都受到严重影响。没有惰性或可流性,没有对持久数据结构、可组合性等的优化。。。而且它又长又冗长!当我写完的时候,我忘了开头是什么
嗯
# 2 楼答案
使用递归函数的伪代码示例。每个递归级别处理一个列表,方法是逐个获取所有元素,将它们放在输出变量中,然后递归地调用自己来处理下一个迭代级别
因此,这可以通过使用递归有效地创建sizeof(输入)嵌套循环
您可以使用以下命令来调用它:
注意:如果不是打印输出,而是要返回输出。然后更改方法的签名:
并使用以下命令调用它:
# 3 楼答案
好的,这是我自己的尝试:我仍然需要测试它,直到以后才能测试:
Map<WordGroup, List<ValueAndScore>> wordGroupsAndScores;
<;-在更早的地方被初始化这将给出地图中所有可能的索引组合,并逐一进行处理
编辑:更新了处理函数以显示如何检索元素
编辑2:这个答案是错误的。确实生成了一些组合,但肯定不是全部
编辑3:答案现在是正确的,经过测试并正常工作
# 4 楼答案
让我们假设它们的大小都是3(为了便于解释)
然后,我们需要为第二个元素打印的索引将如下所示:
到现在为止,我希望你意识到我们实际上只是在计数(准确地说是在基数3)
因此,我们不需要基数3,只需要将每个元素增加到它自己的极限
为了保持代码简单,我只使用了
String[][]
而不是Map<A, List<B>>
(每行的第一个元素对应于A
-我使用了与您相同的数据,因此应该很容易破译)