Parking Function Simulation with Dissatisfaction¶
This code simulates a parking function where drivers arrive in order and attempt to park in their preferred spot. If their preferred spot is taken, they try the next higher-numbered spot. If no spot is available, they receive a penalty.
We also calculate total dissatisfaction, defined as the absolute difference between the assigned spot and preferred spot. If a driver cannot park, a fixed penalty is added.
Step 1: Define the simulation function¶
Simulates parking where each driver tries their preferred spot or the next available higher spot.
Returns the final parking assignments and total dissatisfaction.
Arguments:
- preferences: list of preferred spots for each driver (1-based)
- penalty: dissatisfaction added if a driver cannot park
def parking_function_with_penalties(preferences, penalty=10):
n = len(preferences) # Number of cars/spots
spots = [False] * n # Creates list that tracks which spots are taken (False=open spot)
assignments = [-1] * n # Placeholder- no cars have parked at first
dissatisfaction = 0 # Start with dissatisfaction at 0
for i, pref in enumerate(preferences): # Loop over each car
parked = False
for j in range(pref - 1, n): # try preferred and then higher
if not spots[j]: # If spot is available
spots[j] = True # Mark it as taken
assignments[i] = j + 1 # Record the taken spot
dissatisfaction += abs((j + 1) - pref) # Add how far off from preference
parked = True # Car is now parked
break
if not parked:
dissatisfaction += penalty # Couldn't park add penalty
return assignments, dissatisfaction
Step 2: Define a validity checker for classical parking functions¶
This checks if all drivers would be able to park following the standard parking function rules.
def is_valid_parking_function(preferences):
n = len(preferences)
available = [False] * n
for pref in preferences:
found = False
for i in range(pref - 1, n): # starts checking from pref spot
if not available[i]:
available[i] = True
found = True
break
if not found:
return False # A driver couldn’t park
return True
Step 3: Run the simulation and show results¶
def run_and_report(preferences, penalty=10):
assignments, dissatisfaction = parking_function_with_penalties(preferences, penalty)
print("\nFinal Parking Assignments:")
for i, spot in enumerate(assignments): # Go through each driver and check where they ended up
if spot == -1: # Driver couldn't park
print(f"Driver {i+1} → Could not park (Preferred: {preferences[i]})")
else:
print(f"Driver {i+1} → Spot {spot} (Preferred: {preferences[i]})")
print(f"\nTotal Dissatisfaction: {dissatisfaction}")
# Check if preferences are a valid parking function
if is_valid_parking_function(preferences):
print("The input preferences form a valid parking function.")
else:
print("The input preferences do NOT form a valid parking function.")
# Check if all drivers actually parked
if -1 not in assignments:
print("All drivers successfully parked.")
else:
print("Some drivers could not park.")
# Get user input for preferences
try:
n = int(input("Enter the number of drivers (and parking spots): "))
if n <= 0:
raise ValueError("Number must be positive.")
preferences = []
print(f"\nEnter the preferred parking spot (1 to {n}) for each driver:")
for i in range(n):
while True:
try:
pref = int(input(f"Driver {i+1} preference: "))
if 1 <= pref <= n:
preferences.append(pref)
break
else:
print(f"Please enter a number between 1 and {n}.")
except ValueError:
print("Invalid input. Please enter an integer.")
print("\n Preferences recorded successfully!")
print("π (Preferences):", preferences)
except ValueError as e:
print(f"Error: {e}")
run_and_report(preferences)
Enter the number of drivers (and parking spots): 6 Enter the preferred parking spot (1 to 6) for each driver: Driver 1 preference: 3 Driver 2 preference: 4 Driver 3 preference: 6 Driver 4 preference: 3 Driver 5 preference: 2 Driver 6 preference: 1 Preferences recorded successfully! π (Preferences): [3, 4, 6, 3, 2, 1] Final Parking Assignments: Driver 1 → Spot 3 (Preferred: 3) Driver 2 → Spot 4 (Preferred: 4) Driver 3 → Spot 6 (Preferred: 6) Driver 4 → Spot 5 (Preferred: 3) Driver 5 → Spot 2 (Preferred: 2) Driver 6 → Spot 1 (Preferred: 1) Total Dissatisfaction: 2 The input preferences form a valid parking function. All drivers successfully parked.
Parking Function Simulation with VIP Drivers and Reserved Spots¶
This version allows user input for:
- Preferences of each driver
- Which drivers are VIPs
Rules:
- VIP drivers get priority.
- Non-VIP drivers cannot park in VIP-preferred spots.
- Each driver tries their preferred spot and moves forward if it's taken.
- If no valid spot is available, they do not park and receive a penalty.
def parking_with_vip_reservations(preferences, vip_flags, penalty=10):
n = len(preferences) # Number of drivers and spots
spots = [False] * n # All spots start off open
assignments = [-1] * n # All cars start off as unparked
total_dissatisfaction = 0
# Determine reserved spots: spots preferred by VIPs
reserved_spots = set()
for i, is_vip in enumerate(vip_flags): # Go through each driver to check if VIP
if is_vip:
reserved_spots.add(preferences[i]) # If VIP, reserve their preferred spot
for i, (pref, is_vip) in enumerate(zip(preferences, vip_flags)): # Loop through drivers and their VIP status
parked = False
for j in range(pref - 1, n): # Try from preferred spot
spot_number = j + 1
# If spot taken, move on
if spots[j]:
continue
# Non-VIP drivers cannot take VIP-reserved spots
if not is_vip and spot_number in reserved_spots:
continue
# If spot is available and allowed
spots[j] = True # Mark it as takken
assignments[i] = spot_number # Record the assignment
total_dissatisfaction += abs(spot_number - pref) # Calculate dissatisfaction
parked = True # Mark the car as parked
break
if not parked: # If driver can't park, add penalty
total_dissatisfaction += penalty # Could not park
return assignments, total_dissatisfaction
Validity Check (same as before)¶
def is_valid_parking_function(preferences):
n = len(preferences)
spots = [False] * n
for pref in preferences:
parked = False
for i in range(pref - 1, n):
if not spots[i]:
spots[i] = True
parked = True
break
if not parked:
return False
return True
Function to Get User Input¶
def get_input():
n = int(input("Enter the number of drivers (and spots): "))
preferences = []
print("\nEnter the preferred parking spot (1 to {}) for each driver:".format(n))
for i in range(n):
pref = int(input(f"Driver {i+1} preference: "))
preferences.append(pref)
print("\nEnter the driver numbers (1-based) of the VIPs, separated by commas (e.g., 1,3,5):")
vip_input = input("VIP driver numbers: ")
vip_indices = set(int(x.strip()) - 1 for x in vip_input.split(",") if x.strip().isdigit())
vip_flags = [(i in vip_indices) for i in range(n)]
return preferences, vip_flags
Final Report Function¶
def run_vip_parking_simulation():
preferences, vip_flags = get_input()
assignments, dissatisfaction = parking_with_vip_reservations(preferences, vip_flags)
print("\nFinal Parking Assignments:")
for i, spot in enumerate(assignments):
vip_label = " (VIP)" if vip_flags[i] else ""
if spot == -1:
print(f"Driver {i+1}{vip_label} → Could not park (Preferred: {preferences[i]})")
else:
print(f"Driver {i+1}{vip_label} → Spot {spot} (Preferred: {preferences[i]})")
print(f"\nTotal Dissatisfaction: {dissatisfaction}")
# Check if preferences are a valid parking function (ignores VIP restrictions)
if is_valid_parking_function(preferences):
print("The input preferences form a valid parking function (without VIP restrictions).")
else:
print("The input preferences do NOT form a valid parking function (even without VIP restrictions).")
Run the Simulation¶
run_vip_parking_simulation()
Enter the number of drivers (and spots): 7 Enter the preferred parking spot (1 to 7) for each driver: Driver 1 preference: 4 Driver 2 preference: 3 Driver 3 preference: 2 Driver 4 preference: 1 Driver 5 preference: 1 Driver 6 preference: 5 Driver 7 preference: 6 Enter the driver numbers (1-based) of the VIPs, separated by commas (e.g., 1,3,5): VIP driver numbers: 1,5,6 Final Parking Assignments: Driver 1 (VIP) → Spot 4 (Preferred: 4) Driver 2 → Spot 3 (Preferred: 3) Driver 3 → Spot 2 (Preferred: 2) Driver 4 → Spot 6 (Preferred: 1) Driver 5 (VIP) → Spot 1 (Preferred: 1) Driver 6 (VIP) → Spot 5 (Preferred: 5) Driver 7 → Spot 7 (Preferred: 6) Total Dissatisfaction: 6 The input preferences form a valid parking function (without VIP restrictions).
Random Simulation¶
This function runs the VIP Parking Model many times with randomized inputs to see what happens on average.¶
- n_drivers= 10 (10 drivers and spots)
- vip_ratio= 0.3 (30% of drivers randomly marked as VIP)
- penalty= 10 (dissatisfaction for not parking)
- simulations= 1000 (runs the simulation 1000 times)
import random
def simulate_multiple_runs(n_drivers=100, vip_ratio=0.3, penalty=10, simulations=1000):
total_dissatisfaction = 0
total_parked = 0
for _ in range(simulations): # Each driver randomly chooses a preferred spot between 1 and n_drivers
preferences = [random.randint(1, n_drivers) for _ in range(n_drivers)]
# Assign VIPs randomly
num_vips = int(n_drivers * vip_ratio) # Calculate number of VIPs
vip_indices = set(random.sample(range(n_drivers), num_vips)) # Randomly select that many driver indicies
vip_flags = [(i in vip_indices) for i in range(n_drivers)] # Create list of True/False flags indicating VIPs
# Simulate one parking run
assignments, dissatisfaction = parking_with_vip_reservations(preferences, vip_flags, penalty)
# Store results
total_dissatisfaction += dissatisfaction
total_parked += sum(1 for spot in assignments if spot != -1)
# Calculate Averages
avg_dissatisfaction = total_dissatisfaction / simulations
avg_parked = total_parked / simulations
print(f"\n--- Simulation Results ({simulations} runs) ---")
print(f"Average Total Dissatisfaction: {avg_dissatisfaction:.2f}")
print(f"Average Drivers Parked: {avg_parked:.2f} / {n_drivers}")
print(f"Average Parking Success Rate: {100 * avg_parked / n_drivers:.2f}%")
simulate_multiple_runs(n_drivers=100, vip_ratio=0.3, penalty=10, simulations=1000)
--- Simulation Results (1000 runs) --- Average Total Dissatisfaction: 362.36 Average Drivers Parked: 94.46 / 100 Average Parking Success Rate: 94.46%
Data collection for analysis and visualization¶
- How changing the VIP Ratio affects performance
- How changing the penalty value affects performance
For both we are tracking:
- Average total dissatisfaction
- Parking success rate (Percent of drivers who found a spot)
import matplotlib.pyplot as plt
# Collect data for varying VIP ratios
vip_ratios = [i / 10 for i in range(11)] # 0.0 to 1.0
dissatisfaction_vip = []
success_rate_vip = []
for vip_ratio in vip_ratios:
total_dissatisfaction = 0
total_parked = 0
simulations = 500
n_drivers = 10
penalty = 10
for _ in range(simulations):
preferences = [random.randint(1, n_drivers) for _ in range(n_drivers)]
num_vips = int(n_drivers * vip_ratio)
vip_indices = set(random.sample(range(n_drivers), num_vips))
vip_flags = [(i in vip_indices) for i in range(n_drivers)]
assignments, dissatisfaction = parking_with_vip_reservations(preferences, vip_flags, penalty)
total_dissatisfaction += dissatisfaction
total_parked += sum(1 for spot in assignments if spot != -1)
avg_diss = total_dissatisfaction / simulations
avg_parked = total_parked / simulations
dissatisfaction_vip.append(avg_diss)
success_rate_vip.append((avg_parked / n_drivers) * 100)
penalties = list(range(0, 55, 5)) # 0 to 50 in steps of 5
dissatisfaction_penalty = []
success_rate_penalty = []
for penalty in penalties:
total_dissatisfaction = 0
total_parked = 0
simulations = 500
n_drivers = 10
vip_ratio = 0.3
for _ in range(simulations):
preferences = [random.randint(1, n_drivers) for _ in range(n_drivers)]
num_vips = int(n_drivers * vip_ratio)
vip_indices = set(random.sample(range(n_drivers), num_vips))
vip_flags = [(i in vip_indices) for i in range(n_drivers)]
assignments, dissatisfaction = parking_with_vip_reservations(preferences, vip_flags, penalty)
total_dissatisfaction += dissatisfaction
total_parked += sum(1 for spot in assignments if spot != -1)
avg_diss = total_dissatisfaction / simulations
avg_parked = total_parked / simulations
dissatisfaction_penalty.append(avg_diss)
success_rate_penalty.append((avg_parked / n_drivers) * 100)
Plot the Results¶
# Plot 1: VIP Ratio vs Parking Outcomes
plt.figure(figsize=(10, 5))
plt.plot(vip_ratios, dissatisfaction_vip, marker='o', label="Avg Dissatisfaction")
plt.plot(vip_ratios, success_rate_vip, marker='s', label="Success Rate (%)")
plt.title("Effect of VIP Ratio on Parking Outcomes")
plt.xlabel("VIP Ratio")
plt.ylabel("Metric Value")
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()
Results: Effect of VIP Ratio on Parking Outcomes¶
Success Rate¶
- Remains very stable regardless of how many drivers are VIP
Dissatisfaction¶
- Stays fairly stable showing that with even more VIPs the simulation handles it well
Conclusion:¶
The parking system is resilient to the proportion of VIps. Even with 100% VIPs, people still manage to park at about the same rate and dissatisfaction levels don't spike
# Plot 2: Penalty vs Parking Outcomes
plt.figure(figsize=(10, 5))
plt.plot(penalties, dissatisfaction_penalty, marker='o', label="Avg Dissatisfaction")
plt.plot(penalties, success_rate_penalty, marker='s', label="Success Rate (%)")
plt.title("Effect of Penalty on Parking Outcomes (VIP Ratio = 0.3)")
plt.xlabel("Penalty Value")
plt.ylabel("Metric Value")
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()
Results: Effect of Penalty Value on Parking Outcomes (VIP Ratio= 0.3)¶
Success Rate¶
- Remains very stable, even with higher penalties
Dissatisfaction¶
- Increases significanlty as penalties increase (as expected)
- Around penalty= 30-50, the dissatisfaction starts to rise very fast- showing that not parking becomes very costly
Conclusion:¶
Penalty affects the cost, not the outcome. It doesn't change how many people can park, but increases dissatisfaction when parking fails.
Number of parking function¶
import random
import math
def simulate_multiple_runs(n_drivers=100, vip_ratio=0.3, penalty=10, simulations=1000):
total_dissatisfaction = 0
total_parked = 0
for _ in range(simulations):
# Each driver randomly chooses a preferred spot between 1 and n_drivers
preferences = [random.randint(1, n_drivers) for _ in range(n_drivers)]
# Assign VIPs randomly
num_vips = int(n_drivers * vip_ratio)
vip_indices = set(random.sample(range(n_drivers), num_vips))
vip_flags = [(i in vip_indices) for i in range(n_drivers)]
# Simulate one parking run
assignments, dissatisfaction = parking_with_vip_reservations(preferences, vip_flags, penalty)
# Store results
total_dissatisfaction += dissatisfaction
total_parked += sum(1 for spot in assignments if spot != -1)
# Calculate Averages
avg_dissatisfaction = total_dissatisfaction / simulations
avg_parked = total_parked / simulations
# Theoretical number of parking functions for comparison
theoretical_parking_functions = (n_drivers + 1) ** (n_drivers - 1)
print(f"\n--- Simulation Results ({simulations} runs) ---")
print(f"Average Total Dissatisfaction: {avg_dissatisfaction:.2f}")
print(f"Average Drivers Parked: {avg_parked:.2f} / {n_drivers}")
print(f"Average Parking Success Rate: {100 * avg_parked / n_drivers:.2f}%")
print(f"\nTheoretical Valid Parking Functions (n={n_drivers}): {(n_drivers + 1)}^{n_drivers - 1} = {theoretical_parking_functions:,}")
# run to function
simulate_multiple_runs(n_drivers=10, vip_ratio=0.3, penalty=10, simulations=1000)
--- Simulation Results (1000 runs) --- Average Total Dissatisfaction: 19.28 Average Drivers Parked: 8.71 / 10 Average Parking Success Rate: 87.08% Theoretical Valid Parking Functions (n=10): 11^9 = 2,357,947,691