shithub: puzzles

Download patch

ref: 3d04dd3335a2c4c6007ff4e2a58a2855c7a9c52a
parent: dcc4d82b23095b07673d7c13cb2439c738a67fa1
author: Simon Tatham <[email protected]>
date: Sat Apr 21 13:03:10 EDT 2018

Remove maxflow completely.

Its ability to solve general network flow problems was never actually
used in this code base; it was _always_ used for the restricted
problem of finding a matching in an unweighted bipartite graph. So now
I've switched all its clients over to the new matching.c, maxflow is
no longer needed.

--- a/maxflow.c
+++ /dev/null
@@ -1,461 +1,0 @@
-/*
- * Edmonds-Karp algorithm for finding a maximum flow and minimum
- * cut in a network. Almost identical to the Ford-Fulkerson
- * algorithm, but apparently using breadth-first search to find the
- * _shortest_ augmenting path is a good way to guarantee
- * termination and ensure the time complexity is not dependent on
- * the actual value of the maximum flow. I don't understand why
- * that should be, but it's claimed on the Internet that it's been
- * proved, and that's good enough for me. I prefer BFS to DFS
- * anyway :-)
- */
-
-#include <assert.h>
-#include <stdlib.h>
-#include <stdio.h>
-
-#include "maxflow.h"
-
-#include "puzzles.h"		       /* for snewn/sfree */
-
-int maxflow_with_scratch(void *scratch, int nv, int source, int sink,
-			 int ne, const int *edges, const int *backedges,
-			 const int *capacity, int *flow, int *cut)
-{
-    int *todo = (int *)scratch;
-    int *prev = todo + nv;
-    int *firstedge = todo + 2*nv;
-    int *firstbackedge = todo + 3*nv;
-    int i, j, head, tail, from, to;
-    int totalflow;
-
-    /*
-     * Scan the edges array to find the index of the first edge
-     * from each node.
-     */
-    j = 0;
-    for (i = 0; i < ne; i++)
-	while (j <= edges[2*i])
-	    firstedge[j++] = i;
-    while (j < nv)
-	firstedge[j++] = ne;
-    assert(j == nv);
-
-    /*
-     * Scan the backedges array to find the index of the first edge
-     * _to_ each node.
-     */
-    j = 0;
-    for (i = 0; i < ne; i++)
-	while (j <= edges[2*backedges[i]+1])
-	    firstbackedge[j++] = i;
-    while (j < nv)
-	firstbackedge[j++] = ne;
-    assert(j == nv);
-
-    /*
-     * Start the flow off at zero on every edge.
-     */
-    for (i = 0; i < ne; i++)
-	flow[i] = 0;
-    totalflow = 0;
-
-    /*
-     * Repeatedly look for an augmenting path, and follow it.
-     */
-    while (1) {
-
-	/*
-	 * Set up the prev array.
-	 */
-	for (i = 0; i < nv; i++)
-	    prev[i] = -1;
-
-	/*
-	 * Initialise the to-do list for BFS.
-	 */
-	head = tail = 0;
-	todo[tail++] = source;
-
-	/*
-	 * Now do the BFS loop.
-	 */
-	while (head < tail && prev[sink] < 0) {
-	    from = todo[head++];
-
-	    /*
-	     * Try all the forward edges out of node `from'. For a
-	     * forward edge to be valid, it must have flow
-	     * currently less than its capacity.
-	     */
-	    for (i = firstedge[from]; i < ne && edges[2*i] == from; i++) {
-		to = edges[2*i+1];
-		if (to == source || prev[to] >= 0)
-		    continue;
-		if (capacity[i] >= 0 && flow[i] >= capacity[i])
-		    continue;
-		/*
-		 * This is a valid augmenting edge. Visit node `to'.
-		 */
-		prev[to] = 2*i;
-		todo[tail++] = to;
-	    }
-
-	    /*
-	     * Try all the backward edges into node `from'. For a
-	     * backward edge to be valid, it must have flow
-	     * currently greater than zero.
-	     */
-	    for (i = firstbackedge[from];
-		 j = backedges[i], i < ne && edges[2*j+1]==from; i++) {
-		to = edges[2*j];
-		if (to == source || prev[to] >= 0)
-		    continue;
-		if (flow[j] <= 0)
-		    continue;
-		/*
-		 * This is a valid augmenting edge. Visit node `to'.
-		 */
-		prev[to] = 2*j+1;
-		todo[tail++] = to;
-	    }
-	}
-
-	/*
-	 * If prev[sink] is non-null, we have found an augmenting
-	 * path.
-	 */
-	if (prev[sink] >= 0) {
-	    int max;
-
-	    /*
-	     * Work backwards along the path figuring out the
-	     * maximum flow we can add.
-	     */
-	    to = sink;
-	    max = -1;
-	    while (to != source) {
-		int spare;
-
-		/*
-		 * Find the edge we're currently moving along.
-		 */
-		i = prev[to];
-		from = edges[i];
-		assert(from != to);
-
-		/*
-		 * Determine the spare capacity of this edge.
-		 */
-		if (i & 1)
-		    spare = flow[i / 2];   /* backward edge */
-		else if (capacity[i / 2] >= 0)
-		    spare = capacity[i / 2] - flow[i / 2];   /* forward edge */
-		else
-		    spare = -1;	       /* unlimited forward edge */
-
-		assert(spare != 0);
-
-		if (max < 0 || (spare >= 0 && spare < max))
-		    max = spare;
-
-		to = from;
-	    }
-	    /*
-	     * Fail an assertion if max is still < 0, i.e. there is
-	     * an entirely unlimited path from source to sink. Also
-	     * max should not _be_ zero, because by construction
-	     * this _should_ be an augmenting path.
-	     */
-	    assert(max > 0);
-
-	    /*
-	     * Now work backwards along the path again, this time
-	     * actually adjusting the flow.
-	     */
-	    to = sink;
-	    while (to != source) {
-		/*
-		 * Find the edge we're currently moving along.
-		 */
-		i = prev[to];
-		from = edges[i];
-		assert(from != to);
-
-		/*
-		 * Adjust the edge.
-		 */
-		if (i & 1)
-		    flow[i / 2] -= max;  /* backward edge */
-		else
-		    flow[i / 2] += max;  /* forward edge */
-
-		to = from;
-	    }
-
-	    /*
-	     * And adjust the overall flow counter.
-	     */
-	    totalflow += max;
-
-	    continue;
-	}
-
-	/*
-	 * If we reach here, we have failed to find an augmenting
-	 * path, which means we're done. Output the `cut' array if
-	 * required, and leave.
-	 */
-	if (cut) {
-	    for (i = 0; i < nv; i++) {
-		if (i == source || prev[i] >= 0)
-		    cut[i] = 0;
-		else
-		    cut[i] = 1;
-	    }
-	}
-	return totalflow;
-    }
-}
-
-int maxflow_scratch_size(int nv)
-{
-    return (nv * 4) * sizeof(int);
-}
-
-void maxflow_setup_backedges(int ne, const int *edges, int *backedges)
-{
-    int i, n;
-
-    for (i = 0; i < ne; i++)
-	backedges[i] = i;
-
-    /*
-     * We actually can't use the C qsort() function, because we'd
-     * need to pass `edges' as a context parameter to its
-     * comparator function. So instead I'm forced to implement my
-     * own sorting algorithm internally, which is a pest. I'll use
-     * heapsort, because I like it.
-     */
-
-#define LESS(i,j) ( (edges[2*(i)+1] < edges[2*(j)+1]) || \
-		    (edges[2*(i)+1] == edges[2*(j)+1] && \
-		     edges[2*(i)] < edges[2*(j)]) )
-#define PARENT(n) ( ((n)-1)/2 )
-#define LCHILD(n) ( 2*(n)+1 )
-#define RCHILD(n) ( 2*(n)+2 )
-#define SWAP(i,j) do { int swaptmp = (i); (i) = (j); (j) = swaptmp; } while (0)
-
-    /*
-     * Phase 1: build the heap. We want the _largest_ element at
-     * the top.
-     */
-    n = 0;
-    while (n < ne) {
-	n++;
-
-	/*
-	 * Swap element n with its parent repeatedly to preserve
-	 * the heap property.
-	 */
-	i = n-1;
-
-	while (i > 0) {
-	    int p = PARENT(i);
-
-	    if (LESS(backedges[p], backedges[i])) {
-		SWAP(backedges[p], backedges[i]);
-		i = p;
-	    } else
-		break;
-	}
-    }
-
-    /*
-     * Phase 2: repeatedly remove the largest element and stick it
-     * at the top of the array.
-     */
-    while (n > 0) {
-	/*
-	 * The largest element is at position 0. Put it at the top,
-	 * and swap the arbitrary element from that position into
-	 * position 0.
-	 */
-	n--;
-	SWAP(backedges[0], backedges[n]);
-
-	/*
-	 * Now repeatedly move that arbitrary element down the heap
-	 * by swapping it with the more suitable of its children.
-	 */
-	i = 0;
-	while (1) {
-	    int lc, rc;
-
-	    lc = LCHILD(i);
-	    rc = RCHILD(i);
-
-	    if (lc >= n)
-		break;		       /* we've hit bottom */
-
-	    if (rc >= n) {
-		/*
-		 * Special case: there is only one child to check.
-		 */
-		if (LESS(backedges[i], backedges[lc]))
-		    SWAP(backedges[i], backedges[lc]);
-
-		/* _Now_ we've hit bottom. */
-		break;
-	    } else {
-		/*
-		 * The common case: there are two children and we
-		 * must check them both.
-		 */
-		if (LESS(backedges[i], backedges[lc]) ||
-		    LESS(backedges[i], backedges[rc])) {
-		    /*
-		     * Pick the more appropriate child to swap with
-		     * (i.e. the one which would want to be the
-		     * parent if one were above the other - as one
-		     * is about to be).
-		     */
-		    if (LESS(backedges[lc], backedges[rc])) {
-			SWAP(backedges[i], backedges[rc]);
-			i = rc;
-		    } else {
-			SWAP(backedges[i], backedges[lc]);
-			i = lc;
-		    }
-		} else {
-		    /* This element is in the right place; we're done. */
-		    break;
-		}
-	    }
-	}
-    }
-
-#undef LESS
-#undef PARENT
-#undef LCHILD
-#undef RCHILD
-#undef SWAP
-
-}
-
-int maxflow(int nv, int source, int sink,
-	    int ne, const int *edges, const int *capacity,
-	    int *flow, int *cut)
-{
-    void *scratch;
-    int *backedges;
-    int size;
-    int ret;
-
-    /*
-     * Allocate the space.
-     */
-    size = ne * sizeof(int) + maxflow_scratch_size(nv);
-    backedges = smalloc(size);
-    if (!backedges)
-	return -1;
-    scratch = backedges + ne;
-
-    /*
-     * Set up the backedges array.
-     */
-    maxflow_setup_backedges(ne, edges, backedges);
-
-    /*
-     * Call the main function.
-     */
-    ret = maxflow_with_scratch(scratch, nv, source, sink, ne, edges,
-			       backedges, capacity, flow, cut);
-
-    /*
-     * Free the scratch space.
-     */
-    sfree(backedges);
-
-    /*
-     * And we're done.
-     */
-    return ret;
-}
-
-#ifdef TESTMODE
-
-#define MAXEDGES 256
-#define MAXVERTICES 128
-#define ADDEDGE(i,j) do{edges[ne*2] = (i); edges[ne*2+1] = (j); ne++;}while(0)
-
-int compare_edge(const void *av, const void *bv)
-{
-    const int *a = (const int *)av;
-    const int *b = (const int *)bv;
-
-    if (a[0] < b[0])
-	return -1;
-    else if (a[0] > b[0])
-	return +1;
-    else if (a[1] < b[1])
-	return -1;
-    else if (a[1] > b[1])
-	return +1;
-    else
-	return 0;
-}
-
-int main(void)
-{
-    int edges[MAXEDGES*2], ne, nv;
-    int capacity[MAXEDGES], flow[MAXEDGES], cut[MAXVERTICES];
-    int source, sink, p, q, i, j, ret;
-
-    /*
-     * Use this algorithm to find a maximal complete matching in a
-     * bipartite graph.
-     */
-    ne = 0;
-    nv = 0;
-    source = nv++;
-    p = nv;
-    nv += 5;
-    q = nv;
-    nv += 5;
-    sink = nv++;
-    for (i = 0; i < 5; i++) {
-	capacity[ne] = 1;
-	ADDEDGE(source, p+i);
-    }
-    for (i = 0; i < 5; i++) {
-	capacity[ne] = 1;
-	ADDEDGE(q+i, sink);
-    }
-    j = ne;
-    capacity[ne] = 1; ADDEDGE(p+0,q+0);
-    capacity[ne] = 1; ADDEDGE(p+1,q+0);
-    capacity[ne] = 1; ADDEDGE(p+1,q+1);
-    capacity[ne] = 1; ADDEDGE(p+2,q+1);
-    capacity[ne] = 1; ADDEDGE(p+2,q+2);
-    capacity[ne] = 1; ADDEDGE(p+3,q+2);
-    capacity[ne] = 1; ADDEDGE(p+3,q+3);
-    capacity[ne] = 1; ADDEDGE(p+4,q+3);
-    /* capacity[ne] = 1; ADDEDGE(p+2,q+4); */
-    qsort(edges, ne, 2*sizeof(int), compare_edge);
-
-    ret = maxflow(nv, source, sink, ne, edges, capacity, flow, cut);
-
-    printf("ret = %d\n", ret);
-
-    for (i = 0; i < ne; i++)
-	printf("flow %d: %d -> %d\n", flow[i], edges[2*i], edges[2*i+1]);
-
-    for (i = 0; i < nv; i++)
-	if (cut[i] == 0)
-	    printf("difficult set includes %d\n", i);
-
-    return 0;
-}
-
-#endif
--- a/maxflow.h
+++ /dev/null
@@ -1,95 +1,0 @@
-/*
- * Edmonds-Karp algorithm for finding a maximum flow and minimum
- * cut in a network. Almost identical to the Ford-Fulkerson
- * algorithm, but apparently using breadth-first search to find the
- * _shortest_ augmenting path is a good way to guarantee
- * termination and ensure the time complexity is not dependent on
- * the actual value of the maximum flow. I don't understand why
- * that should be, but it's claimed on the Internet that it's been
- * proved, and that's good enough for me. I prefer BFS to DFS
- * anyway :-)
- */
-
-#ifndef MAXFLOW_MAXFLOW_H
-#define MAXFLOW_MAXFLOW_H
-
-/*
- * The actual algorithm.
- * 
- * Inputs:
- * 
- *  - `scratch' is previously allocated scratch space of a size
- *    previously determined by calling `maxflow_scratch_size'.
- * 
- *  - `nv' is the number of vertices. Vertices are assumed to be
- *    numbered from 0 to nv-1.
- * 
- *  - `source' and `sink' are the distinguished source and sink
- *    vertices.
- * 
- *  - `ne' is the number of edges in the graph.
- * 
- *  - `edges' is an array of 2*ne integers, giving a (source, dest)
- *    pair for each network edge. Edge pairs are expected to be
- *    sorted in lexicographic order.
- * 
- *  - `backedges' is an array of `ne' integers, each a distinct
- *    index into `edges'. The edges in `edges', if permuted as
- *    specified by this array, should end up sorted in the _other_
- *    lexicographic order, i.e. dest taking priority over source.
- * 
- *  - `capacity' is an array of `ne' integers, giving a maximum
- *    flow capacity for each edge. A negative value is taken to
- *    indicate unlimited capacity on that edge, but note that there
- *    may not be any unlimited-capacity _path_ from source to sink
- *    or an assertion will be failed.
- * 
- * Output:
- * 
- *  - `flow' must be non-NULL. It is an array of `ne' integers,
- *    each giving the final flow along each edge.
- * 
- *  - `cut' may be NULL. If non-NULL, it is an array of `nv'
- *    integers, which will be set to zero or one on output, in such
- *    a way that:
- *     + the set of zero vertices includes the source
- *     + the set of one vertices includes the sink
- *     + the maximum flow capacity between the zero and one vertex
- * 	 sets is achieved (i.e. all edges from a zero vertex to a
- * 	 one vertex are at full capacity, while all edges from a
- * 	 one vertex to a zero vertex have no flow at all).
- * 
- *  - the returned value from the function is the total flow
- *    achieved.
- */
-int maxflow_with_scratch(void *scratch, int nv, int source, int sink,
-			 int ne, const int *edges, const int *backedges,
-			 const int *capacity, int *flow, int *cut);
-
-/*
- * The above function expects its `scratch' and `backedges'
- * parameters to have already been set up. This allows you to set
- * them up once and use them in multiple invocates of the
- * algorithm. Now I provide functions to actually do the setting
- * up.
- */
-int maxflow_scratch_size(int nv);
-void maxflow_setup_backedges(int ne, const int *edges, int *backedges);
-
-/*
- * Simplified version of the above function. All parameters are the
- * same, except that `scratch' and `backedges' are constructed
- * internally. This is the simplest way to call the algorithm as a
- * one-off; however, if you need to call it multiple times on the
- * same network, it is probably better to call the above version
- * directly so that you only construct `scratch' and `backedges'
- * once.
- * 
- * Additional return value is now -1, meaning that scratch space
- * could not be allocated.
- */
-int maxflow(int nv, int source, int sink,
-	    int ne, const int *edges, const int *capacity,
-	    int *flow, int *cut);
-
-#endif /* MAXFLOW_MAXFLOW_H */