Machine Coding Problem

Ride Pooling Logic

maco60macoAllgeoroute-overlap-algorithms
Commonly Asked By:UberLyftGrabOla

Functional Scope (In-Scope)

  • Detour-Aware Path Matching: Find compatible rides for new requests by evaluating detour impacts on existing riders.
  • Pickup-Before-Dropoff Constraints: Enforce logical ordering, ensuring a rider's pickup always happens before their dropoff.
  • Optimal Stop Insertion: Enumerate stop list insertion combinations to select paths that minimize total detour distance.
  • Segment-Based Fare Splitting: Calculate individual fare cuts by prorating distance segments across co-riders using the Strategy Pattern.

Explicit Boundaries (Out-of-Scope)

  • No Real-World Map Engines (OSM/Google Maps): Excludes live traffic updates, turn-by-turn navigation data, or OpenStreetMap integrations; uses simplified 2D Cartesian spatial coordinates.
  • No Passenger Driver Churn Predictors: Does not predict driver cancellations or rider churn patterns.

Clean reference designs demonstrating optimal detour stop insertions in Java and Python:

// ─── JAVA BLUEPRINT ──────────────────────────────────────────────────────────
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.*;

enum StopType { PICKUP, DROPOFF }

class Location {
    final double x;
    final double y;

    public Location(double x, double y) {
        this.x = x;
        this.y = y;
    }

    public double distanceTo(Location other) {
        return Math.sqrt(Math.pow(this.x - other.x, 2) + Math.pow(this.y - other.y, 2));
    }
}

class Stop {
    private final StopType type;
    private final String riderId;
    private final Location location;

    public Stop(StopType type, String riderId, Location location) {
        this.type = type;
        this.riderId = riderId;
        this.location = location;
    }

    public StopType getType() { return type; }
    public String getRiderId() { return riderId; }
    public Location getLocation() { return location; }
}

class RideRequest {
    private final String riderId;
    private final Location pickup;
    private final Location dropoff;
    private final double maxDetourFactor; // e.g., 1.5 means max distance is 1.5 * direct distance

    public RideRequest(String riderId, Location pickup, Location dropoff, double maxDetourFactor) {
        this.riderId = riderId;
        this.pickup = pickup;
        this.dropoff = dropoff;
        this.maxDetourFactor = maxDetourFactor;
    }

    public String getRiderId() { return riderId; }
    public Location getPickup() { return pickup; }
    public Location getDropoff() { return dropoff; }
    public double getMaxDetourFactor() { return maxDetourFactor; }

    public double getDirectDistance() {
        return pickup.distanceTo(dropoff);
    }
}

// Strategy Pattern for Fare Splitting
interface FareSplittingStrategy {
    Map<String, Double> calculateFareSplits(PooledRoute route, double baseRatePerKm);
}

class SegmentBasedFareSplitStrategy implements FareSplittingStrategy {
    @Override
    public Map<String, Double> calculateFareSplits(PooledRoute route, double baseRatePerKm) {
        List<Stop> stops = route.getStops();
        Map<String, Double> finalFares = new HashMap<>();
        if (stops.size() < 2) return finalFares;

        Set<String> activeRiders = new HashSet<>();
        Location currentPos = stops.get(0).getLocation();

        for (int i = 0; i < stops.size(); i++) {
            Stop stop = stops.get(i);
            
            if (i > 0) {
                Location nextPos = stop.getLocation();
                double segmentDist = currentPos.distanceTo(nextPos);
                double segmentCost = segmentDist * baseRatePerKm;

                if (!activeRiders.isEmpty()) {
                    double splitCost = segmentCost / activeRiders.size();
                    for (String rider : activeRiders) {
                        finalFares.put(rider, finalFares.getOrDefault(rider, 0.0) + splitCost);
                    }
                }
                currentPos = nextPos;
            }

            if (stop.getType() == StopType.PICKUP) {
                activeRiders.add(stop.getRiderId());
            } else {
                activeRiders.remove(stop.getRiderId());
            }
        }
        return finalFares;
    }
}

class PooledRoute {
    private final String driverId;
    private final int capacity;
    private final List<Stop> stops = new ArrayList<>();
    private final Map<String, RideRequest> riderRequests = new HashMap<>();

    public PooledRoute(String driverId, int capacity, Location startLocation) {
        this.driverId = driverId;
        this.capacity = capacity;
        // Seed driver start location stop
        this.stops.add(new Stop(StopType.PICKUP, "DRIVER", startLocation));
    }

    public String getDriverId() { return driverId; }
    public int getCapacity() { return capacity; }
    public List<Stop> getStops() { return stops; }

    public double calculateTotalDistance() {
        return getRouteDistance(stops);
    }

    public boolean tryInsertRider(RideRequest req) {
        int bestPickupIdx = -1;
        int bestDropoffIdx = -1;
        double minDistance = Double.MAX_VALUE;

        // Iterate over all insertion points
        for (int i = 1; i <= stops.size(); i++) {
            for (int j = i; j <= stops.size(); j++) {
                List<Stop> testStops = new ArrayList<>(stops);
                testStops.add(i, new Stop(StopType.PICKUP, req.getRiderId(), req.getPickup()));
                testStops.add(j + 1, new Stop(StopType.DROPOFF, req.getRiderId(), req.getDropoff()));

                if (isValidRoute(testStops, req)) {
                    double dist = getRouteDistance(testStops);
                    if (dist < minDistance) {
                        minDistance = dist;
                        bestPickupIdx = i;
                        bestDropoffIdx = j;
                    }
                }
            }
        }

        if (bestPickupIdx != -1) {
            stops.add(bestPickupIdx, new Stop(StopType.PICKUP, req.getRiderId(), req.getPickup()));
            stops.add(bestDropoffIdx + 1, new Stop(StopType.DROPOFF, req.getRiderId(), req.getDropoff()));
            riderRequests.put(req.getRiderId(), req);
            return true;
        }
        return false;
    }

    private boolean isValidRoute(List<Stop> routeStops, RideRequest newReq) {
        int currentRiders = 0;
        // Verify capacity is never violated
        for (Stop stop : routeStops) {
            if (stop.getType() == StopType.PICKUP && !stop.getRiderId().equals("DRIVER")) {
                currentRiders++;
            } else if (stop.getType() == StopType.DROPOFF) {
                currentRiders--;
            }
            if (currentRiders > capacity) return false;
        }

        // Verify detour constraint for all riders including the new one
        Map<String, Double> actualDistances = calculateTravelDistances(routeStops);
        
        // 1. Check existing riders
        for (Map.Entry<String, RideRequest> entry : riderRequests.entrySet()) {
            double actual = actualDistances.getOrDefault(entry.getKey(), 0.0);
            double limit = entry.getValue().getDirectDistance() * entry.getValue().getMaxDetourFactor();
            if (actual > limit) return false;
        }

        // 2. Check the candidate rider
        double candidateActual = actualDistances.getOrDefault(newReq.getRiderId(), 0.0);
        double candidateLimit = newReq.getDirectDistance() * newReq.getMaxDetourFactor();
        return candidateActual <= candidateLimit;
    }

    private Map<String, Double> calculateTravelDistances(List<Stop> routeStops) {
        Map<String, Double> distanceMap = new HashMap<>();
        Map<String, Double> startAccumulated = new HashMap<>();
        
        double currentTotal = 0.0;
        Location prev = routeStops.get(0).getLocation();

        for (Stop stop : routeStops) {
            currentTotal += prev.distanceTo(stop.getLocation());
            prev = stop.getLocation();

            if (stop.getType() == StopType.PICKUP && !stop.getRiderId().equals("DRIVER")) {
                startAccumulated.put(stop.getRiderId(), currentTotal);
            } else if (stop.getType() == StopType.DROPOFF) {
                double pickupTotal = startAccumulated.getOrDefault(stop.getRiderId(), 0.0);
                distanceMap.put(stop.getRiderId(), currentTotal - pickupTotal);
            }
        }
        return distanceMap;
    }

    private double getRouteDistance(List<Stop> routeStops) {
        if (routeStops.size() < 2) return 0.0;
        double distance = 0.0;
        for (int i = 0; i < routeStops.size() - 1; i++) {
            distance += routeStops.get(i).getLocation().distanceTo(routeStops.get(i + 1).getLocation());
        }
        return distance;
    }
}

class RideMatchingManager {
    private final List<PooledRoute> activeRoutes = new CopyOnWriteArrayList<>();
    private final ReentrantLock lock = new ReentrantLock();

    public void addRoute(PooledRoute route) {
        activeRoutes.add(route);
    }

    public boolean matchRider(RideRequest request) {
        lock.lock();
        try {
            for (PooledRoute route : activeRoutes) {
                if (route.tryInsertRider(request)) {
                    System.out.println("Matched Rider " + request.getRiderId() + " to Driver " + route.getDriverId());
                    return true;
                }
            }
            return false;
        } finally {
            lock.unlock();
        }
    }

    public List<PooledRoute> getActiveRoutes() {
        return activeRoutes;
    }
}

// Complete Simulation
public class Main {
    public static void main(String[] args) {
        RideMatchingManager manager = new RideMatchingManager();

        // 1. Create a route representing a driver from origin (0, 0)
        PooledRoute route = new PooledRoute("D-101", 3, new Location(0.0, 0.0));
        manager.addRoute(route);

        // 2. Submit concurrent or sequential ride requests
        System.out.println("--- Submitting ride request 1 (R-1) ---");
        RideRequest req1 = new RideRequest("R-1", new Location(1.0, 1.0), new Location(5.0, 5.0), 1.5);
        boolean success1 = manager.matchRider(req1);
        System.out.println("Match success: " + success1);

        System.out.println("\n--- Submitting ride request 2 (R-2) with close overlap ---");
        RideRequest req2 = new RideRequest("R-2", new Location(2.0, 1.5), new Location(4.5, 4.0), 1.4);
        boolean success2 = manager.matchRider(req2);
        System.out.println("Match success: " + success2);

        // 3. Compute final segment splits
        System.out.println("\n--- Calculating Fair splits for overlapping segments ---");
        FareSplittingStrategy splitStrategy = new SegmentBasedFareSplitStrategy();
        Map<String, Double> splits = splitStrategy.calculateFareSplits(route, 2.5); // $2.5 per unit distance
        for (Map.Entry<String, Double> entry : splits.entrySet()) {
            System.out.println("Rider " + entry.getKey() + " Fare: $" + String.format("%.2f", entry.getValue()));
        }
    }
}