mirror of
git://git.tartarus.org/simon/puzzles.git
synced 2025-04-21 08:01:30 -07:00
Files

This is a dedicated algorithm for finding a maximal matching in a bipartite graph. Previously I've been solving that problem in this code base using maxflow.c, modelling the graph matching problem as a restricted case of the optimal network flow problem and then using a full-strength algorithm for the latter. That's overkill, and also algorithmically wasteful - the H-K algorithm is asymptotically better, because it can find multiple augmenting paths in each pass (hence getting the maximum benefit out of each expensive breadth-first search), as a consequence of not having to worry about lower- or higher-value augmenting paths in this restricted problem. So I expect this algorithm to be faster, at least in principle or in large cases, when it takes over the jobs currently being done by maxflow. But that's not the only benefit. Another is that the data representation is better suited to the problems actually being solved, which should simplify all the call sites; a third is that I've incorporated randomisation of the generated matching into the H-K module itself, which will further save effort at each call site because they will no longer have to worry about permuting the algorithm's input - they just have to pass it a random_state and it will take care of that internally. This commit only introduces the new matching.c and builds a test utility for it. I haven't yet migrated any clients of maxflow.
81 lines
3.0 KiB
C
81 lines
3.0 KiB
C
/*
|
|
* Hopcroft-Karp algorithm for finding a maximal matching in a
|
|
* bipartite graph.
|
|
*/
|
|
|
|
#ifndef MATCHING_MATCHING_H
|
|
#define MATCHING_MATCHING_H
|
|
|
|
/*
|
|
* The actual algorithm.
|
|
*
|
|
* Inputs:
|
|
*
|
|
* - 'scratch' is previously allocated scratch space of a size
|
|
* previously determined by calling 'matching_scratch_size'.
|
|
*
|
|
* - 'nl' is the number of vertices on the left side of the graph.
|
|
* Left vertices are numbered from 0 to nl-1.
|
|
*
|
|
* - 'nr' is the number of vertices on the left side of the graph.
|
|
* Right vertices are numbered from 0 to nr-1.
|
|
*
|
|
* - 'adjlists' and 'adjsizes' represents the graph in adjacency-list
|
|
* form. For each left vertex L, adjlists[L] points to an array of
|
|
* adjsizes[L] integers giving the list of right vertices adjacent
|
|
* to L.
|
|
*
|
|
* - 'rs', if not NULL, is a random_state used to perturb the
|
|
* progress of the algorithm so as to choose randomly from the
|
|
* possible matchings if there's more than one. (The exact
|
|
* probability distribution can't be guaranteed, but at the very
|
|
* least, any matching that exists should be a _possible_ output.)
|
|
*
|
|
* If 'rs' is not NULL, then each list in adjlists[] will be permuted
|
|
* during the course of the algorithm as a side effect. (That's why
|
|
* it's not an array of _const_ int pointers.)
|
|
*
|
|
* Output:
|
|
*
|
|
* - 'outl' may be NULL. If non-NULL, it is an array of 'nl'
|
|
* integers, and outl[L] will be assigned the index of the right
|
|
* vertex that the output matching paired with the left vertex L,
|
|
* or -1 if L is unpaired.
|
|
*
|
|
* - 'outr' may be NULL. If non-NULL, it is an array of 'nr'
|
|
* integers, and outr[R] will be assigned the index of the left
|
|
* vertex that the output matching paired with the right vertex R,
|
|
* or -1 if R is unpaired.
|
|
*
|
|
* - the returned value from the function is the total number of
|
|
* edges in the matching.
|
|
*/
|
|
int matching_with_scratch(void *scratch,
|
|
int nl, int nr, int **adjlists, int *adjsizes,
|
|
random_state *rs, int *outl, int *outr);
|
|
|
|
/*
|
|
* The above function expects its 'scratch' parameter to have already
|
|
* been set up. This function tells you how much space is needed for a
|
|
* given size of graph, so that you can allocate a single instance of
|
|
* scratch space and run the algorithm multiple times without the
|
|
* overhead of an alloc and free every time.
|
|
*/
|
|
size_t matching_scratch_size(int nl, int nr);
|
|
|
|
/*
|
|
* Simplified version of the above function. All parameters are the
|
|
* same, except that 'scratch' is constructed internally and freed on
|
|
* exit. 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 size of
|
|
* graph, it is probably better to call the above version directly so
|
|
* that you only construct 'scratch' once.
|
|
*
|
|
* Additional return value is now -1, meaning that scratch space
|
|
* could not be allocated.
|
|
*/
|
|
int matching(int nl, int nr, int **adjlists, int *adjsizes,
|
|
random_state *rs, int *outl, int *outr);
|
|
|
|
#endif /* MATCHING_MATCHING_H */
|