Machine Coding Problem

Surge Pricing Engine

macoAllcommercedynamic-multiplierzone-based
Commonly Asked By:UberLyftBolt

Functional Specifications

  • Dynamic Geofenced Pricing: Adjusts fares dynamically per coordinate zone depending on realtime supply/demand dynamics.
  • Gradual Price Smoothness (Ramp-Down): Multipliers decrement smoothly over multiple update cycles rather than sharp sudden drops.
  • Driver Attraction Feedbacks: Higher multiplier incentives trigger system events that relocate nearby supply into high-value cells.

Out of Scope

  • Complex Route Math: Complex traffic routing is delegated to external maps microservices.
  • Customer Credit Check: Driver payout or card pre-auth states are decoupled.

Production reference implementations demonstrating ratio curves, cached indexes, and ramp down smoothing checks:

// ─── JAVA BLUEPRINT ──────────────────────────────────────────────────────────
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;

class GeoZone {
    private final String zoneId;
    private final String zoneName;
    private final String h3Index;

    public GeoZone(String zoneId, String zoneName, String h3Index) {
        this.zoneId = zoneId;
        this.zoneName = zoneName;
        this.h3Index = h3Index;
    }

    public String getZoneId() { return zoneId; }
    public String getZoneName() { return zoneName; }
    public String getH3Index() { return h3Index; }
}

class SupplyDemandSnapshot {
    private final int activeDrivers;
    private final int pendingRequests;
    private final long timestamp;

    public SupplyDemandSnapshot(int activeDrivers, int pendingRequests) {
        this.activeDrivers = activeDrivers;
        this.pendingRequests = pendingRequests;
        this.timestamp = System.currentTimeMillis();
    }

    public int getActiveDrivers() { return activeDrivers; }
    public int getPendingRequests() { return pendingRequests; }
    public long getTimestamp() { return timestamp; }
    
    public double getRatio() {
        if (activeDrivers == 0) {
            return pendingRequests > 0 ? 5.0 : 1.0; // Avoid division by zero
        }
        return (double) pendingRequests / activeDrivers;
    }
}

class SurgeRuleConfig {
    private final double baseMultiplier = 1.0;
    private final double maxMultiplier = 4.5;
    private final double surgeThreshold = 1.2; // Ratio at which surge starts
    private final double multiplierCoeff = 0.5; // Multiplier step size
    private final double maxRampDownStep = 0.2; // Max decrease in multiplier per interval

    public double getBaseMultiplier() { return baseMultiplier; }
    public double getMaxMultiplier() { return maxMultiplier; }
    public double getSurgeThreshold() { return surgeThreshold; }
    public double getMultiplierCoeff() { return multiplierCoeff; }
    public double getMaxRampDownStep() { return maxRampDownStep; }
}

class SurgeCalculator {
    private final SurgeRuleConfig config;

    public SurgeCalculator(SurgeRuleConfig config) {
        this.config = config;
    }

    public double calculateMultiplier(SupplyDemandSnapshot snapshot, double previousMultiplier) {
        double ratio = snapshot.getRatio();
        double targetMultiplier = config.getBaseMultiplier();

        if (ratio > config.getSurgeThreshold()) {
            double excess = ratio - config.getSurgeThreshold();
            targetMultiplier = config.getBaseMultiplier() + (excess * config.getMultiplierCoeff());
            targetMultiplier = Math.min(targetMultiplier, config.getMaxMultiplier());
        }

        // Apply gradual ramp-down policy to avoid sharp price drops
        if (targetMultiplier < previousMultiplier) {
            double maxDrop = config.getMaxRampDownStep();
            if (previousMultiplier - targetMultiplier > maxDrop) {
                targetMultiplier = previousMultiplier - maxDrop;
            }
        }

        return Math.max(1.0, targetMultiplier);
    }
}

class SurgeCache {
    private final Map<String, Double> zoneMultipliers = new ConcurrentHashMap<>();
    private final Map<String, SupplyDemandSnapshot> snapshots = new ConcurrentHashMap<>();
    private final SurgeCalculator calculator;

    public SurgeCache(SurgeCalculator calculator) {
        this.calculator = calculator;
    }

    public void updateSnapshot(String zoneId, int activeDrivers, int pendingRequests) {
        SupplyDemandSnapshot newSnapshot = new SupplyDemandSnapshot(activeDrivers, pendingRequests);
        snapshots.put(zoneId, newSnapshot);
        
        // Dynamic re-calculation
        double prev = zoneMultipliers.getOrDefault(zoneId, 1.0);
        double next = calculator.calculateMultiplier(newSnapshot, prev);
        zoneMultipliers.put(zoneId, next);
    }

    public double getMultiplier(String zoneId) {
        return zoneMultipliers.getOrDefault(zoneId, 1.0);
    }

    public SupplyDemandSnapshot getSnapshot(String zoneId) {
        return snapshots.get(zoneId);
    }
}

class PriceEstimator {
    private final SurgeCache surgeCache;

    public PriceEstimator(SurgeCache surgeCache) {
        this.surgeCache = surgeCache;
    }

    public double estimateFare(String zoneId, double baseFare) {
        double multiplier = surgeCache.getMultiplier(zoneId);
        return baseFare * multiplier;
    }
}

public class Main {
    public static void main(String[] args) {
        System.out.println("=== JAVA SURGE PRICING SIMULATION ===");
        SurgeRuleConfig config = new SurgeRuleConfig();
        SurgeCalculator calculator = new SurgeCalculator(config);
        SurgeCache cache = new SurgeCache(calculator);
        PriceEstimator estimator = new PriceEstimator(cache);

        String zoneId = "ZONE_1";
        
        System.out.println("Initial state: 10 drivers, 5 requests (low demand)...");
        cache.updateSnapshot(zoneId, 10, 5);
        System.out.println("Zone 1 Multiplier: " + cache.getMultiplier(zoneId) + "x");
        System.out.println("Fare for $15 base: $" + estimator.estimateFare(zoneId, 15.0));

        System.out.println("\nSpike demand: 2 drivers, 20 requests (severe mismatch)...");
        cache.updateSnapshot(zoneId, 2, 20);
        System.out.println("Zone 1 Multiplier: " + cache.getMultiplier(zoneId) + "x");
        System.out.println("Fare for $15 base: $" + estimator.estimateFare(zoneId, 15.0));

        System.out.println("\nDemand resolves instantly: 20 drivers, 0 requests...");
        cache.updateSnapshot(zoneId, 20, 0);
        System.out.println("Zone 1 Multiplier: " + cache.getMultiplier(zoneId) + "x (smooth gradual ramp-down)");
        System.out.println("Fare for $15 base: $" + estimator.estimateFare(zoneId, 15.0));

        System.out.println("=== END OF JAVA SIMULATION ===");
    }
}