First version

This commit is contained in:
shinya 2025-03-01 22:04:52 +01:00
commit 96f33bc328
2 changed files with 180 additions and 0 deletions

167
Program.cs Normal file
View File

@ -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<KeyValuePair<uint, uint>> Customers = new List<KeyValuePair<uint, uint>>();
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<double> satisfied_coefficient = new List<double>();
for (int i = 1; i <= MaxNumOfCounters; ++i)
{
//Console.WriteLine($"Number of counters: {i}");
List<uint> 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<KeyValuePair<uint, uint>> 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<uint, uint>(Time, ServiceTime));
}
return Customers;
}
public List<uint> 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<uint> waiting_time = new List<uint>();
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<uint> 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();
}
}
}

13
README.md Normal file
View File

@ -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)