namespace MonteCarlo_Bank { //! Because of dividing day into 11 parts (working hours) //! last served customer will probably overflow the hour //! and be served in the next hour, i will ignore this for now class Program { static void Main() { /* * uint CustomersPerDay - 300 customers per day, * uint MaxWaitTime - 15 min. maximal waiting time, * uint MaxServiceTime - 15 min. maximal service time, * uint MinServiceTime - 5 min. minimal service time * uint MaxNumOfCounters is optional - Maximal number of opened counters * uint float[11] TimeCoef is optional - Coefficients for number of customers for each hour */ _ = new BankSim(250, 15, 15, 5); } } class BankSim { public uint CustomersPerDay; public uint MaxWaitTime; public uint MaxServiceTime; public uint MinServiceTime; public uint MaxNumOfCounters; private float[] NumOfCustomersCoef = new float[] { 0.04f, 0.1f, 0.15f, 0.08f, 0.1f, 0.6f, 0.09f, 0.14f, 0.1f, 0.12f, 0.02f }; private uint NumOfCustomers; private static Random random = new Random(); private Dictionary> result = new Dictionary>(); private List> Customers = new List>(); public BankSim(uint CustomersPerDay, uint MaxWaitTime, uint MaxServiceTime, uint MinServiceTime, uint? MaxNumOfCounters = 250, float[]? TimeCoef = null) // Time is in minutes { // Set the parameters for the simulation this.CustomersPerDay = CustomersPerDay; this.MaxWaitTime = MaxWaitTime; this.MaxServiceTime = MaxServiceTime; this.MinServiceTime = MinServiceTime; this.MaxNumOfCounters = MaxNumOfCounters ?? 250; if (TimeCoef != null && TimeCoef.Length == 11) //! Remove check for 11 elements in array { NumOfCustomersCoef = TimeCoef; } Console.WriteLine("Running simulation with this parameters:"); Console.WriteLine($"Total Number Of Customers: {this.CustomersPerDay}"); Console.WriteLine($"Maximal Customer Waiting Time To Tolerate: {this.MaxWaitTime} min."); Console.WriteLine($"Maximal Service Time Of One Customer: {this.MaxServiceTime} min."); Console.WriteLine($"Minimal Service Time Of One Customer: {this.MinServiceTime} min."); Console.WriteLine($"Maximal Number Of Opened Counters: {this.MaxNumOfCounters} min."); Console.WriteLine(); // Generate the number of customers for each hour and run simulation for each hour uint day_hour = 1; foreach (var DayTimeCoef in NumOfCustomersCoef) { Console.WriteLine($"Daytime: {day_hour}"); CalcNumOfCustomersPerHour(DayTimeCoef); Customers = GenCustTimesOfArrivalAndService(); // Run simulation for number of counters from 1 to number of customers List satisfied_coefficient = new List(); for (int i = 1; i <= MaxNumOfCounters; ++i) { //Console.WriteLine($"Number of counters: {i}"); List waiting_time_per_customer = RunSimulationHour(i); satisfied_coefficient.Add(SatisfiedCustomers(waiting_time_per_customer)); //Console.WriteLine(); } // Select number of counter with the satisfied coefficient int best_counter = satisfied_coefficient.IndexOf(satisfied_coefficient.Max()) + 1; Console.WriteLine($"Best number of counters: {best_counter}, satisfied customers {satisfied_coefficient.Max() * 100}%"); result[day_hour] = new KeyValuePair((uint)best_counter, satisfied_coefficient.Max()); Customers.Clear(); Console.WriteLine(); day_hour++; } Console.WriteLine("Simulation finished."); Console.WriteLine(); } private uint CalcNumOfCustomersPerHour(float DayTime) { // Calculate the number of customers for each hour of the day NumOfCustomers = (uint)(CustomersPerDay * DayTime); return NumOfCustomers; } private List> GenCustTimesOfArrivalAndService() { // Generate random times of arrival for each customer (from one to 60 minutes) for (uint i = 0; i < NumOfCustomers; ++i) { uint Time = (uint)random.Next(1, 60); uint ServiceTime = (uint)random.Next((int)MinServiceTime, (int)MaxServiceTime); Customers.Add(new KeyValuePair(Time, ServiceTime)); } return Customers; } private List RunSimulationHour(int counters) { Customers.Sort((x, y) => x.Key.CompareTo(y.Key)); // Queue for waiting customers Queue<(uint arrivalTime, uint serviceTime, uint waitingTime)> waitingQueue = new Queue<(uint, uint, uint)>(); // Track counters List<(uint endTime, uint arrivalTime, uint waitingTime)> activeCounters = new List<(uint, uint, uint)>(); List waiting_time = new List(); for (uint i = 0; i < 10000; ++i) //! Optimize this number somehow maybe { // Add new arriving customers to waiting queue foreach (var customer in Customers.FindAll(x => x.Key == i)) { waitingQueue.Enqueue((customer.Key, customer.Value, 0)); } // Free up counters where customers have finished service activeCounters.RemoveAll(counter => counter.endTime == i); // Assign waiting customers to available counters while (activeCounters.Count < counters && waitingQueue.Count > 0) { var (arrivalTime, serviceTime, waitingTime) = waitingQueue.Dequeue(); uint startTime = Math.Max(i, arrivalTime); uint endTime = startTime + serviceTime; activeCounters.Add((endTime, arrivalTime, waitingTime)); //Console.WriteLine($"customer {arrivalTime}-{endTime} | served at {startTime} min. for {serviceTime} min. | waiting {waitingTime}"); waiting_time.Add(waitingTime); } // Increase waiting time for customers still in queue int queueSize = waitingQueue.Count; for (int j = 0; j < queueSize; j++) { var customer = waitingQueue.Dequeue(); waitingQueue.Enqueue((customer.arrivalTime, customer.serviceTime, customer.waitingTime + 1)); } } return waiting_time; } private double SatisfiedCustomers(List waiting_times) { uint unsatisfied = 0; foreach (var time in waiting_times) { if (time > MaxWaitTime) { unsatisfied++; } } // Calculate the number of satisfied customers uint satisfied = (uint)(waiting_times.Count() - unsatisfied); // Return the percentage of satisfied customers as a value between 0 and 1 return (double)satisfied / waiting_times.Count(); } } }