//' C++ wrapper for Gale-Shapley Algorithm //' //' This function provides an R wrapper for the C++ backend. Users should not //' call this function directly and instead use //' \code{\link{galeShapley.marriageMarket}} or //' \code{\link{galeShapley.collegeAdmissions}}. //' //' @param proposerPref is a matrix with the preference order of the proposing //' side of the market. If there are \code{n} proposers and \code{m} reviewers //' in the market, then this matrix will be of dimension \code{m} by \code{n}. //' The \code{i,j}th element refers to \code{j}'s \code{i}th most favorite //' partner. Preference orders must be complete and specified using C++ //' indexing (starting at 0). //' @param reviewerUtils is a matrix with cardinal utilities of the courted side //' of the market. If there are \code{n} proposers and \code{m} reviewers, then //' this matrix will be of dimension \code{n} by \code{m}. The \code{i,j}th //' element refers to the payoff that individual \code{j} receives from being //' matched to individual \code{i}. //' @return A list with elements that specify who is matched to whom. Suppose //' there are \code{n} proposers and \code{m} reviewers. The list contains //' the following items: //' \itemize{ //' \item{\code{proposals} is a vector of length \code{n} whose \code{i}th //' element contains the number of the reviewer that proposer \code{i} is //' matched to using C++ indexing. Proposers that remain unmatched will be //' listed as being matched to \code{m}.} //' \item{\code{engagements} is a vector of length \code{m} whose \code{j}th //' element contains the number of the proposer that reviewer \code{j} is //' matched to using C++ indexing. Reviwers that remain unmatched will be //' listed as being matched to \code{n}.} //' } // [[Rcpp::export]] List cpp_wrapper_galeshapley(const umat& proposerPref, const mat& reviewerUtils) { // number of proposers (men) int M = proposerPref.n_cols; // number of reviewers (women) int N = proposerPref.n_rows; // initialize engagements, proposals vec engagements(N), proposals(M); // create an integer queue of bachelors // the idea of using queues for this problem is borrowed from // http://rosettacode.org/wiki/Stable_marriage_problem#C.2B.2B queue<int> bachelors; // set all proposals to N (aka no proposals) proposals.fill(N); // set all engagements to M (aka no engagements) engagements.fill(M); // every proposer starts out as a bachelor for(int iX=M-1; iX >= 0; iX--) { bachelors.push(iX); } // loop until there are no more proposals to be made while (!bachelors.empty()) { // get the index of the proposer int proposer = bachelors.front(); // get the proposer's preferences: we use a raw pointer to the memory // used by the column `proposer` for performance reasons (this is to avoid // making a copy of the proposers vector of preferences) const uword * proposerPrefcol = proposerPref.colptr(proposer); // find the best available match for proposer for(int jX=0; jX<N; jX++) { // get the index of the reviewer that the proposer is interested in // by dereferencing the pointer; increment the pointer after use (not its value) const uword wX = *proposerPrefcol++; // check if wX is available (`M` means unmatched) if(engagements(wX)==M) { // if available, then form a match engagements(wX) = proposer; proposals(proposer) = wX; // go to the next proposer break; } // wX is already matched, let's see if wX can be poached if(reviewerUtils(proposer, wX) > reviewerUtils(engagements(wX), wX)) { // wX's previous partner becomes unmatched (`N` means unmatched) proposals(engagements(wX)) = N; bachelors.push(engagements(wX)); // proposer and wX form a match engagements(wX) = proposer; proposals(proposer) = wX; // go to the next proposer break; } } // remove proposer from bachelor queue: proposer will remain unmatched bachelors.pop(); } return List::create( _["proposals"] = proposals, _["engagements"] = engagements); }
//------------------------------------------------------------------------------ void NiceGraph::makeStrangersBanquetGraph(int numVertices, int groups, float density, float mu) { makeEmptyGraph(numVertices); // first initialize all the vertices makeUndirected(); // the SB is only undirected for now... if (groups > numVertices) // bounds checking on number of groups groups = numVertices; for (int c = 0; c < numVertices; c++) // now subdivide into groups { for (int g = 0; g < groups; g++) { if (c % groups == g) setVertexColor(c,g); } } // make declining edge scheme to speed things up int total_edges = numVertices * (numVertices - 1) / 2; vector<Edge> proposals (total_edges); int edgeIndex = 0; for (int froms = 0; froms < numVertices; froms++) { for (int tos = 0; tos < numVertices; tos++) { if ((froms != tos) && (froms < tos)) { proposals[edgeIndex].from = vertexList[froms]; proposals[edgeIndex].to = vertexList[tos]; edgeIndex++; } } } // propose connections and build graph int num_edges = (int) (density * (float) total_edges); int edge_counter = 0; while (edge_counter < num_edges) { // use declining proposal list to speed things up... int randIndex = rand() % proposals.size(); // choose two vertices to make the proposal int vertex1 = proposals[randIndex].from->vID; int vertex2 = proposals[randIndex].to->vID; if ((vertex1 != vertex2) && (!testConnected(vertex1,vertex2))) // if isn't itself, and aren't already connected { // now check to see if they are accepted if (vertexList[vertex1]->vColor == vertexList[vertex2]->vColor) { // if same color approve match addEdge(vertex1, vertex2); // add edge edge_counter++; if (!proposals.empty()) proposals.erase(proposals.begin()+randIndex); // remove from list } else // if different colors check against mu { float chance1 = rand() / (RAND_MAX+1.0); float chance2 = rand() / (RAND_MAX+1.0); if ((chance1 < mu) || (chance2 < mu)) continue; // if either one rejects the connection do nothing else { // approve the connection addEdge(vertex1, vertex2); // add edge edge_counter++; if (!proposals.empty()) proposals.erase(proposals.begin()+randIndex); // remove from list } } } } }