2016-04-07 2 views
1

Я занимаюсь имитацией банка, в котором у меня есть одна очередь и три кассира; целью является получение статистики, например, среднее время ожидания клиентов. У каждого кассира разное внимание, и каждый клиент приходит в банк каждые 1,5 минуты. У банка всего 5 часов. Теперь, есть ли способ, которым я мог бы программировать его в реальном времени (потому что я думаю, что это единственный способ), а затем каким-то образом ускорить jvm, чтобы быстрее получить статистику?Fast-foward в реальном времени java-моделирование

+0

Я уверен, что это просто как раз, когда математика уравнение придет в ... Не «быстрая перемотка вперед» виртуальная машина ... что в техническом плане нет .. вы не может – 3kings

+0

Теперь я могу полностью упустить эту метку, но вы должны разработать концепцию «тика», в которой один тик = единица времени irl. Чтобы ускорить или замедлить время в симуляции, вы просто увеличиваете количество тиков в секунду. –

+0

«в реальном времени» противоречит «быстрой перемотке», но если у вас есть симуляция, тогда у вас есть концепция периодов времени, которые вы просто разделите. Например, деление на 2 приведет к тому, что симуляция будет идти в два раза быстрее. –

ответ

1

Вы не можете ускорить перемотку JVM, но вы можете запрограммировать это, чтобы имитировать в режиме реального времени, чтобы получить статистику в ближайшее время. Играйте с переменными, которые представляют время в достаточно малой единице времени (например, в секундах) и обычно выполняют симуляцию, увеличивая время в одном устройстве. Для очереди вы можете использовать деку, содержащую моменты, когда каждый клиент входит в банк, а затем повторяется со временем.

private static int TOTAL_TIME = 5*3600; // Hours to seconds 
private static int TIME_BETWEEN_CLIENTS = 90; // In seconds 
private static int CASHIERS = 3; 

public static void main(String[] args) { 

    // Create cashiers and assign range of attentions time to each cashier, in seconds 
    // This is an example for cashiers with 3, 3~6 and 4.5~15 minutes 
    List<Cashier> cashiers = new ArrayList<Cashier>(CASHIERS); 
    cashiers.add(new Cashier(180, 180)); 
    cashiers.add(new Cashier(180, 360)); 
    cashiers.add(new Cashier(270, 900)); 

    int time = 0; // Counting variable in seconds 
    int waitingTime = 0; // Save here all waiting time for all clients 
    int clients = 0; // Save here all clients 

    // Register here all available cashiers 
    ArrayList<Cashier> freeCashiers; 

    Deque<Integer> queue = new ArrayDeque<Integer>(); // Clients queue 

    // Iterate until bank closes and all clients have been attended 
    while (time < TOTAL_TIME || !queue.isEmpty()) { 
     // New client if the bank is not closed 
     if (time < TOTAL_TIME && time%TIME_BETWEEN_CLIENTS == 0) { 
      queue.add(time); // Register customer start waiting time 
      clients++; 
     } 

     // Check for free cashiers when someone is on queue 
     if (!queue.isEmpty()) { 
      freeCashiers = new ArrayList<Cashier>(CASHIERS); 
      for (Cashier c : cashiers) { 
       if (c.isFree(time)) 
        freeCashiers.add(c); 
      } 

      if (!freeCashiers.isEmpty()) { 
       // Register spent time for the removed client 
       waitingTime += time - queue.removeFirst(); 

       // Select a random cashier from all the available cashiers 
       Cashier randomAvailableCashier = freeCashiers.get(Cashier.RANDOM.nextInt(freeCashiers.size())); 

       // Register when the randomly selected cashier will be free again 
       randomAvailableCashier.attendNewClient(time); 
      } 
     } 
     time++; // Adds one second 
    } 

    // Calculate statistics 
    int avgWaitingTime = waitingTime/clients; // In seconds 

    System.out.println("Average waiting time on queue: " + formatTime(avgWaitingTime)); 
} 

/** 
* Formats a time in minutes and seconds 
* @param time the time in seconds 
* @return the formatted time 
*/ 
private static String formatTime(int time) { 
    StringBuilder result = new StringBuilder(); 
    if (time > 60) { 
     result.append(time/60).append(" minutes"); 
     time %= 60; 
     if (time > 0) 
      result.append(" and "); 
     else 
      result.append("."); 
    } 
    if (time > 0) 
     result.append(time).append(" seconds."); 
    return result.toString(); 
} 

И класс Кассира:

public class Cashier { 
    public static final Random RANDOM = new Random(); 

    private int minAttentionTime, maxAttentionTime, endTime; 

    /** 
    * Constructs new Cashier with a range of possible attention time, in seconds 
    * @param minAttentionTime in seconds 
    * @param maxAttentionTime in seconds 
    */ 
    public Cashier(int minAttentionTime, int maxAttentionTime) { 
     this.minAttentionTime = minAttentionTime; 
     this.maxAttentionTime = maxAttentionTime; 
     endTime = 0; 
    } 

    /** 
    * Register end time with a random attention time in the range. 
    * @param currentTime the current time in seconds 
    */ 
    public void attendNewClient(int currentTime) { 
     endTime = currentTime + getRandomNumberInRange(minAttentionTime, maxAttentionTime); 
    } 

    /** 
    * Returns if this cashier is available 
    * @param currentTime the current time in seconds 
    * @return true if this cashier is free, false otherwise 
    */ 
    public boolean isFree(int currentTime) { 
     return currentTime >= endTime; 
    } 

    /** 
    * Returns a random number in range [min, max] 
    * @param min the minimum number, inclusive 
    * @param max the maximum number, inclusive 
    * @return a random number in range [min, max] 
    */ 
    private int getRandomNumberInRange(int min, int max) { 
     return RANDOM.nextInt(max - min + 1) + min; 
    } 

    public String toString() { 
     return String.valueOf(endTime); 
    } 
} 

С этой реализацией новым клиент будет идти к случайному свободному кассиру.

+0

А что, если у каждого кассира есть такая же возможность выбора? Должно ли генерироваться случайное число, разделенное на три, а затем проверенное значение для выбора? – niklabaz

+1

Нет, если вы разделите случайное число, вы получите доступ к позиции за пределами. Вы можете создать случайное число в диапазоне с Random nextInt (arrayLenght), но вы должны проверить все позиции, так что лучший способ - изменить массив для списка и сделать Collections.shuffle (cashiersEndTime) для рандомизации позиций до итерации а затем проверить с первой позиции, как на самом деле. –

+0

Это не работает для небольших времен ожидания, Im пытается с кассиром ОДИН, время ожидания от 0,5 до 1,5 минут, кассир ДВА от 0,5 до 2 минут, а cahsier THREE от 0,5 до 2,5 минут. Также с клиентами, произвольно выбирающими, если есть более одного бесплатного кассира. – niklabaz

1

Если вы использовали систему очередей, вы могли бы пойти очень быстро. Очередь будет содержать события, которые идут вверх, и временную метку, когда они подлежат. Очередь будет организована по метке события. Первоначально вы могли засеять эту очередь новым клиентом каждые 1,5 минуты, а событие для закрытия банка через 5 часов. Затем вы вытаскиваете первое событие из очереди, выясняете, что это вызывает. Например, первое событие ввода клиента заставляет вас увеличить счет очереди ожидания клиента. Кассир, становящийся свободным, вызывает подсчет очереди ожидания и активность агента-клиента, которая длится для некоторой случайной длины времени. Это приводит к тому, что в очередь помещается новое событие (другое приглашение). Когда кассир свободен, и нет клиентов в очереди, вы устанавливаете пустой флажок кассира, который будет проверяться, когда вы будете обрабатывать событие ввода клиента. Вы можете быстро проработать весь день в течение нескольких секунд с подходом, и вы можете собирать статистику о том, что вы хотите на каждом мероприятии.

+0

Я не могу посеять очередь, а затем вводить другие события между ними. Я просто мог бы занять место за закрытием банковского события, не так ли? – niklabaz

+1

С объектом очереди акций, правильно, но вы можете легко написать свои собственные функции очереди, которые работают со стандартным списком объектов событий. Затем ваш метод push может вставить новое событие в метку времени или событие. –

0

Если вы не против использовать формулы, здесь моя математика. Если у какого-то кассира есть время T1 для обработки одного человека, то этот кассир обрабатывает 1/T1 человек за единицу времени. Количество людей, обработанных кассирами N, добавляется. Возвращаясь от скорости обработки на время обработки мы получаем общее время для обработки одного человека всех кассиров:

Tcom = 1/(1/T1 + 1/T2 + ... + 1/Tn) 

Вы сравните это время с 1,5 минут, а если TCOM < = 1,5 минуты, то ваша очередь будет в основном пуста или состоят из 1 человека (а среднее время ожидания будет около половины среднего значения Ti). В случае Tcom> 1,5 минуты ваша очередь будет постоянно расти на одного человека, каждый Tcom - 1,5 минуты. Длина очереди в конце рабочего дня будет Lavg = 5 * 60/(Tcom - 15) человек (и половина из них в среднем). Каждый человек в голове очереди удаляется из него каждые минуты Tcom. Это означает, что среднее время ожидания будет Tcom * Lavg/2.

Я знаю, что это очень приблизительные оценки, но почему бы и нет?

1

(Вы удалили другой почти такой же вопрос. Вот длинный ответ я уже писал, зная, что. Хорошо, что вы имели эту копию, так что я не тратить мое время)

Вот функционал псевдо. Я действительно закодировал это, из любопытства.

Я использую компьютерный язык под названием APL, который, скорее всего, неизвестен вам, следовательно, псевдо.

Вам необходимо создать двумерную таблицу, т.е. массив с шестью столбцами. Если вы не можете создавать двумерные массивы, вы можете альтернативно (надеюсь) создать одномерные массивы, т.е. списки, в которых один список содержит значения одного столбца в таблице. Важно то, что вы можете индексировать в массивы. Если вы используете listst, вы должны немного расширить код.

Вы будете работать только с целыми значениями.

Решение состоит в том, что вы предварительно создали таблицу, в которой время прибытия клиента в столбце 1. Все остальные столбцы содержат нули для начала. Затем вы просматриваете таблицу, обновляя другие столбцы, по очереди, используя вспомогательную переменную для кассиров.

После того, как вы зациклились на всех строках таблицы, вы извлекаете из него желаемые результаты, путем дополнительных вычислений.

Для действий кассиров вам нужна еще одна двумерная таблица, держащая [nr кассиров] строки и 3 столбца. Аналогично, вы можете заменить его на 3 списка, по одному на столбец таблицы. Столбцы 2 и 3 в этом маленьком столике накапливают каждого кассира, потраченного на обслуживание клиента, и время, которое они провели в ожидании клиентов.

Вы не работаете с часами, но с секундами с начала дня. Например, 8am равно 28800 (т.е. количество секунд, прошедших с начала дня). К этому значению вы можете легко добавить время прибытия клиента с шагом в 90 секунд (или любое, произвольное количество секундных интервалов). Вы заранее создаете время прибытия клиента для всего 5-часового (или любого часа) дня.

// Create a table with 6 columns. Columns are 
// 1: Customer arrival timestamp (first value is 28800) [seconds since midnight] 
// 2: The timestamp the customer was attended [seconds since midnight] 
// 3: The time a customer had to wait [seconds] 
// 4: The time the customer spent with the cashier [seconds] 
// 5: The timestamp the customer leaves the bank [seconds since midnight] 
// 6: The cashier # that provided service, 1,2 or 3 in your case 
// 
// If the customer arrive at regular 90 s intervals, the table now looks like this 
// (10 first rows only, but you must populate it up to bank closing time) 
// which would need ca 200 rows. Call this table "Cust": 
// 28800 0 0 0 0 0 
// 28890 0 0 0 0 0 
// 28980 0 0 0 0 0 
// 29070 0 0 0 0 0 
// 29160 0 0 0 0 0 
// 29250 0 0 0 0 0 
// 29340 0 0 0 0 0 
// 29430 0 0 0 0 0 
// 29520 0 0 0 0 0 
// 29610 0 0 0 0 0 
// 
// Create a variable for the cashiers, 1 row per cashier. Columns are: 
// 1. An "action timestamp", initially 28800 [seconds since midnight] 
// 2. Accumulation of cashiers attention time [seconds] 
// 3. Accumulation of cashiers wait/slack time [seconds] 
// 
// The table now looks like this 
// (add more rows if you have more cashiers). Call this table "Cash": 
// 28800 0 0 
// 28800 0 0 
// 28800 0 0 

rows = [numers of rows in Cust] 
i = 0 

:While i < rows 
// Note! This pseudocode uses 0-origin, ie. array[0] is the first element 
// Find the row number of Cash with the _smallest_ value in it's column 1 
    row = [0, 1 or 2] // You commonly first find the smallest number, then compare it against each row. The match is the row with smallest number. 
// Attention time for this customer (you said "range of"), 
// we use 265 s now, for simplicity, but you can give time another value each time 
    time = 265 

    :If Cust[i;0]<Cash[row;0]    // Customer has waited 
     Cust[i;1]=Cash[row;0]    // Timestamp this Customer got service 
     Cust[i;3]=time      // Time spent with cashier 
     Cust[i;4]=Cash[row;0]+time   // Timestamp customer leaves the bank 
     Cust[i;5]=row      // Which cashier provided the service 
     Cash[row;1]+=time     // Increase accumulated attend time for cashier 
     Cash[row;0]+=time     // Next timestamp this cashier is free 
    :Else         // Cashier had free time (bank was empty until now) 
     Cash[row;2]+=Cust[i;0]-Cash[row;0] // Accumulate freetime for cashier 
     Cash[row;1]+=time     // Accumulate attend time for cashier 
     Cust[i;1]=Cust[i;0]    // There was no wait time for customer 
     Cust[i;3]=time      // Time spent with cashier 
     Cust[i;4]=Cust[i;0]+time   // Timestamp customer leaves the bank 
     Cust[i;5]=row      // Which cashier provided the service 
     Cash[row;0]=Cust[i;0]+time   // Next timestamp this cashier is free 
    :End 
    i+←1 
:End 

// Resolve customer wait times, equals [time attended] - [time arrived] and 
// populate 3rd column of Cust (you must probably loop), row by row 
Cust[n;2] = Cust[n;1] - Cust[n;0] // n = each row 

// Cust now looks like this: 
// 28800 28800 0 265 29065 0 
// 28890 28890 0 265 29155 1 
// 28980 28980 0 265 29245 2 
// 29070 29070 0 265 29335 0 
// 29160 29160 0 265 29425 1 
// 29250 29250 0 265 29515 2 
// 29340 29340 0 265 29605 0 
// 29430 29430 0 265 29695 1 
// 29520 29520 0 265 29785 2 
// 29610 29610 0 265 29875 0 

fnAvg = [function that calculates the average of a list of numbers] 

// Extract the results 
// Note: For example "Cust[;2]" means _all rows in column 2 of Cust_, ie. a list of numbers 
// You don't need to create any lists though, just loop through them all and calculate/accumulate 
    'Nr of customers attended immediately: ',[number of zeroes in Cust[;2]] 
    'Nr of customers who waited: ',[number of non-zeroes in Cust[;2]] 
    'Wait times: ',[all numbers in Cust[;2] - this is a list of numbers] 
    'Average wait times: ',[avg Cust[;2]] 
    'Average wait times (only those who waited): ',[avg Cust[;2] elements that are non-zero] 
    'Total cashier attend times: ',Cash[;1] 
    'Total cashier free times: ',Cash[;2] 

// And finally a verification calc (backwards calc, just for curiosity) 
    'Check: Total customer existance time: ',Cust[i-1;4]-Cust[0;0] 
    'Check: Cashier total times (should be close to above value): ',[sum of Cash[n;1 2]] 

В результате с указанными выше аргументами (числа не секунды, если иное сказана):

Nr of customers attended immediately: 10 
Nr of customers who waited: 0 
Wait times: 0 0 0 0 0 0 0 0 0 0 
Average wait times: 0 
Average wait times (only those who waited): 0 
Total cashier attend times: 1060 795 795 
Total cashier free times: 15 100 190 
Check: Total customer existance time: 1075 
Check: Cashier total times (should be close to previous row): 1075 895 985 

Если у вас есть 20 клиентов, прибывающие и использовать случайные времена ПРИСУТСТВУЙТЕ между 100 и 480 секундами, вы можете получить, например:

Nr of customers served immediately: 8 
Nr of customers waited: 12 
Wait times: 0 0 0 0 0 14 0 0 0 200 120 147 183 122 149 111 185 178 244 218 
Average wait times: 93.55 
Average wait times (only those who waited): 155.9166667 
Total cashier attend times: 1820 1836 1819 
Total cashier free times: 217 197 309 
Check: Total customer existance time: 2128 
Check: Cashier total times (should be close to previous row): 2037 2033 2128 

with Cust (edited: was erratically Cash) table looking like this: 

28800 28800 0 160 28960 0 
28890 28890 0 433 29323 1 
28980 28980 0 114 29094 2 
29070 29070 0 194 29264 0 
29160 29160 0 117 29277 2 
29250 29264 14 149 29413 0 
29340 29340 0 470 29810 2 
29430 29430 0 390 29820 1 
29520 29520 0 417 29937 0 
29610 29810 200 253 30063 2 
29700 29820 120 389 30209 1 
29790 29937 147 155 30092 0 
29880 30063 183 445 30508 2 
29970 30092 122 169 30261 0 
30060 30209 149 216 30425 1 
30150 30261 111 403 30664 0 
30240 30425 185 408 30833 1 
30330 30508 178 220 30728 2 
30420 30664 244 173 30837 0 
30510 30728 218 200 30928 2 

Попробуйте с наполнением 1-й столбца Каст с другой, более случайным клиентом arrivbal раз, и для каждой итерации, время со значениями, которые соответствуют вашему реальная окружающая среда лучше.

Это псевдо поддерживает как «банк временно пуст», так и «длинный que all time», а также поддерживает «Как только банк закрывается через 5 часов, каждый клиент получает» -критерий (оставшиеся клиенты будут очищены »,).

Удачи! :-)

Смежные вопросы