日韩久久久精品,亚洲精品久久久久久久久久久,亚洲欧美一区二区三区国产精品 ,一区二区福利

用Python的SimPy庫簡化復雜的編程模型的介紹

系統 1819 0

在我遇到 SimPy 包的其中一位創始人 Klaus Miller 時,從他那里知道了這個包。Miller 博士閱讀過幾篇提出使用 Python 2.2+ 生成器實現半協同例程和“輕便”線程的技術的 可愛的 Python專欄文章。特別是(使我很高興的是),他發現在用 Python 實現 Simula-67 樣式模擬時,這些技術很有用。

結果表明 Tony Vignaux 和 Chang Chui 以前曾創建了另一個 Python 庫,它在概念上更接近于 Simscript,而且該庫使用了標準線程技術,而不是我的半協同例程技術。該小組在一起研究時,認為基于生成器的樣式更有效得多,并且最近在 SourceForge 上發起了使用 GPL 的項目,稱為 SimPy(請參閱 參考資料,獲得 SimPy 主頁的鏈接),目前處于 beta 測試版狀態。Vignaux 教授希望他在惠靈頓維多利亞大學(University of Victoria)的將來大學教學中使用統一的 SimPy 包;我相信該庫也非常適合應用到各類實用問題中。

我承認在近期的通信交流和調查研究之前,我對編程領域的模擬方面沒有任何基礎知識。我猜想本專欄文章的大部分讀者也和我一樣,對這方面的知識知之甚少。盡管有人會認為這種樣式編程的方式有些新奇,但在理解資源有限的實際系統的行為時,模擬是很有用的。不管您感興趣的是有限帶寬網絡、汽車交通行為、市場和商業性優化、生物/進化的交互作用還是其它“隨機”系統,SimPy 對這樣的建模都提供了簡單的 Python 工具。
隨機的定義

與“連接”相類似,它是那些 最適合形容其作業的詞匯之一 - 再也找不到更適合的了:

隨機(stochastic),源自希臘語 stokhastikos(形容詞)
1)推測的、與推測相關的或者具有推測特點的;好推測的。
2)在統計學上:涉及或包含一個隨機變量或多個隨機變量,或涉及偶然性或概率。

來源:Dictionary.com

在本專欄文章中,我將一直使用食品雜貨店內具有多條通道的付款區域這個相當簡單的示例。通過使用所演示的模擬,我們可以根據對掃描器技術、購物者習慣、人員配備需求等進行的各種更改所產生的經濟上和等待時間上的含義提出問題。這個建模的優點是在您對所做的更改產生的含義有清晰的想法時,它讓您能提前制定策略。很明顯,大多數讀者不會專門經營一家食品雜貨店,但這些技術可以廣泛地應用于各類系統中。
模擬的概念

SimPy 庫只提供了三個抽象/父類,并且它們對應于模擬的三個基本概念。有許多其它常規函數和常量用于控制模擬的運行,但重要的概念都與這些類結合在一起。

模擬中的核心概念是 進程。一個進程只是一個對象,它完成某些任務,隨后在它準備完成下一個任務之前有時會等待一會兒。在 SimPy 中,您還可以“鈍化”進程,這意味著在一個進程完成一個任務后,只有當其它進程要求該進程完成其它任務時,它才會去做。把進程當作嘗試完成一個目標,常常是很有用的。在編寫進程時,通常把它編寫成可以在其中執行多個操作的循環。在每個操作之間,可以插入 Python“yield”語句,它讓模擬調度程序在返回控制之前執行每個等待進程的操作。

進程執行的許多操作取決于 資源的使用。資源只是在可用性方面受到限制。在生物學模型中,資源可能是食物供應;在網絡模型中,資源可以是路由器或有限帶寬通道;在我們的市場模擬中,資源是付款通道。資源執行的唯一任務是在任何給定的時間內將它的使用限于一個特定的進程上。在 SimPy 編程模型下,進程單獨決定它要保留資源的時間有多長,資源本身是被動的。在實際系統中,SimPy 模型可能適合概念性方案,也可能不適合;很容易想象到資源在本質上會限制其利用率(例如,如果服務器計算機在必需的時間幀內沒有獲得滿意的響應,則它會中斷連接)。但作為編程問題,進程或資源是否是“主動”方就不是特別重要(只要確保您理解了您的意圖)。

最后一個 SimPy 類是 監控程序。實際上監控程序不是很重要,只不過它很方便。監控程序所做的全部任務就是記錄向它報告的事件,并保存有關這些事件的統計信息(平均值、計數、方差等)。該庫提供的 Monitor 類對記錄模擬措施是個有用的工具,但您也可以通過您想使用的其它任何技術來記錄事件。事實上,我的示例使 Monitor 子類化,以提供某些(稍微)增強的能力。


設置商店:對模擬編程

在我所撰寫的大部分文章中,我都會馬上給出樣本應用程序,但在本例中,我認為帶您經歷食品雜貨店應用程序的每個步驟會更有用。如果您愿意的話,可以把每個部分剪貼在一起;SimPy 創造者們將在將來的發行版中包含我的示例。

SimPy 模擬中的第一步是幾個常規的導入(import)語句:
清單 1. 導入 SimPy 庫

            
#!/usr/bin/env python
from __future__ import generators
from SimPy import Simulation
from SimPy.Simulation import hold, request, release, now
from SimPy.Monitor import Monitor
import random
from math import sqrt


          

有些 SimPy 附帶的示例使用 import * 樣式,但我更喜歡使我填充的名稱空間更清晰。對于 Python 2.2(SimPy 所需的最低版本),將需要如指出的那樣,導入生成器特性。對于 Python 2.3 以后的版本,不需要這樣做。

對于我的應用程序,我定義了幾個運行時常量,它們描述了在特定的模擬運行期間我感興趣的幾個方案。在我更改方案時,我必須在主腳本內編輯這些常量。要是這個應用程序的內容更充實,那么我就可能用命令行選項、環境變量或配置文件來配置這些參數。但就目前而言,這個樣式已經足夠了:
清單 2. 配置模擬參數

            
AISLES = 5     # Number of open aisles
ITEMTIME = 0.1   # Time to ring up one item
AVGITEMS = 20   # Average number of items purchased
CLOSING = 60*12  # Minutes from store open to store close
AVGCUST = 1500   # Average number of daily customers
RUNS = 10     # Number of times to run the simulation


          

我們的模擬需要完成的主要任務是定義一個或多個進程。對于模擬食品雜貨店,我們感興趣的進程是在通道處付款的顧客。
清單 3. 定義顧客的操作

            
class Customer(Simulation.Process):
  def __init__(self):
    Simulation.Process.__init__(self)
    # Randomly pick how many items this customer is buying
    self.items = 1 + int(random.expovariate(1.0/AVGITEMS))
  def checkout(self):
    start = now()      # Customer decides to check out
    yield request, self, checkout_aisle
    at_checkout = now()   # Customer gets to front of line
    waittime.tally(at_checkout-start)
    yield hold, self, self.items*ITEMTIME
    leaving = now()     # Customer completes purchase
    checkouttime.tally(leaving-at_checkout)
    yield release, self, checkout_aisle


          

每位顧客已經決定采購一定數量的商品。(我們的模擬不涉及從食品雜貨店通道上選擇商品;顧客只是推著他們的手推車到達付款處。)我不能確定這里的指數變量分布確實是一個精確的模型。在其低端處我感覺是對的,但我感到對實際購物者究竟采購了多少商品的最高極限有點失實。在任何情況下,您可以看到如果可以使用更好的模型信息,則調整我們的模擬是多么簡單。

顧客采取的操作是我們所關注的。顧客的“執行方法”就是 .checkout() 。這個進程方法通常被命名為 .run() 或 .execute() ,但在我的示例中, .checkout() 似乎是最可描述的。您可以對它起任何您希望的名稱。 Customer 對象所采取的實際 操作僅僅是檢查幾個點上的模擬時間,并將持續時間記錄到 waittime 和 checkouttime 監控程序中。但在這些操作之間是至關重要的 yield 語句。在第一種情況中,顧客請求資源(付款通道)。只有當顧客獲得了所需的資源之后,他們才能做其它操作。一旦來到付款通道,顧客實際上就在付款了 ― 所花時間與所購商品的數量成比例。最后,經過付款處之后,顧客就釋放資源,以便其他顧客可以使用它。

上述代碼定義了 Customer 類的操作,但我們需要在運行模擬之前,創建一些實際的顧客對象。我們 可以為一天中將要購物的每位顧客生成顧客對象,并為每位顧客分配相應的付款時間。但更簡潔的方法是“在每位顧客到商店時”,讓工廠對象生成所需的顧客對象。實際上模擬并不會同時對一天內將要購物的所有顧客感興趣,而是只對那些要同時爭用付款通道的顧客感興趣。注意: Customer_Factory 類本身是模擬的一部分 ― 它是一個進程。盡管對于這個客戶工廠,您可能聯想到人造的機器工人(la Fritz Lang 的 Metropolis),但還是應該只把它看作編程的便利工具;它并不直接對應已建模域中的任何事物。
清單 4. 生成顧客流

            
class Customer_Factory(Simulation.Process):
  def run(self):
    while 1:
      c = Customer()
      Simulation.activate(c, c.checkout())
      arrival = random.expovariate(float(AVGCUST)/CLOSING)
      yield hold, self, arrival


          

正如我前面提到的,我想收集一些當前 SimPy Monitor 類沒有解決的統計信息。也就是,我并不僅僅對平均付款時間感興趣,而且還對給定方案中最糟糕情況感興趣。所以我創建了一個增強的監控程序,它收集最小和最大的計數值。
用監控程序監視模擬

            
class Monitor2(Monitor):
  def __init__(self):
    Monitor.__init__(self)
    self.min, self.max = (int(2**31-1),0)
  def tally(self, x):
    Monitor.tally(self, x)
    self.min = min(self.min, x)
    self.max = max(self.max, x)


          

我們模擬的最后一步當然是 運行它。在大多數標準示例中,只運行一次模擬。但對于我的食品雜貨店,我決定通過幾次模擬進行循環,每次對應于某一天的業務。這看來是個好主意,因為有些統計信息會隨每天的情況而有相當大的不同(因為到達的顧客人次以及所購商品數采用隨機產生的不同值)。
清單 6. 每天運行模擬

            
for run in range(RUNS):
  waittime = Monitor2()
  checkouttime = Monitor2()
  checkout_aisle = Simulation.Resource(AISLES)
  Simulation.initialize()
  cf = Customer_Factory()
  Simulation.activate(cf, cf.run(), 0.0)
  Simulation.simulate(until=CLOSING)
  #print "Customers:", checkouttime.count()
  print "Waiting time average: %.1f" % waittime.mean(), \
     "(std dev %.1f, maximum %.1f)" % (sqrt(waittime.var()),waittime.max)
  #print "Checkout time average: %1f" % checkouttime.mean(), \
  #   "(standard deviation %.1f)" % sqrt(checkouttime.var())
print 'AISLES:', AISLES, ' ITEM TIME:', ITEMTIME


          


三人不歡:一些結果(以及它們意味著什么)

當我最初考慮食品雜貨店模型時,我認為模擬可以解答幾個直接問題。例如,我想象店主可能會選擇購買改進的掃描儀(減少 ITEMTIME ),或者選擇雇傭更多職員(增加 AISLES )。我想只要在每個方案下運行這個模擬(假設雇員和技術成本給定的情況下),并確定上面兩種選擇哪種更能減少成本。

只有運行了模擬后,我才意識到可能會出現比預料的更有趣的事情。查看收集的所有數據,我意識到我不知道要嘗試優化的是什么。 什么。例如,減少 平均付款時間和減少 最差情況的時間,哪個更重要?哪些方面會提高總體顧客滿意度?另外,如何比較顧客在付款之前所用的等待時間以及掃描所購商品所花的時間?以我個人的經驗,我會在等待的隊列中感到不耐煩,但在掃描我的商品時,我不會感到很麻煩(即使這會花一些時間)。

當然,我沒有經營食品雜貨店,所以我不知道所有這些問題的答案。但這個模擬確實讓我準確地決定什么是折衷方案;而且它很簡單,足以稍作調整就可適用于許多行為(包括那些還未顯式地參數化的行為 ― 例如,“一整天中顧客 真的會一直不斷地來嗎?”)。

我只要演示最后一個示例,就可以說明該模型的價值。我在上面曾寫道復雜系統的行為難以概念化。我認為這里的示例可以證明這一事實。在可用的通道從 6 條減少到 5 條(其它參數不變)時,您認為會出現什么情況?最初我想會 稍微增加最糟糕情況下的付款時間。而事實并非如此:
清單 7. 通道數變化前后運行的兩個樣本

            
% python Market.py
Waiting time average: 0.5 (std dev 0.9, maximum 4.5)
Waiting time average: 0.3 (std dev 0.6, maximum 3.7)
Waiting time average: 0.4 (std dev 0.8, maximum 5.6)
Waiting time average: 0.4 (std dev 0.8, maximum 5.2)
Waiting time average: 0.4 (std dev 0.8, maximum 5.8)
Waiting time average: 0.3 (std dev 0.6, maximum 5.2)
Waiting time average: 0.5 (std dev 1.1, maximum 5.2)
Waiting time average: 0.5 (std dev 1.0, maximum 5.4)
AISLES: 6  ITEM TIME: 0.1
% python Market.py
Waiting time average: 2.1 (std dev 2.3, maximum 9.5)
Waiting time average: 1.8 (std dev 2.3, maximum 10.9)
Waiting time average: 1.3 (std dev 1.7, maximum 7.3)
Waiting time average: 1.7 (std dev 2.1, maximum 9.5)
Waiting time average: 4.2 (std dev 5.6, maximum 21.3)
Waiting time average: 1.6 (std dev 2.6, maximum 12.0)
Waiting time average: 1.3 (std dev 1.6, maximum 7.5)
Waiting time average: 1.5 (std dev 2.1, maximum 11.2)
AISLES: 5  ITEM TIME: 0.1


          

減少一條付款通道不是使平均等待時間增加 1/5 或類似的情況,而是使它增加了大約 4 倍。而且,最不幸的顧客(在這些特定的運行期間)的等待時間從 6 分鐘增加到了 21 分鐘。如果我是經理,我認為了解這個極限情況對顧客滿意度而言是極其重要的。誰會早已知道這一點呢?


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 阳春市| 招远市| 南华县| 宿松县| 南岸区| 丽江市| 斗六市| 阿拉善左旗| 平武县| 淳化县| 平顶山市| 阳谷县| 新密市| 邳州市| 弥渡县| 静乐县| 玉林市| 宁安市| 洛隆县| 龙口市| 大余县| 昌都县| 长武县| 余庆县| 农安县| 柘城县| 越西县| 威宁| 娄烦县| 德保县| 含山县| 五河县| 灌云县| 新巴尔虎右旗| 洞头县| 额尔古纳市| 铜山县| 社会| 渝北区| 北票市| 武穴市|