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

來源:互聯網
上載者:User
在我遇到 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 pythonfrom __future__ import generatorsfrom SimPy import Simulationfrom SimPy.Simulation import hold, request, release, nowfrom SimPy.Monitor import Monitorimport randomfrom math import sqrt

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

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

AISLES = 5     # Number of open aislesITEMTIME = 0.1   # Time to ring up one itemAVGITEMS = 20   # Average number of items purchasedCLOSING = 60*12  # Minutes from store open to store closeAVGCUST = 1500   # Average number of daily customersRUNS = 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.pyWaiting 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.pyWaiting 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 分鐘。如果我是經理,我認為瞭解這個極限情況對顧客滿意度而言是極其重要的。誰會早已知道這一點呢?

  • 相關文章

    聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.