Machine Coding Problem

Surge Pricing Engine

macoAllcommercedynamic-multiplierzone-based
Commonly Asked By:UberLyftBolt

Requirements & Scope

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.

Architecture & System Entities

System components map for dynamic calculations, updates, and spatial routing:

Loading...
  • SurgeCalculator: Resolves demand/supply ratios into multipliers using an exponential or step-coeff curve.
  • SurgeCache: Multi-region high-read cache offering microsecond lookup bounds during bookings.

Design Patterns

  • Strategy Pattern: Allows the surge pricing curve calculator to choose dynamic curves (e.g. logarithmic, exponential, step curves) at runtime.
  • Observer Pattern: Dispatches zone surge change events to active drivers, inviting dynamic spatial relocations.

Calculation Flow

Surge Pricing Workflow

  1. Read Supply & Demand: Count free driver beacons and booking attempts in matching zone within past window.
  2. Calculate Ratio: Ratio = Demand / Supply.
  3. Evaluate Surge: If Ratio > Threshold, compute surge.
  4. Apply Gradual Smoothness: Clamp the output to prevent drops exceeding maxRampDownStep per interval.
  5. Flush Cache: Broadcast update to ride matchers.

Clean Code Blueprint

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 ===");
    }
}

Interactive Simulator

โšก Dynamic Surge Pricing Visualizer

Adjust drivers, bookings, thresholds, coefficients and monitor real-time spatial multipliers.

0.5x1.2x ratio2.5x
0.1 (low)0.5 step1.5 (steep)
0.05 (gradual)0.2 per tick0.5 (fast)

Downtown Core

h3: 8326a1fffffffff
2.50x
Demand (Requests/s):25
Supply (Active Drivers):5
Ratio (D/S):5.00

International Airport

h3: 8326a5fffffffff
3.20x
Demand (Requests/s):18
Supply (Active Drivers):2
Ratio (D/S):9.00

Residential Suburbs

h3: 8326a7fffffffff
1.00x
Demand (Requests/s):4
Supply (Active Drivers):15
Ratio (D/S):0.27

Tech Park / Hub

h3: 8326a9fffffffff
1.25x
Demand (Requests/s):12
Supply (Active Drivers):8
Ratio (D/S):1.50

๐Ÿš• Live Fare Estimation Tool

Base Booking Fare:$15.00
Surge Multiplier Applied:2.50x
Final Estimated Fare:$37.50
๐Ÿ–ณ Surge Calculator Engine Logs
[13:50:00] Surge pricing daemon active. Sampling spatial coordinates...

๐Ÿ’ฌReview

Help Us Improve

How helpful was this walkthrough?

Click a star to rate. We actively use this feedback to refine and update our system design content.

Placeholder
Optional but highly appreciated!

Discussion

Share your thoughts, ask questions, or help others.

Loading comments...