我遇到的问题实际上是一个普通的编程问题,但我的实现是用Java编写的,所以我会用这样的方式提供我的例子
我有一个这样的课程:
public class Foo {
LinkedHashMap<String, Vector<String>> dataStructure;
public Foo(LinkedHashMap<String, Vector<String>> dataStructure){
this.dataStructure = dataStructure;
}
public String[][] allUniqueCombinations(){
//this is what I need to do
}
}
我需要从我的LinkedHashMap
生成一个嵌套数组,它代表LHM中所有值的每个唯一组合。例如,如果我的LHM看起来像这样(伪代码,但我认为你可以得到这个想法..):
{"foo" => ["1","2","3"], "bar" => ["3","2"], "baz" => ["5","6","7"]};
然后我的String [] []看起来像这样:
{
{"foo","bar","baz"},
{"1","3","5"},
{"1","2","5"},
{"1","3","6"},
{"1","2","6"},
{"1","3","7"},
{"1","2","7"},
{"2","3","5"},
{"2","2","5"},
{"2","3","6"},
{"2","2","6"},
{"2","3","7"},
{"2","2","7"},
{"3","3","5"},
{"3","2","5"},
{"3","3","6"},
{"3","2","6"},
{"3","3","7"},
{"3","2","7"},
}
我认为这就是所有这些,我手动(显然)这样做,所以我可能错过了一套,但我认为这说明了我正在尝试做的事情。只要存在所有独特的组合,每组的顺序无关紧要。另外需要明确的是,您不知道LHM中有多少元素,也不知道每个后续Vector中有多少元素。我找到的答案与你想要在一个数组中所有元素的每个独特组合的情况相匹配,但没有任何东西完全符合这一点。如果这是问题的完全重复,请在回复中添加一个链接,我将结束问题。
更新 - 我将类型更改为字符串,因为我的真实世界示例实际上是字符串。我试图使用整数来使示例更具可读性,但到目前为止我得到的答案并没有很好地转换为字符串。所以,是的,它们是数字,但在我的实际情况中,它们将是除了使用此特定应用程序的人之外对任何人都没有多大意义的字符串。所以,这只是它的抽象。
答案 0 :(得分:17)
尝试这样的事情:
public static void generate(int[][] sets) {
int solutions = 1;
for(int i = 0; i < sets.length; solutions *= sets[i].length, i++);
for(int i = 0; i < solutions; i++) {
int j = 1;
for(int[] set : sets) {
System.out.print(set[(i/j)%set.length] + " ");
j *= set.length;
}
System.out.println();
}
}
public static void main(String[] args) {
generate(new int[][]{{1,2,3}, {3,2}, {5,6,7}});
}
将打印:
1 3 5
2 3 5
3 3 5
1 2 5
2 2 5
3 2 5
1 3 6
2 3 6
3 3 6
1 2 6
2 2 6
3 2 6
1 3 7
2 3 7
3 3 7
1 2 7
2 2 7
3 2 7
我已经基于(我相信)Knuth的TAOCP书之一实现了上面的算法(在评论中@chikitin有一个更具体的参考:它在PRE FASCICLE 2A第7.2.1.1节生成所有n - Knuth,Addison Wesley的计算机程序设计艺术的一部分。
请注意,我已将数组命名为set
,但它们当然不需要包含唯一元素。我使用它时,它们确实包含了独特的元素,因此得名。
这是一对一的翻译:
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Vector;
public class Foo {
private LinkedHashMap<String, Vector<String>> dataStructure;
public Foo(LinkedHashMap<String, Vector<String>> dataStructure){
this.dataStructure = dataStructure;
}
public String[][] allUniqueCombinations(){
int n = dataStructure.keySet().size();
int solutions = 1;
for(Vector<String> vector : dataStructure.values()) {
solutions *= vector.size();
}
String[][] allCombinations = new String[solutions + 1][];
allCombinations[0] = dataStructure.keySet().toArray(new String[n]);
for(int i = 0; i < solutions; i++) {
Vector<String> combination = new Vector<String>(n);
int j = 1;
for(Vector<String> vec : dataStructure.values()) {
combination.add(vec.get((i/j)%vec.size()));
j *= vec.size();
}
allCombinations[i + 1] = combination.toArray(new String[n]);
}
return allCombinations;
}
public static void main(String[] args) {
LinkedHashMap<String, Vector<String>> data = new LinkedHashMap<String, Vector<String>>();
data.put("foo", new Vector<String>(Arrays.asList("1", "2", "3")));
data.put("bar", new Vector<String>(Arrays.asList("3", "2")));
data.put("baz", new Vector<String>(Arrays.asList("5", "6", "7")));
Foo foo = new Foo(data);
for(String[] combination : foo.allUniqueCombinations()) {
System.out.println(Arrays.toString(combination));
}
}
}
如果您运行上述课程,则会打印以下内容:
[foo, bar, baz]
[1, 3, 5]
[2, 3, 5]
[3, 3, 5]
[1, 2, 5]
[2, 2, 5]
[3, 2, 5]
[1, 3, 6]
[2, 3, 6]
[3, 3, 6]
[1, 2, 6]
[2, 2, 6]
[3, 2, 6]
[1, 3, 7]
[2, 3, 7]
[3, 3, 7]
[1, 2, 7]
[2, 2, 7]
[3, 2, 7]
答案 1 :(得分:3)
如何懒洋洋地生成产品,即。只在你访问元组时创建元组?
/**
* A random access view of tuples of a cartesian product of ArrayLists
*
* Orders tuples in the natural order of the cartesian product
*
* @param T the type for both the values and the stored tuples, ie. values of the cartesian factors are singletons
* While the type of input sets is List<T> with elements being treated as singletons
*
*/
abstract public class CartesianProductView<T> extends AbstractList<T> {
private final List<List<T>> factors;
private final int size;
/**
* @param factors the length of the factors (ie. the elements of the factors argument) should not change,
* otherwise get may not return all tuples, or throw exceptions when trying to access the factors outside of range
*/
public CartesianProductView(List<List<T>> factors) {
this.factors = new ArrayList<>(factors);
Collections.reverse(this.factors);
int acc = 1;
for (Iterator<List<T>> iter = this.factors.iterator(); iter.hasNext(); ) {
acc *= iter.next().size();
}
this.size = acc;
}
@Override
public T get(int index) {
if (index < 0 || index >= size()) {
throw new IndexOutOfBoundsException(String.format("index %d > size() %d", index, size()));
}
T acc = null;
for (Iterator<List<T>> iter = factors.iterator(); iter.hasNext();) {
List<T> set = iter.next();
acc = makeTupleOrSingleton(set.get(index % set.size()), acc);
index /= set.size();
}
return acc;
}
@Override
public int size() {
return size;
}
private T makeTupleOrSingleton(T left, T right) {
if (right == null) {
return left;
}
return makeTuple(left, right);
}
/**
*
* @param left a singleton of a value
* @param right a tuple of values taken from the cartesian product factors, with null representing the empty set
* @return the sum of left and right, with the value of left being put in front
*/
abstract protected T makeTuple(T left, T right);
}
并像这样使用
final List<List<String>> l1 = new ArrayList<List<String>>() {{ add(singletonList("a")); add(singletonList("b")); add(singletonList("c")); }};
final List<List<String>> l2 = new ArrayList<List<String>>() {{ add(singletonList("X")); add(singletonList("Y")); }};
final List<List<String>> l3 = new ArrayList<List<String>>() {{ add(singletonList("1")); add(singletonList("2")); add(singletonList("3")); add(singletonList("4")); }};
List<List<List<String>>> in = new ArrayList<List<List<String>>>() {{ add(l1); add(l2); add(l3); }};
List<List<String>> a = new CartesianProductView<List<String>>(in) {
@Override
protected List<String> makeTuple(final List<String> left, final List<String> right) {
return new ArrayList<String>() {{ add(left.get(0)); addAll(right); }};
}
};
System.out.println(a);
结果:
[[a, X, 1], [a, X, 2], [a, X, 3], [a, X, 4], [a, Y, 1], [a, Y, 2], [a, Y, 3], [a, Y, 4], [b, X, 1], [b, X, 2], [b, X, 3], [b, X, 4], [b, Y, 1], [b, Y, 2], [b, Y, 3], [b, Y, 4], [c, X, 1], [c, X, 2], [c, X, 3], [c, X, 4], [c, Y, 1], [c, Y, 2], [c, Y, 3], [c, Y, 4]]
作为一个额外的好处,你可以使用它连接所有的字符串:
final List<String> l1 = new ArrayList<String>() {{ add("a"); add("b"); add("c"); }};
final List<String> l2 = new ArrayList<String>() {{ add("X"); add("Y"); }};
final List<String> l3 = new ArrayList<String>() {{ add("1"); add("2"); add("3"); add("4"); }};
List<List<String>> in = new ArrayList<List<String>>() {{ add(l1); add(l2); add(l3); }};
List<String> a = new CartesianProductView<String>(in) {
@Override
protected String makeTuple(String left, String right) {
return String.format("%s%s", left, right);
}
};
System.out.println(a);
结果:
[aX1, aX2, aX3, aX4, aY1, aY2, aY3, aY4, bX1, bX2, bX3, bX4, bY1, bY2, bY3, bY4, cX1, cX2, cX3, cX4, cY1, cY2, cY3, cY4]
答案 2 :(得分:3)
在你需要答案之后很久就知道了,但不知怎的,我不能注意到人们可以切换到Groovy,至少对于Java应用程序的某些部分,并编写一个包装类匹配所需的界面。这种排列的Groovy代码是
myListOfLists.combinations()
自从我开始在我的Java应用程序中使用Groovy以来,编写它们要快得多,并且调试/配置它们的方式更有趣(嗯......)
答案 3 :(得分:2)
看看以下两种方法,它们完全符合您的要求。我把它们写成通用的,无论你的列表有多长,或者地图中存在多少个密钥,生成的组合都是正确的。
下面的代码是迭代,基于Python itertools.product()
函数的算法,用于计算列表列表的笛卡尔乘积。
public String[][] allUniqueCombinations() {
List<String> labels = new ArrayList<String>();
List<List<String>> lists = new ArrayList<List<String>>();
for (Map.Entry<String, Vector<String>> entry : dataStructure.entrySet()) {
labels.add(entry.getKey());
lists.add(entry.getValue());
}
List<List<String>> combinations = product(lists);
int m = combinations.size() + 1;
int n = labels.size();
String[][] answer = new String[m][n];
for (int i = 0; i < n; i++)
answer[0][i] = labels.get(i);
for (int i = 1; i < m; i++)
for (int j = 0; j < n; j++)
answer[i][j] = combinations.get(i-1).get(j);
return answer;
}
private List<List<String>> product(List<List<String>> lists) {
List<List<String>> result = new ArrayList<List<String>>();
result.add(new ArrayList<String>());
for (List<String> e : lists) {
List<List<String>> tmp1 = new ArrayList<List<String>>();
for (List<String> x : result) {
for (String y : e) {
List<String> tmp2 = new ArrayList<String>(x);
tmp2.add(y);
tmp1.add(tmp2);
}
}
result = tmp1;
}
return result;
}
我用问题中的例子测试了它们:
LinkedHashMap<String, Vector<String>> sample =
new LinkedHashMap<String, Vector<String>>();
Vector<String> v1 = new Vector<String>();
v1.add("1"); v1.add("2"); v1.add("3");
Vector<String> v2 = new Vector<String>();
v2.add("3"); v2.add("2");
Vector<String> v3 = new Vector<String>();
v3.add("5"); v3.add("6"); v3.add("7");
sample.put("foo", v1);
sample.put("bar", v2);
sample.put("baz", v3);
Foo foo = new Foo(sample);
String[][] ans = foo.allUniqueCombinations();
for (String[] row : ans)
System.out.println(Arrays.toString(row));
打印的答案是预期的(虽然组合以不同的顺序出现):
[foo, bar, baz]
[1, 3, 5]
[1, 3, 6]
[1, 3, 7]
[1, 2, 5]
[1, 2, 6]
[1, 2, 7]
[2, 3, 5]
[2, 3, 6]
[2, 3, 7]
[2, 2, 5]
[2, 2, 6]
[2, 2, 7]
[3, 3, 5]
[3, 3, 6]
[3, 3, 7]
[3, 2, 5]
[3, 2, 6]
[3, 2, 7]
答案 4 :(得分:2)
您也可以使用Functional Java's List monad轻松解决此问题:
import fj.data.List;
public class cartesian {
public static void main(String[] args) {
List<String> foo = List.list("a", "b");
List<Integer> bar = List.list(1,2,3);
List<Float> baz = List.list(0.2f,0.4f,0.3f);
List<P3<String, Integer, Float>>
// the Cartesian product is assembled into a list of P3's
result = foo.bind(bar, baz, P.<String, Integer, Float>p3());
String out = Show.listShow(Show.p3Show(Show.stringShow, Show.intShow, Show.floatShow))
.showS(result);
System.out.println(out);
}
}
答案 5 :(得分:2)
Guava有一个实用程序方法,它返回给定列表集的笛卡尔积:Sets.cartesianProduct。
答案 6 :(得分:1)
这是一个link,它的c#,但我相信你可以使用它!
答案 7 :(得分:1)
字符串向量的LinkedHashMap是...... - 很麻烦。我不得不花费大量时间来转换解决方案以使用它,但最后,我不会生成一个ArrayOfArrays,而是一个列表列表,并向读者保留最后一步。
import java.util.*;
/**
CartesianProductLHM
*/
public class CartesianProductLHM
{
LinkedHashMap <String, Vector<String>> dataStructure;
public CartesianProductLHM (final String[] data) {
dataStructure = new LinkedHashMap <String, Vector<String>> ();
for (String str : data)
{
String [] kv = str.split (":");
String [] values = kv[1].split (",");
Vector <String> v = new Vector <String> ();
for (String s: values) {
v.add (s);
// System.out.print (s);
}
// System.out.println ("\n---");
dataStructure.put (kv[0], v);
}
// System.out.println (" --- --- ---");
}
List <String> getCombiFor (final int i, final List <List <String>> livs)
{
List <String> ls = new ArrayList <String> ();
if (! livs.isEmpty ()) {
List <String> vs = livs.remove (0);
int idx = i % vs.size ();
String elem = vs.get (idx);
ls.add (elem);
ls.addAll (getCombiFor (i / vs.size (), livs));
}
return ls;
}
List <String> getOuterCombiFor (int i, List <List <String>> coll)
{
List <String> ls = new ArrayList <String> ();
if (! coll.isEmpty ()) {
List <List <String>> livs = new ArrayList <List <String>> ();
for (List<String> li : coll)
{
livs.add (li);
}
ls.addAll (getCombiFor (i, livs));
}
return ls;
}
public List <List <String>> allUniqueCombinations () {
Collection <Vector <String>> li = dataStructure.values ();
List <List <String>> lls = new ArrayList <List <String>> ();
for (Vector <String> vs : li) {
List <String> l = new ArrayList <String> ();
for (String s : vs) {
l.add (s);
}
lls.add (l);
}
int count = 1;
for (Vector <String> vec: li) {
count *= vec.size ();
}
List <List <String>> result = new ArrayList <List <String>> ();
for (int i = 0; i < count; ++i)
{
List <String> l = getOuterCombiFor (i, lls);
result.add (l);
}
return result;
}
public static void main (String args[])
{
String[] arr = {"foo:1,2,3", "bar:a,b", "baz:5,6,7"};
CartesianProductLHM cp = new CartesianProductLHM (arr);
List <List <String>> lls = cp.allUniqueCombinations ();
for (List <String> ls : lls)
{
for (String s : ls)
System.out.print (s + "\t");
System.out.println ();
}
}
}
嗯 - 是的,我解析了一些测试数据。
主要的想法是,你有一些列表(abc,12,defg,...)你在pos 0有3种可能性,在pos 1有4种,在pos 3有4种,所以3 * 2 *到目前为止4种组合。
从数字0到23,您可以从每个子列表中选择模数,并将数字的其余部分除以前一个列表的大小,其余列表递归到该过程,直到没有列表为止。< / p>
答案 8 :(得分:1)
我迟到了,但我遵循了Shiomi的链接并将功能翻译成了Java。结果是一个易于理解的算法(我可能有点慢,因为我很难理解Bart Kiers的解决方案)。
这里是(键是int,替换为String应该是直截了当的):
<强>用法强>
public void testProduct(){
Map<Integer, List<String>> data = new LinkedHashMap<Integer, List<String>>(){{
put(0, new ArrayList<String>(){{
add("John"); add("Sarah");
}});
put(1, new ArrayList<String>(){{
add("Red"); add("Green"); add("Blue"); add("Orange");
}});
put(2, new ArrayList<String>(){{
add("Apple"); add("Tomatoe"); add("Bananna");
}});
}};
List<String[]> product = GetCrossProduct(data);
for(String[] o : product)
System.out.println(Arrays.toString(o));
}
<强>结果强>
[John, Red, Apple]
[John, Red, Tomatoe]
[John, Red, Bananna]
[John, Green, Apple]
[John, Green, Tomatoe]
[John, Green, Bananna]
[John, Blue, Apple]
[John, Blue, Tomatoe]
[John, Blue, Bananna]
[John, Orange, Apple]
[John, Orange, Tomatoe]
[John, Orange, Bananna]
[Sarah, Red, Apple]
[Sarah, Red, Tomatoe]
[Sarah, Red, Bananna]
[Sarah, Green, Apple]
[Sarah, Green, Tomatoe]
[Sarah, Green, Bananna]
[Sarah, Blue, Apple]
[Sarah, Blue, Tomatoe]
[Sarah, Blue, Bananna]
[Sarah, Orange, Apple]
[Sarah, Orange, Tomatoe]
[Sarah, Orange, Bananna]
笛卡尔积函数
public static List<String[]> GetCrossProduct(Map<Integer, List<String>> lists)
{
List<String[]> results = new ArrayList<String[]>();
GetCrossProduct(results, lists, 0, new String[(lists.size())]);
return results;
}
private void GetCrossProduct(List<String[]> results, Map<Integer, List<String>> lists, int depth, String[] current)
{
for (int i = 0; i < lists.get(depth).size(); i++)
{
current[depth] = lists.get(depth).get(i);
if (depth < lists.keySet().size() - 1)
GetCrossProduct(results, lists, depth + 1, current);
else{
results.add(Arrays.copyOf(current,current.length));
}
}
}
答案 9 :(得分:1)
感谢Vitalii Fedorenko,我可以使用来获得相同的列表
Lists.cartesianProduct(..)
我认为,如果生产代码需要它,那么最好依赖像Guava这样经过验证的库,而不是构建我们自己的库。
答案 10 :(得分:0)
您可以递归生成组合。
public class Main {
public static void main(String[] args) {
int[][] arr = new int[][] { { 1, 2, 3 }, { 3, 2 }, { 5, 6, 7 } };
cartesianProduct(arr, 0, new int[arr.length]);
}
private static void cartesianProduct(int[][] arr, int level, int[] cp) {
if (level == arr.length) {
for (int x : cp)
System.out.print(x + " ");
System.out.println();
return;
}
for (int i = 0; i < arr[level].length; i++) {
cp[level] = arr[level][i];
cartesianProduct(arr, level + 1, cp);
}
}
}
输出:
1 3 5
1 3 6
1 3 7
1 2 5
1 2 6
1 2 7
2 3 5
2 3 6
2 3 7
2 2 5
2 2 6
2 2 7
3 3 5
3 3 6
3 3 7
3 2 5
3 2 6
3 2 7
答案 11 :(得分:0)
递归解决方案:
public <T> List<List<T>> cartesianProduct(int i, List<T>... a) {
if(i == a.length ) {
List<List<T>> result = new ArrayList<>();
result.add(new ArrayList());
return result;
}
List<List<T>> next = cartesianProduct(i+1, a);
List<List<T>> result = new ArrayList<>();
for(int j=0; j < a[i].size(); j++) {
for(int k=0; k < next.size(); k++) {
List<T> concat = new ArrayList();
concat.add(a[i].get(j));
concat.addAll(next.get(k));
result.add(concat);
}
}
return result;
}