renamed to MaximumIntegerWeightBipartiteAuctionMatching
This commit is contained in:
@@ -1,145 +0,0 @@
|
|||||||
import org.jgrapht.Graph;
|
|
||||||
import org.jgrapht.GraphTests;
|
|
||||||
import org.jgrapht.alg.interfaces.MatchingAlgorithm;
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Maximum weight matching in bipartite graphs with strictly integer edge weights, found using the
|
|
||||||
unscaled forward auction algorithm
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class MaximumWeightBipartiteAuctionMatching<V, E> implements MatchingAlgorithm<V, E> {
|
|
||||||
|
|
||||||
private final Graph<V, E> graph;
|
|
||||||
private final Set<V> partition1;
|
|
||||||
private final Set<V> partition2;
|
|
||||||
private final BigDecimal delta;
|
|
||||||
private final Set<E> matching;
|
|
||||||
private BigDecimal matchingWeight;
|
|
||||||
private boolean swappedPartitions = false;
|
|
||||||
|
|
||||||
public MaximumWeightBipartiteAuctionMatching(Graph<V, E> graph, Set<V> partition1, Set<V> partition2) {
|
|
||||||
this.graph = GraphTests.requireUndirected(graph);
|
|
||||||
this.partition1 = Objects.requireNonNull(partition1, "Partition 1 cannot be null");
|
|
||||||
this.partition2 = Objects.requireNonNull(partition2, "Partition 2 cannot be null");
|
|
||||||
int n = Math.max(partition1.size(), partition2.size());
|
|
||||||
this.delta = BigDecimal.valueOf(1 / ((double) n + 1));
|
|
||||||
// this.delta = BigDecimal.valueOf(n/2);
|
|
||||||
this.matching = new LinkedHashSet<>();
|
|
||||||
this.matchingWeight = BigDecimal.ZERO;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Method coded using MaximumWeightBipartiteMatching.class from JgraphT as a model
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Matching<V, E> getMatching() {
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Test input instance
|
|
||||||
*/
|
|
||||||
if (!GraphTests.isSimple(graph)) {
|
|
||||||
throw new IllegalArgumentException("Only simple graphs supported");
|
|
||||||
}
|
|
||||||
if (!GraphTests.isBipartitePartition(graph, partition1, partition2)) {
|
|
||||||
throw new IllegalArgumentException("Graph partition is not bipartite");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
If the two partitions are different sizes, the bidders must be the smaller of the two partitions.
|
|
||||||
*/
|
|
||||||
Set<V> items;
|
|
||||||
Set<V> bidders;
|
|
||||||
if (partition2.size() >= partition1.size()) {
|
|
||||||
bidders = partition1;
|
|
||||||
items = partition2;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
bidders = partition2;
|
|
||||||
items = partition1;
|
|
||||||
swappedPartitions = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Create a map to track the owner of each item, which is initially null,
|
|
||||||
and a map to track the price of each item, which is initially 0.
|
|
||||||
*/
|
|
||||||
Map<V, V> owners = new HashMap<>();
|
|
||||||
Map<V, BigDecimal> prices = new HashMap<>();
|
|
||||||
for(V item: items) {
|
|
||||||
owners.put(item, null);
|
|
||||||
prices.put(item, BigDecimal.ZERO);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Initialize queue of all bidders that don't currently own an item
|
|
||||||
Queue<V> unmatchedBidders = new ArrayDeque<>();
|
|
||||||
for(V bidder: bidders) {
|
|
||||||
unmatchedBidders.offer(bidder);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (unmatchedBidders.size() > 0) {
|
|
||||||
V bidder = unmatchedBidders.poll();
|
|
||||||
V item = null;
|
|
||||||
BigDecimal bestValue = BigDecimal.valueOf(-1.0);
|
|
||||||
//find the item that offers the best value for this bidder
|
|
||||||
for (E edge: graph.edgesOf(bidder)) {
|
|
||||||
double weight = graph.getEdgeWeight(edge);
|
|
||||||
if(weight == 0.0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
V tmp = getItem(bidder, edge);
|
|
||||||
BigDecimal value = BigDecimal.valueOf(weight).subtract(prices.get(tmp));
|
|
||||||
if (value.compareTo(bestValue) >= 0) {
|
|
||||||
bestValue = value;
|
|
||||||
item = tmp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(bestValue.compareTo(BigDecimal.ZERO) >= 0) {
|
|
||||||
V formerOwner = owners.get(item);
|
|
||||||
BigDecimal formerPrice = prices.get(item);
|
|
||||||
if (formerOwner != null) {
|
|
||||||
unmatchedBidders.offer(formerOwner);
|
|
||||||
}
|
|
||||||
owners.put(item, bidder);
|
|
||||||
prices.put(item, formerPrice.add(delta));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (V item: owners.keySet()) {
|
|
||||||
if (owners.get(item) != null) {
|
|
||||||
matching.add(graph.getEdge(item, owners.get(item)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for(E edge: matching) {
|
|
||||||
this.matchingWeight = this.matchingWeight.add(BigDecimal.valueOf(graph.getEdgeWeight(edge)));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return new MatchingImpl<>(graph, matching, matchingWeight.doubleValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
private V getItem(V bidder, E edge) {
|
|
||||||
if (swappedPartitions) {
|
|
||||||
return graph.getEdgeSource(edge);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return graph.getEdgeTarget(edge);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private V getBidder(V item, E edge) {
|
|
||||||
if (swappedPartitions) {
|
|
||||||
return graph.getEdgeTarget(edge);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return graph.getEdgeSource(edge);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public BigDecimal getMatchingWeight() {
|
|
||||||
return matchingWeight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user