迭代计算任意数量的集合的笛卡尔积
我想计算 Java 中任意数量的非空集合的笛卡尔积。
我已经编写了迭代代码......
public static <T> List<Set<T>> cartesianProduct(List<Set<T>> list) {
List<Iterator<T>> iterators = new ArrayList<Iterator<T>>(list.size());
List<T> elements = new ArrayList<T>(list.size());
List<Set<T>> toRet = new ArrayList<Set<T>>();
for (int i = 0; i < list.size(); i++) {
iterators.add(list.get(i).iterator());
elements.add(iterators.get(i).next());
}
for (int j = 1; j >= 0;) {
toRet.add(Sets.newHashSet(elements));
for (j = iterators.size()-1; j >= 0 && !iterators.get(j).hasNext(); j--) {
iterators.set(j, list.get(j).iterator());
elements.set(j, iterators.get(j).next());
}
elements.set(Math.abs(j), iterators.get(Math.abs(j)).next());
}
return toRet;
}
但我发现它相当不优雅。 有人有更好的、仍然迭代的解决方案吗?使用某种奇妙的类似函数式方法的解决方案? 否则...有关如何改进它的建议?错误?
I want to compute the cartesian product of an arbitrary number of nonempty sets in Java.
I've wrote that iterative code...
public static <T> List<Set<T>> cartesianProduct(List<Set<T>> list) {
List<Iterator<T>> iterators = new ArrayList<Iterator<T>>(list.size());
List<T> elements = new ArrayList<T>(list.size());
List<Set<T>> toRet = new ArrayList<Set<T>>();
for (int i = 0; i < list.size(); i++) {
iterators.add(list.get(i).iterator());
elements.add(iterators.get(i).next());
}
for (int j = 1; j >= 0;) {
toRet.add(Sets.newHashSet(elements));
for (j = iterators.size()-1; j >= 0 && !iterators.get(j).hasNext(); j--) {
iterators.set(j, list.get(j).iterator());
elements.set(j, iterators.get(j).next());
}
elements.set(Math.abs(j), iterators.get(Math.abs(j)).next());
}
return toRet;
}
...but I found it rather inelegant.
Someone has a better, still iterative solution? A solution that uses some wonderful functional-like approach?
Otherwise... suggestion about how to improve it? Errors?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
我编写了一个解决方案,不需要您在内存中填充大量集合。不幸的是,所需的代码长达数百行。您可能需要等到它出现在 Guava 项目中(https://github.com/google/guava),我希望能在今年年底实现。对不起。 :(
请注意,如果笛卡尔生成的集数是编译时已知的固定数字,您可能不需要这样的实用程序 - 您可以只使用该数量的嵌套 for 循环。
编辑: 代码现已发布
Sets.cartesianProduct()
我想您会对它感到非常满意。它只会根据您的要求创建单独的列表它们;不会用所有 MxNxPxQ 填充内存。
如果您想检查源代码,则为 此处。
尽情享受吧!
I've written a solution that doesn't require you to fill up a large collection in memory. Unfortunately, the code required is hundreds of lines long. You may have to wait until it appears in the Guava project (https://github.com/google/guava), which I hope will be by the end of the year. Sorry. :(
Note that you may not need such a utility if the number of sets you're cartesian-producting is a fixed number known at compile time -- you could just use that number of nested for loops.
EDIT: the code is released now.
Sets.cartesianProduct()
I think you'll be very happy with it. It only creates the individual lists as you ask for them; doesn't fill up memory with all MxNxPxQ of them.
If you want to inspect the source, it's here.
Enjoy!
使用 Google Guava 19 和 Java 8 非常简单:
假设您有要关联的所有数组的 List...
制作不可变列表的方法如下:
输出如下:
Using Google Guava 19 and Java 8 is very simple:
Say you have the List of all arrays you want to associate...
The method to make the list of immutable lists is as follows:
The output is as follows:
这是我编写的一个迭代的、惰性的实现。该界面与 Google 的 Sets.cartesianProduct 非常相似,但它更灵活一些:它处理 Iterables 而不是 Sets。此代码及其单元测试位于 https://gist.github.com/1911614。
Here's an iterative, lazy implementation I wrote. The interface is very similar to Google's Sets.cartesianProduct, but it's a bit more flexible: it deals in Iterables instead of Sets. This code and its unit tests are at https://gist.github.com/1911614.
基于索引的解决方案
使用索引是一种简单的替代方案,它速度快、内存效率高,并且可以处理任意数量的集合。实现 Iterable 可以在 for-each 循环中轻松使用。有关使用示例,请参阅#main 方法。
Index-based solution
Working with the indices is a simple alternative that is fast and memory-efficient and can handle any number of sets. Implementing Iterable allows easy use in a for-each loop. See the #main method for a usage example.
以下答案使用迭代而不是递归。它使用与我之前的答案相同的
Tuple
类。这是一个单独的答案,因为恕我直言,两者都是有效的、不同的方法。
这是新的主类:
The following answer uses iteration and not recursion. It uses the same
Tuple
class from my previous answer.It is a separate answer because IMHO both are valid, different approaches.
Here is the new main class:
这是一种惰性迭代器方法,它使用函数来生成适当的输出类型。
Here is a lazy iterator approach that uses a function to produce an appropriate output type.
我为字符串表编写了递归笛卡尔积算法。您可以将其修改为具有集合。下面是算法。我的文章中也对此进行了解释
I wrote an recursive cartesian product algorithm for table of Strings. You can modify it to have sets istead. Below is the algorithm. It's also explained in my article
您可以使用
Stream.reduce
方法。Java 9 没有额外的库。
输出(元素的顺序可能不同):
另请参阅:任意数量集合的笛卡尔积< /sup>
You can use
Stream.reduce
method.Java 9 without additional libraries.
Output (the order of the elements may differ):
See also: Cartesian product of an arbitrary number of sets
您可能对有关笛卡尔积的另一个问题感兴趣(编辑:删除以保存超链接,搜索标签笛卡尔积)。这个答案有一个很好的递归解决方案,我很难改进。您是否特别想要迭代解决方案而不是递归解决方案?
在查看了 Perl 中堆栈溢出的另一个迭代解决方案和一个干净的解释,这是另一个解决方案:
You might be interested in Another question about cartesian products (edit: removed to conserve hyperlinks, search for the tag cartesian products). That answer has a nice recursive solution that I'd be hard pressed to improve on. Do you specifically want an iterative solution instead of recursive solution?
After looking at another iterative solution on stack overflow in perl and a clean explanation, here is another solution:
我相信这是正确的。它不追求效率,而是通过递归和抽象来追求干净的风格。
关键的抽象是引入一个简单的
Tuple
类。这有助于以后的泛型:有了这个类,我们可以编写一个像这样的类:
我有一个很好的例子,但为了简洁起见,它被省略了。
I believe this is correct. It is not seeking efficiency, but a clean style through recursion and abstraction.
The key abstraction is to introduce a simple
Tuple
class. This helps the generics later:With this class, we can write a class like so:
I have a decent example of this working, but it is omitted for brevity.