(ps。我只是重写了这个问题,因为我认为这是在处理排列,但它实际上是处理组合。)
更具体地考虑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()
是可变的,这意味着地图中可以有任意数量的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]
。将它作为列表表示为地图,使用as元素作为原始地图内列表中元素的索引。我们从地图中最后一个元素的组合开始。
我的失败代码输出为
[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]
。
这里出了什么问题?
答案 0 :(得分: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(输入)嵌套循环。
您使用以下方式调用它:
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);
答案 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
函数中完成。它唯一的参数是一个多地图,它分成一个键到多值对的向量,用它来调用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-ified版本。
更新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。
答案 2 :(得分:1)
我们假设它们的大小都是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));
}
答案 3 :(得分:0)
好的,这是我自己的尝试:我仍然需要测试它,并且将无法这样做,直到以后的日期:
Map<WordGroup, List<ValueAndScore>> wordGroupsAndScores;
&lt; - 在某个地方获得初始化
//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:现在答案是正确的,经过测试和运作。