All Downloads are FREE. Search and download functionalities are using the official Maven repository.

fr.boreal.model.partition.Partition Maven / Gradle / Ivy

The newest version!
package fr.boreal.model.partition;

import java.util.AbstractSet;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author Guillaume Pérution-Kihli
 * 
* This class represents the partition of a set which contains elements of type E * The data structure used to manage the partition is union-find, which is optimal * in terms of algorithmic complexity for this problem * * @param the type of elements of the partition * */ public class Partition { private final Map nodes; private final Set representatives; private Integer hashCode = null; private Comparator comparator = null; /** * Creates a new empty Partition */ public Partition () { nodes = new HashMap<>(); representatives = new HashSet<>(); } /** * Creates a new empty Partition with the given comparator * @param comparator the comparator to choose the representative of a class */ public Partition(Comparator comparator) { this.comparator = comparator; nodes = new HashMap<>(); representatives = new HashSet<>(); } /** * Creates a new Partition with the given elements, each in its own class * @param initialElements the initial elements of this partition */ public Partition (Set initialElements) { this(initialElements, null); } /** * Creates a new Partition with the given elements and comparator * @param initialElements the initial elements of this partition * @param comparator the comparator to choose the representative of a class */ public Partition (Set initialElements, Comparator comparator) { this(comparator); initialElements.forEach(this::addNode); } /** * Creates a new Partition with the given classes * @param partition the initial elements represented as multiple partitions */ public Partition (Collection> partition) { this(partition, null); } /** * Creates a new Partition with the given classes and comparator * @param partition the initial elements represented as multiple partitions * @param comparator the comparator to choose the representative of a class */ public Partition (Collection> partition, Comparator comparator) { this(comparator); partition.forEach(this::addClass); } /** * Creates a new Partition copying the given one * @param toCopy partition to copy */ public Partition (Partition toCopy) { this(toCopy.comparator); this.join(toCopy); } /** * Creates a new Partition copying the given one but overriding its comparator * @param toCopy partition to copy * @param comparator the comparator to choose the representative of a class */ public Partition (Partition toCopy, Comparator comparator) { this(comparator); this.join(toCopy); } //////////////////// // PUBLIC METHODS // //////////////////// /** * Add a class to the partition * If there are some common elements with existing classes, * the class will be merged with these existing classes * @param c : the class to add */ public void addClass (Set c) { eraseMemoizedValues(); if (!c.isEmpty()) { var it = c.iterator(); Node root = getNode(it.next()); while (it.hasNext()) { union(getNode(it.next()), root); } } } /** * Returns a class' representative of x * If x is not already in the partition, it is added in its own class * The returned representative is always the same if the partition * is not modified * @param x : the element from which we want the class' representative * @return a class' representative of x */ public E getRepresentative (E x) { return find(getNode(x)).value; } /** * Returns an immutable Set containing all the class' * elements of x * @param x : the element from which we want the class * @return an iterator on the class' elements of x */ public Set getClass (E x) { return new ClassView(x); } /** * Merge the classes of x and y * If x or y are not yet in the partition, they are be added to it * @param x : the first element of the couple * @param y : the second element of the couple */ public void union (E x, E y) { eraseMemoizedValues(); union(getNode(x), getNode(y)); } /** * Join the class of another partition * This operation consists of merge the classes of this partition and the * other one when they share a common element * @param other : the partition we want to join */ public void join (Partition other) { eraseMemoizedValues(); for (E e : other.nodes.keySet()) { union(e, other.getRepresentative(e)); } } /** * Return all the classes of the partition * @return a list of immutable sets that is the list of all classes */ public List> getClasses () { return getRepresentatives().stream() .map(r -> new ClassView(r)) .collect(Collectors.toList()); } /** * Return all the elements contained in the partition * @return an immutable set containing all the elements of the partition */ public Set getElements () { return Collections.unmodifiableSet(this.nodes.keySet()); } @Override public synchronized int hashCode () { if (hashCode == null) { hashCode = getClasses().stream() /* We multiply the hash code of each class by its * size in order that two different partitions with * the same elements does not always have the same * hash codes (it avoids some collisions) */ .map(c -> c.hashCode() * c.size()) .reduce(0, Integer::sum); } return hashCode; } @Override public boolean equals (Object o) { if (o == this) return true; if (!(o instanceof Partition other)) return false; if (nodes.size() != other.nodes.size()) return false; if (!nodes.keySet().equals(other.nodes.keySet())) return false; List> otherClasses = other.getClasses(); return new HashSet<>(otherClasses).containsAll(this.getClasses()); } @Override public String toString () { return getClasses().toString(); } ///////////////////// // PRIVATE METHODS // ///////////////////// private Set getRepresentatives () { return representatives.stream().map(n -> n.value).collect(Collectors.toSet()); } private void addNode (E x) { if (!nodes.containsKey(x)) { nodes.put(x, new Node(x)); } } private Node getNode(E x) { Node node = nodes.get(x); if (node == null) { addNode(x); node = nodes.get(x); } return node; } private void union (Node x, Node y) { link(find(x), find(y)); } private void link (Node x, Node y) { if (x != y) { if (x.size > y.size) { this.order(x, y); y.parent = x; representatives.remove(y); x.children.add(y); x.size += y.size; } else { this.order(y, x); x.parent = y; representatives.remove(x); y.children.add(x); y.size += x.size; } } } private void order(Node x, Node y) { if(this.comparator != null && this.comparator.compare(x.value, y.value) > 0) { E aux = x.value; x.value = y.value; y.value = aux; } } private Node find (Node x) { if (x.parent != x.parent.parent) { x.parent.children.remove(x); var old_parent = x.parent; x.parent = find(x.parent); old_parent.size -= x.size; x.parent.children.add(x); } return x.parent; } /** * Erase the memoized values */ protected void eraseMemoizedValues () { hashCode = null; } class Node { int size; Node parent; final Set children; E value; Node (E value) { this.size = 1; this.parent = this; children = new HashSet<>(); this.value = value; representatives.add(this); } @Override public String toString() { return "(size : "+size+", parent: "+parent.value+", children: " +(children.stream().map(c -> c.value).toList())+", value: "+value+")"; } } class ClassIterator implements Iterator { final Queue queue; ClassIterator (E x) { this.queue = new LinkedList<>(); queue.add(find(getNode(x))); } @Override public boolean hasNext() { return !queue.isEmpty(); } @Override public E next() { Node n = queue.poll(); queue.addAll(n.children); return n.value; } } class ClassView extends AbstractSet { final E x; ClassView (E x) { this.x = x; } @Override public boolean contains(Object o) { if (!nodes.containsKey(o)) { return false; } return find(getNode(x)).equals(find(nodes.get(o))); } @Override public Iterator iterator() { return new ClassIterator(x); } @Override public int size() { return find(getNode(x)).size; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy