Skip to content

Instantly share code, notes, and snippets.

@psyq321
Created June 17, 2023 09:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save psyq321/5a3b123002e5326ce31e73c4a58bb9ca to your computer and use it in GitHub Desktop.
Save psyq321/5a3b123002e5326ce31e73c4a58bb9ca to your computer and use it in GitHub Desktop.

This problem could be modelled as a variant of the Maximal Coverage (MC) model with a distance metric added into it. However, practical solution for the problem would require sophisticated approaches, such as spatial analytics, clustering, and graph theory algorithms. Here I will give a high-level idea how this can be implemented:

We may start with a brute force approach, calculate triangle coverage and dispersion for each possible subset of tracts and finally select the one maximizing coverage and dispersion. However, this approach is computationally expensive.

Instead, I suggest the following approach that uses k-means clustering algorithm for computation efficiency:

  1. Compute the centroid for each tract by averaging its start and end points.
  2. Use k-means clustering algorithm over the tract centroids where k is the number of requested tracts. This will give us k clusters of tracts.
  3. For each cluster, select the tract that is closest to the cluster centroid, this tract will maximize the dispersion.
  4. Among the subset of tracts selected in step 3, select the tract that covers the maximum number of triangles, this will satisfy CRITERIUM-A.

The following is a high level C++ code snippen that assume the existence of a k-means function, a function getTractCentroid to calculate the centroid of a tract and a function getTriangleCoverage to compute the number of covered triangles.

Note that this code does not take ties into account, if two tracts cover the same number of maximum triangles, only one will be selected.

#include <glm/glm.hpp>
#include <vector>
#include <algorithm>

struct Tract {
    glm::vec3 startPoint;
    glm::vec3 endPoint;
};

std::vector<Tract> selectTracts(const std::vector<Tract>& tracts, unsigned int numReqTracts) {
    std::vector<glm::vec3> tractCentroids;
    for (const Tract& tract : tracts){
        tractCentroids.push_back(getTractCentroid(tract));
    }
   
    std::vector<glm::vec3> clusterCentroids = kMeans(tractCentroids, numReqTracts);
   
    std::vector<Tract> selectedTracts;
    for (const glm::vec3& clusterCentroid : clusterCentroids) {
        Tract* closestTract = nullptr;
        float minDist = FLT_MAX;
        for (const Tract& tract : tracts) {
            float dist = glm::distance(clusterCentroid, getTractCentroid(tract));
            if (dist < minDist) {
                closestTract = &tract;
                minDist = dist;
            }
        }
        selectedTracts.push_back(*closestTract);
    }

    auto cmp = [](const Tract& tract1, const Tract& tract2) {
        return getTriangleCoverage(tract1) < getTriangleCoverage(tract2);
    };
   
    std::sort(selectedTracts.begin(), selectedTracts.end(), cmp);
    selectedTracts.resize(numReqTracts);
   
    return selectedTracts;
}

Implementing the kMeans function and calculating the number of covered triangles getTriangleCoverage are likely non-trivial problems requiring further specification. They are beyond the scope of the current response.

In this algorithm, I assumed that dispersion is achieved by getting the tract that is closest to the cluster center, but in a stricter sense dispersion could be calculated differently. Also, to achieve a fair balance between coverage and dispersion, we may need to use a weighted score rather than maximizing coverage after selecting for dispersion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment