From 96f33bc328b7dea0dc529834241f8aa9d10d5643 Mon Sep 17 00:00:00 2001 From: shinya Date: Sat, 1 Mar 2025 22:04:52 +0100 Subject: [PATCH] First version --- Program.cs | 167 +++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 13 +++++ 2 files changed, 180 insertions(+) create mode 100644 Program.cs create mode 100644 README.md diff --git a/Program.cs b/Program.cs new file mode 100644 index 0000000..052a1ea --- /dev/null +++ b/Program.cs @@ -0,0 +1,167 @@ +using System.Reflection.Metadata.Ecma335; + +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() + { + BankSim bankSim = new BankSim(250, 15, 15, 5, MaxNumOfCounters: 5); + BankSim bankSim2 = 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 List> Customers = new List>(); + + public BankSim(uint CustomersPerDay, uint MaxWaitTime, uint MaxServiceTime, uint MinServiceTime, uint? MaxNumOfCounters = null, 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; + + + if (MaxNumOfCounters == null) + { + this.MaxNumOfCounters = CustomersPerDay; + } + + if (TimeCoef != null && TimeCoef.Length == 11) //! Remove check for 11 elements in array + { + NumOfCustomersCoef = TimeCoef; + } + + // 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}"); + + Customers.Clear(); + Console.WriteLine(); + day_hour++; + } + } + + public uint CalcNumOfCustomersPerHour(float DayTime) + { + // Calculate the number of customers for each hour of the day + NumOfCustomers = (uint)(CustomersPerDay * DayTime); + return NumOfCustomers; + } + + public 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; + } + + public 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(); + } + + + } +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..8236ad6 --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +# Bank Calc + +- počet otevřených přepážek nikdy nemůže být vyšší než počet zákazníků za dannou hodinu +- čekací doba musí zároveň odpovídat max 15 minutám pro každého zákazníka +- počet přepážek musí být co nejnižší + +## Postup + +- generace počtu zákazníků za dannou hodinu +- náhodná generace času příchodu zákazníků +- výpočet dob čekání jednotlivých zákazníků pro počty otevřených přepážek (od maximálního počtu zákazníků až po 1 přepážku) +- přepočet počtu zákazníků kteří nebyli uspokojeni a odešli na procenta (90% zákazníků bylo uspokojeno) +- filtrace pouze přepážek které splňují požadavek na co nejvyšší spokojenost zákazníků (není menší než 80% třeba)