C语言编程模拟超市抹零结账,STL实践项目之用queue模拟超市结账环节
前面章節介紹了 queue 容器適配器的具有用法,本節將利用 queue 模擬超市中結賬環節運轉的程序。
在超市營業過程中,結賬隊列的長度是超市運轉的關鍵因素。它會影響超市可容納的顧客數,因為太長的隊伍會使顧客感到氣餒,從而放棄排隊,這和醫院可用病床數會嚴重影響應急處理設施的運轉,是同樣的道理。
首先,我們要在頭文件 Customer.h 中定義一個類來模擬顧客:
#ifndef CUSTOMER_H
#define CUSTOMER_H
class Customer
{
protected:
size_t service_t {}; //顧客結賬需要的時間
public:
explicit Customer(size_t st = 10) :service_t {st}{}
//模擬隨著時間的變化,顧客結賬所需時間也會減短
Customer& time_decrement()
{
if (service_t > 0)
--service_t;
return *this;
}
bool done() const { return service_t == 0; }
};
#endif
這里只有一個成員變量 service_t,用來記錄顧客結賬需要的時間。每個顧客的結賬時間都不同。每過一分鐘,會調用一次 time_decrement() 函數,這個函數會減少 service_t 的值,它可以反映顧客結賬所花費的時間。當 service_t 的值為 0 時,成員函數 done() 返回 true。
超市的每個結賬柜臺都有一隊排隊等待的顧客。Checkout.h 中定義的 Checkout 類如下:
#ifndef CHECKOUT_H
#define CHECKOUT_H
#include // For queue container
#include "Customer.h"
class Checkout
{
private:
std::queue customers; //該隊列等到結賬的顧客數量
public:
void add(const Customer& customer) { customers.push(customer); }
size_t qlength() const { return customers.size(); }
void time_increment()
{
if (!customers.empty())
{
//有顧客正在等待結賬,如果顧客結賬了,就出隊列
if (customers.front().time_decrement().done())
customers.pop();
}
}
bool operator
bool operator>(const Checkout& other) const { return qlength() > other.qlength(); }
};
#endif
可以看到,queue 容器是 Checkout 唯一的成員變量,用來保存等待結賬的 Customer 對象。成員函數 add() 可以向隊列中添加新顧客。只能處理隊列中的第一個元素。 每過一分鐘,調用一次 Checkout 對象的成員函數 time_increment(},它會調用第一個 Customer 對象的成員函數 time_decrement() 來減少剩余的等待時間,然后再調用成員函數 done()。如果 done() 返回 true,表明顧客結賬完成,因此把他從隊列中移除。Checkout 對象的比較運算符可以比較隊列的長度。
為了模擬超市結賬,我們需要有隨機數生成的功能。因此打算使用 頭文件中的一個非常簡單的工具,但不打算深入解釋它。我們會在教程后面的章節深入探討 random 頭文件中的內容。程序使用了一個 uniform_int_distribution() 類型的實例。顧名思義,它定義的整數值在最大值和最小值之間均勻分布。在均勻分布中,所有這個范圍內的值都可能相等。可以在 10 和 100 之間定義如下分布:
std::uniform_int_distribution<> d {10, 100};
這里只定義了分布對象 d,它指定了整數值分布的范圍。為了獲取這個范圍內的隨機數,我們需要使用一個隨機數生成器,然后把它作為參數傳給 d 的調用運算符,從而返回一個隨機整數。 random 頭文件中定義了幾種隨機數生成器。這里我們使用最簡單的一個,可以按如下方式定義:
std::random_device random_number_engine;
為了在 d 分布范圍內生成隨機數,我們可以這樣寫:
auto value = d(random_number_engine);
value 的值在 d 分布范圍內。
完整模擬器的源文件如下:
#include // For standard streams
#include // For stream manipulators
#include // For vector container
#include // For string class
#include // For accumulate()
#include // For min_element & max_element
#include // For random number generation
#include "Customer.h"
#include "Checkout.h"
using std::string;
using distribution = std::uniform_int_distribution<>;
// 以橫向柱形圖的方式輸出每個服務時間出現的次數
void histogram(const std::vector& v, int min)
{
string bar (60, '*');
for (size_t i {}; i < v.size(); ++i)
{
std::cout << std::setw(3) << i+min << " " //結賬等待時間為 index + min
<< std::setw(4) << v[i] << " " //輸出出現的次數
<< bar.substr(0, v[i])
<< (v[i] > static_cast(bar.size()) ? "...": "")
<< std::endl;
}
}
int main()
{
std::random_device random_n;
//設置最大和最小的結賬時間,以分鐘為單位
int service_t_min {2}, service_t_max {15};
distribution service_t_d {service_t_min, service_t_max};
//設置在超市開業時顧客的人數
int min_customers {15}, max_customers {20};
distribution n_1st_customers_d {min_customers, max_customers};
// 設置顧客到達的最大和最小的時間間隔
int min_arr_interval {1}, max_arr_interval {5};
distribution arrival_interval_d {min_arr_interval, max_arr_interval};
size_t n_checkouts {};
std::cout << "輸入超市中結賬柜臺的數量:";
std::cin >> n_checkouts;
if (!n_checkouts)
{
std::cout << "結賬柜臺的數量必須大于 0,這里將默認設置為 1" << std::endl;
n_checkouts = 1;
}
std::vector checkouts {n_checkouts};
std::vector service_times(service_t_max-service_t_min+1);
//等待超市營業的顧客人數
int count {n_1st_customers_d(random_n)};
std::cout << "等待超市營業的顧客人數:" << count << std::endl;
int added {};
int service_t {};
while (added++ < count)
{
service_t = service_t_d(random_n);
std::min_element(std::begin(checkouts), std::end(checkouts))->add(Customer(service_t));
++service_times[service_t - service_t_min];
}
size_t time {};
const size_t total_time {600}; // 設置超市持續營業的時間
size_t longest_q {}; // 等待結賬最長隊列的長度
// 新顧客到達的時間
int new_cust_interval {arrival_interval_d(random_n)};
//模擬超市運轉的過程
while (time < total_time)
{
++time; //時間增長
// 新顧客到達
if (--new_cust_interval == 0)
{
service_t = service_t_d(random_n); // 設置顧客結賬所需要的時間
std::min_element(std::begin(checkouts), std::end(checkouts))->add(Customer(service_t));
++service_times[service_t - service_t_min]; // 記錄結賬需要等待的時間
//記錄最長隊列的長度
for (auto & checkout : checkouts)
longest_q = std::max(longest_q, checkout.qlength());
new_cust_interval = arrival_interval_d(random_n);
}
// 更新每個隊列中第一個顧客的結賬時間
for (auto & checkout : checkouts)
checkout.time_increment();
}
std::cout << "最大的隊列長度為:" << longest_q << std::endl;
std::cout << "\n各個結賬時間出現的次數::\n";
histogram(service_times, service_t_min);
std::cout << "\n總的顧客數:"
<< std::accumulate(std::begin(service_times), std::end(service_times), 0)
<< std::endl;
return 0;
}
直接使用 using 指令可以減少代碼輸入,簡化代碼。顧客結賬信息記錄在 vector 中。結賬時間減去 service_times 的最小值可以用來索引需要自增的 vector 元素,這導致 vector 的第一個元素會記錄下最少結賬時間出現的次數。histogram() 函數會以水平條形圖的形式生成每個服務時間出現次數的柱狀圖。
程序中 checkouts 的值為 600,意味著將模擬開業時間設置為 600 分鐘,也可以用參數輸入這個時間。main() 函數生成了顧客結賬時間,超市開門時等在門外的顧客數,以及顧客到達時間間隔的分布對象。我們可以輕松地將這個程序擴展為每次到達的顧客數是一個處于一定范圍內的隨機數。
通過調用 min_element() 算法可以找到最短的 Checkout 對象隊列,因此顧客總是可以被分配到最短的結賬隊列。在這次模擬開始前,當超市開門營業時,在門外等待的顧客的初始序列被添加到 Checkout 對象中,然后結賬時間記錄被更新。
模擬在 while 循環中進行,在每次循環中,time 都會增加 1 分鐘。在下一個顧客到達期間,new_cust_interval 會在每次循環中減小,直到等于 0。用新的隨機結賬時間生成新的顧客,然后把它加到最短的 Checkout 對象隊列中。這個時候也會更新變量 longest_q,因為在添加新顧客后,可能出現新的最長隊列。然后調用每個 Checkout 對象的 time_increment() 函數來處理隊列中的第一個顧客。
下面是一些示例輸出:
輸入超級中結賬柜臺的數量:2
等待超市營業的顧客人數:20
最大的隊列長度為:43
各個結賬時間出現的次數:
2?? 13 *************
3?? 20 ********************
4?? 11 ***********
5?? 16 ****************
6?? 12 ************
7?? 18 ******************
8?? 17 *****************
9?? 18 ******************
10?? 10 **********
11?? 22 **********************
12?? 19 *******************
13?? 13 *************
14?? 16 ****************
15?? 18 ******************
總的顧客數:223
這里有 2 個結賬柜臺,最長隊列的長度達到 43,已經長到會讓顧客放棄付款。
以上代碼還可以做更多改進,讓模擬更加真實,例如,均勻分配并不符合實際,顧客通常成群結隊到來。可以增加一些其他的因素,比如收銀員休息時間、某個收銀員生病工作狀態不佳,這些都會導致顧客不選擇這個柜臺結賬。
總結
以上是生活随笔為你收集整理的C语言编程模拟超市抹零结账,STL实践项目之用queue模拟超市结账环节的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: c语言for要分号错误,c语言for语句
- 下一篇: Linux下查看与修改mtu值