1

我有一个集合列表:

setlist = [s1,s2,s3...sn]

我想要一组的全方位比较,即 2^n 组数:

setIntersection = [s1 ∩ s2, s1 ∩ s2 ∩ s3, ....., s2 ∩ s3 ∩ s4, ...., sn-1 ∩ sn]

在 Java 中执行此操作的最佳方法是什么?

例如,如果我只使用 5 套。我希望能够填充5 圈维恩图的所有重叠部分。

我正在尝试使用一组列表来做到这一点:

List<Set<People>> lListSets = new ArrayList<Set<People>>();
for (DaysObject day : listOfDaysInJanuary) {
        lListSets.add(day.peopleOneInternet());
}
findPowerSetsAndCompare(lListSets, listOfDaysInJanuary);

我想找到一些结果,例如:

January 1 (Bob, Sally, Tommy)
January 1, January 2 (Sally, Tommy)
...
so on for all possible combination of days.

本质上,我要问的是如何应用与集合并集相结合的powerset 算法。

4

1 回答 1

4

在 Java 中执行此操作的最佳方法是什么?

您描述的第一部分是一个powerset(因为我上周编辑了您的问题以包括在内)。然后,您将获得 powerset 中每组集合的交集。

因为你正在做一个集合的幂集,而不是像整数这样的简单的幂集,所以实现会涉及更多。

额外学分

我写了一个你的要求的基本实现,作为你如何做的一个例子。此示例中的所有方法和类型都是Example该类的成员。

main仅具有演示工作代码的方法的示例类。我相信你会原谅我Date在演示中使用不推荐使用的构造函数。

import java.text.*;
import java.util.*;

public class Example
{
    public static void main(String[] args) {
        // create simple test harness
        Set<PeopleByDays> peepsByDay = new HashSet<PeopleByDays>();
        peepsByDay.add(new PeopleByDays(new Date(2015 - 1900, Calendar.JANUARY, 1),
            Person.BOB, Person.FRANK, Person.JIM, Person.JUDY, Person.SALLY));
        peepsByDay.add(new PeopleByDays(new Date(2015 - 1900, Calendar.JANUARY, 2),
            Person.BOB, Person.FRANK, Person.JIM, Person.JUDY, Person.TOMMY));
        peepsByDay.add(new PeopleByDays(new Date(2015 - 1900, Calendar.JANUARY, 3),
            Person.BOB, Person.FRANK, Person.JIM, Person.SALLY, Person.TOMMY));
        peepsByDay.add(new PeopleByDays(new Date(2015 - 1900, Calendar.JANUARY, 4),
            Person.BOB, Person.FRANK, Person.JUDY, Person.SALLY, Person.TOMMY));
        peepsByDay.add(new PeopleByDays(new Date(2015 - 1900, Calendar.JANUARY, 5),
            Person.BOB, Person.JIM, Person.JUDY, Person.SALLY, Person.TOMMY));

        // make powerSet, then intersect, then sort
        Set<Set<PeopleByDays>> powerPeeps = powerSet(peepsByDay);
        List<PeopleByDays> powerPeepsIntersected = intersect(powerPeeps);
        sort(powerPeepsIntersected);

        // print out results
        for (PeopleByDays peeps: powerPeepsIntersected) {
            String daysFormatted = format(peeps.getDays());
            System.out.print(daysFormatted);
            System.out.println(peeps);
        }
    }

    // all other Example members as defined in this answer
}

Person是人名的简单enum类型。在这里使用枚举的好处是它可以处理所需行为的适当equals()和实现。hashCode()HashSet

    static enum Person {
        BOB, FRANK, JIM, JUDY, SALLY, TOMMY;
    }

PeopleByDays扩展HashSet<Person>以收集一组额外的Date对象来表示日期。覆盖retainAll()(相交)以组合天数;覆盖equals()hashSet()外部集合中的正确行为。

    static class PeopleByDays extends HashSet<Person> {
        private final Set<Date> days = new HashSet<Date>();

        public PeopleByDays() {
            super();
        }
        public PeopleByDays(Date day, Person... people) {
            super(Arrays.asList(people));
            this.days.add(day);
        }
        public PeopleByDays(PeopleByDays other) {
            super(other);
            this.days.addAll(other.days);
        }

        public List<Date> getDays() {
            return new ArrayList<Date>(this.days);
        }

        @Override
        public boolean retainAll(Collection<?> c) {
            if (c instanceof PeopleByDays) {
                this.days.addAll(((PeopleByDays)c).days);
            }
            return super.retainAll(c);
        }

        @Override
        public boolean equals(Object o) {
            return super.equals(o) && this.days.equals(((PeopleByDays) o).days);
        }
        @Override
        public int hashCode() {
            return super.hashCode() + this.days.hashCode();
        }
    }

powerSet()方法,逐字取自这个答案

    public static <T> Set<Set<T>> powerSet(Set<T> originalSet) {
        Set<Set<T>> sets = new HashSet<Set<T>>();
        if (originalSet.isEmpty()) {
            sets.add(new HashSet<T>());
            return sets;
        }
        List<T> list = new ArrayList<T>(originalSet);
        T head = list.get(0);
        Set<T> rest = new HashSet<T>(list.subList(1, list.size()));
        for (Set<T> set: powerSet(rest)) {
            Set<T> newSet = new HashSet<T>();
            newSet.add(head);
            newSet.addAll(set);
            sets.add(newSet);
            sets.add(set);
        }
        return sets;
    }

intersect()方法为 powerset 中的每组集合创建交集。

    static List<PeopleByDays> intersect(Set<Set<PeopleByDays>> powerSet) {
        List<PeopleByDays> intersected = new ArrayList<PeopleByDays>();
        for (Set<PeopleByDays> powerElement: powerSet) {
            PeopleByDays intersection = null;
            if (powerElement.isEmpty()) {
                intersection = new PeopleByDays();
            } else for (PeopleByDays peeps: powerElement) {
                if (intersection == null) {
                    intersection = new PeopleByDays(peeps);
                } else {
                    intersection.retainAll(peeps);
                }
            }
            intersected.add(intersection);
        }
        return intersected;
    }

sort()方法按日期对生成的相交集进行排序。

    static void sort(List<PeopleByDays> peeps) {
        Collections.sort(peeps, new Comparator<PeopleByDays>() {
            @Override
            public int compare(PeopleByDays p1, PeopleByDays p2) {
                List<Date> days1 = p1.getDays();
                List<Date> days2 = p2.getDays();
                Collections.sort(days1);
                Collections.sort(days2);
                for (int i = 0; i < days1.size() && i < days2.size(); i++) {
                    int compare = days1.get(i).compareTo(days2.get(i));
                    if (compare != 0) {
                        return compare;
                    }
                }
                return days1.size() - days2.size();
            }
        });
    }

format()方法来格式化每个交叉点的天数列表。

    static String format(List<Date> days) {
        if (days.isEmpty()) {
            return "";
        }
        StringBuilder sb = new StringBuilder();
        DateFormat format = new SimpleDateFormat("MMM d");
        Collections.sort(days);
        String separator = "";
        for (Date day: days) {
            sb.append(separator);
            sb.append(format.format(day));
            separator = ", ";
        }
        sb.append(" ");
        return sb.toString();
    }

最后是输出。

[]
Jan 1 [BOB, JUDY, FRANK, JIM, SALLY]
Jan 1, Jan 2 [BOB, JUDY, FRANK, JIM]
Jan 1, Jan 2, Jan 3 [BOB, FRANK, JIM]
Jan 1, Jan 2, Jan 3, Jan 4 [BOB, FRANK]
Jan 1, Jan 2, Jan 3, Jan 4, Jan 5 [BOB]
Jan 1, Jan 2, Jan 3, Jan 5 [BOB, JIM]
Jan 1, Jan 2, Jan 4 [BOB, JUDY, FRANK]
Jan 1, Jan 2, Jan 4, Jan 5 [BOB, JUDY]
Jan 1, Jan 2, Jan 5 [BOB, JUDY, JIM]
Jan 1, Jan 3 [BOB, FRANK, JIM, SALLY]
Jan 1, Jan 3, Jan 4 [BOB, FRANK, SALLY]
Jan 1, Jan 3, Jan 4, Jan 5 [BOB, SALLY]
Jan 1, Jan 3, Jan 5 [BOB, JIM, SALLY]
Jan 1, Jan 4 [BOB, JUDY, FRANK, SALLY]
Jan 1, Jan 4, Jan 5 [BOB, JUDY, SALLY]
Jan 1, Jan 5 [BOB, JUDY, JIM, SALLY]
Jan 2 [BOB, JUDY, TOMMY, FRANK, JIM]
Jan 2, Jan 3 [BOB, TOMMY, FRANK, JIM]
Jan 2, Jan 3, Jan 4 [BOB, TOMMY, FRANK]
Jan 2, Jan 3, Jan 4, Jan 5 [BOB, TOMMY]
Jan 2, Jan 3, Jan 5 [BOB, TOMMY, JIM]
Jan 2, Jan 4 [BOB, JUDY, TOMMY, FRANK]
Jan 2, Jan 4, Jan 5 [BOB, JUDY, TOMMY]
Jan 2, Jan 5 [BOB, JUDY, TOMMY, JIM]
Jan 3 [BOB, TOMMY, FRANK, JIM, SALLY]
Jan 3, Jan 4 [BOB, TOMMY, FRANK, SALLY]
Jan 3, Jan 4, Jan 5 [BOB, TOMMY, SALLY]
Jan 3, Jan 5 [BOB, TOMMY, JIM, SALLY]
Jan 4 [BOB, JUDY, TOMMY, FRANK, SALLY]
Jan 4, Jan 5 [BOB, JUDY, TOMMY, SALLY]
Jan 5 [BOB, JUDY, TOMMY, JIM, SALLY]

希望有帮助。我修改它的时间比我预期的要长得多;)尽管如此,仍然没有对输出中的人名进行排序。

于 2015-01-29T01:54:37.320 回答