การจำลองมอนติคาร์โลในอาร์


11

ฉันพยายามที่จะแก้ปัญหาการออกกำลังกายต่อไปนี้ แต่จริง ๆ แล้วฉันไม่มีเงื่อนงำเกี่ยวกับวิธีการเริ่มต้นทำเช่นนี้ ฉันพบรหัสบางอย่างในหนังสือของฉันที่ดูเหมือนว่ามัน แต่เป็นการออกกำลังกายที่แตกต่างอย่างสิ้นเชิงและฉันไม่รู้วิธีการเชื่อมโยงพวกเขากับแต่ละคน ฉันจะเริ่มเลียนแบบการมาถึงได้อย่างไรและฉันจะรู้ได้อย่างไรเมื่อพวกเขาเสร็จสิ้นแล้ว ฉันรู้วิธีจัดเก็บและคำนวณ a, b, c, d ตามนั้น แต่ฉันไม่รู้ว่าจริง ๆ แล้วฉันต้องการจำลอง monte carlo Simulation อย่างไร ใครช่วยกรุณาเริ่มต้นได้บ้าง ฉันรู้ว่านี่ไม่ใช่สถานที่สำหรับตอบคำถามของคุณ แต่ได้รับการแก้ไขแทน แต่ปัญหาคือฉันไม่รู้วิธีเริ่มต้น

แผนกช่วยเหลือด้านไอทีแสดงถึงระบบเข้าคิวด้วยผู้ช่วยห้าคนที่รับสายจากลูกค้า การโทรเกิดขึ้นตามกระบวนการปัวซงโดยมีอัตราเฉลี่ยของการโทรหนึ่งครั้งทุก 45 วินาที เวลาบริการสำหรับผู้ช่วยที่ 1, 2, 3, 4 และ 5 คือตัวแปรสุ่มเอ็กซ์โพเนนเชียลทั้งหมดที่มีพารามิเตอร์λ1 = 0.1, λ2 = 0.2, λ3 = 0.3, λ4 = 0.4, และλ5 = 0.5 นาที − 1 ตามลำดับ ( ผู้ช่วยแผนกช่วยเหลือของ jth มีλk = k / 10 นาที − 1) นอกเหนือจากลูกค้าที่ได้รับความช่วยเหลือแล้วลูกค้าอีกสิบรายสามารถพักไว้ได้ ในบางครั้งเมื่อถึงความจุนี้ผู้โทรใหม่จะรับสัญญาณไม่ว่าง ใช้วิธีการ Monte Carlo เพื่อประเมินคุณสมบัติด้านประสิทธิภาพดังต่อไปนี้

(a) สัดส่วนของลูกค้าที่ได้รับสัญญาณไม่ว่าง

(b) เวลาตอบสนองที่คาดหวัง;

(c) เวลารอเฉลี่ย

(d) ส่วนของลูกค้าที่ให้บริการโดยผู้ช่วยฝ่ายให้ความช่วยเหลือแต่ละราย;

แก้ไข: สิ่งที่ฉันมีจนถึงตอนนี้คือ (ไม่มาก):

pa = 1/45sec-1

jobs = rep(1,5); onHold = rep(1,10);

jobsIndex = 0;

onHoldIndex = 0;

u = runif(1)
for (i in 1:1000) {

    if(u  <= pa){ # new arrival

        if(jobsIndex < 5) # assistant is free, #give job to assistant

            jobsIndex++;

        else #add to onHold array

            onHoldIndex++;
    }
}

มันไม่เกี่ยวกับ "วิธีทำ MC" แต่คุณคุ้นเคยกับแพคเกจนี้หรือไม่: r-bloggers.com/… ? ดูเหมือนว่าจะเหมาะอย่างยิ่งสำหรับประเภทของปัญหาที่คุณอธิบาย (แม้ว่าการใช้รูปแบบที่แตกต่างกัน)
ทิม

ฉันพยายามแก้ปัญหานี้โดยไม่ใช้ไลบรารีภายนอก แต่ถ้าฉันทำไม่ได้ฉันจะใช้ของคุณอย่างแน่นอน :)
user3485470

แสดงสิ่งที่คุณทำไปแล้ว คุณไม่สามารถมาที่นี่เพื่อขอคำปรึกษาเรื่องงานบ้านได้
Aksakal

คำตอบ:


22

นี่เป็นหนึ่งในการจำลองที่ให้คำแนะนำและสนุกสนานที่สุด:คุณสร้างตัวแทนอิสระในคอมพิวเตอร์ให้พวกเขามีปฏิสัมพันธ์โต้ตอบติดตามสิ่งที่พวกเขาทำและศึกษาสิ่งที่เกิดขึ้น มันเป็นวิธีที่ยอดเยี่ยมในการเรียนรู้เกี่ยวกับระบบที่ซับซ้อนโดยเฉพาะอย่างยิ่ง (แต่ไม่ จำกัด เพียง) ระบบที่ไม่สามารถเข้าใจได้ด้วยการวิเคราะห์ทางคณิตศาสตร์อย่างหมดจด

วิธีที่ดีที่สุดในการสร้างแบบจำลองดังกล่าวคือด้วยการออกแบบจากบนลงล่าง

ในระดับสูงสุดรหัสควรมีลักษณะเหมือน

initialize(...)
while (process(get.next.event())) {}

(นี่และตัวอย่างที่ตามมาทั้งหมดเป็นรหัสที่ปฏิบัติการ Rได้ไม่ใช่แค่รหัสหลอก) การวนซ้ำเป็นการจำลองเหตุการณ์ที่ขับเคลื่อนด้วย : get.next.event()ค้นหา "เหตุการณ์" ที่น่าสนใจและส่งคำอธิบายของมันไปยังprocessซึ่งทำอะไรกับมัน (รวมถึงการบันทึกใด ๆ ข้อมูลเกี่ยวกับมัน) มันกลับมาตราบTRUEใดที่สิ่งต่าง ๆ ทำงานได้ดี ในการระบุข้อผิดพลาดหรือจุดสิ้นสุดของการจำลองมันจะส่งกลับFALSE, สิ้นสุดการวนซ้ำ

หากเรานึกภาพการใช้คิวนี้เช่นคนที่รอใบขับขี่ในนิวยอร์กซิตี้หรือขอใบขับขี่หรือตั๋วรถไฟเกือบทุกที่เรานึกถึงตัวแทนสองประเภท: ลูกค้าและ "ผู้ช่วย" (หรือเซิร์ฟเวอร์) . ลูกค้าประกาศตัวเองโดยแสดง; ผู้ช่วยประกาศความพร้อมของพวกเขาโดยการเปิดไฟหรือลงนามหรือเปิดหน้าต่าง นี่เป็นเหตุการณ์สองประเภทที่ต้องดำเนินการ

สภาพแวดล้อมในอุดมคติสำหรับการจำลองนั้นเป็นสภาพแวดล้อมจริงที่วัตถุไม่แน่นอนซึ่งสามารถเปลี่ยนสถานะเพื่อตอบสนองต่อสิ่งต่าง ๆ รอบตัวได้อย่างอิสระ Rมันแย่มากสำหรับเรื่องนี้ (แม้แต่ Fortran ก็ยังดีกว่า!) อย่างไรก็ตามเรายังสามารถใช้งานได้หากเราใช้ความระมัดระวัง เคล็ดลับคือการรักษาข้อมูลทั้งหมดในชุดโครงสร้างข้อมูลทั่วไปที่สามารถเข้าถึงได้ (และแก้ไข) โดยขั้นตอนการโต้ตอบที่แยกกันจำนวนมาก ฉันจะรับรองระเบียบการใช้ชื่อตัวแปรใน CAPS ทั้งหมดสำหรับข้อมูลดังกล่าว

ระดับถัดไปprocessของการออกแบบจากบนลงล่างเป็นรหัส มันตอบสนองต่อ descriptor เหตุการณ์เดียวe:

process <- function(e) {
  if (is.null(e)) return(FALSE)
  if (e$type == "Customer") {
    i <- find.assistant(e$time)
    if (is.null(i)) put.on.hold(e$x, e$time) else serve(i, e$x, e$time)
  } else {
    release.hold(e$time)
  }
  return(TRUE)
}

มันจะต้องตอบสนองต่อเหตุการณ์ที่เป็นโมฆะเมื่อget.next.eventไม่มีเหตุการณ์ที่ต้องรายงาน มิฉะนั้นจะprocessใช้ "กฎเกณฑ์ทางธุรกิจ" ของระบบ มันเขียนตัวเองจากคำอธิบายในคำถาม วิธีการทำงานควรมีความคิดเห็นเล็ก ๆ น้อย ๆ ยกเว้นเพื่อชี้ให้เห็นว่าในที่สุดเราจะต้องโค้ดรูทีนย่อยput.on.holdและrelease.hold(การนำคิวการถือครองลูกค้า) และserve(การนำการโต้ตอบระหว่างผู้ช่วยลูกค้าไปใช้)

"กิจกรรม" คืออะไร มันจะต้องมีข้อมูลเกี่ยวกับผู้ที่จะทำหน้าที่, สิ่งที่ชนิดของการกระทำพวกเขาจะพาและเมื่อมันเกิดขึ้น รหัสของฉันจึงใช้รายการที่มีข้อมูลสามประเภทนี้ อย่างไรก็ตามget.next.eventจำเป็นต้องตรวจสอบเวลาเท่านั้น มันมีหน้าที่รับผิดชอบในการรักษาลำดับเหตุการณ์ที่เกิดขึ้นเท่านั้น

  1. เหตุการณ์ใด ๆ ที่สามารถใส่ลงในคิวเมื่อได้รับและ

  2. เหตุการณ์ที่เร็วที่สุดในคิวสามารถดึงและส่งผ่านไปยังผู้โทรได้อย่างง่ายดาย

การดำเนินงานที่ดีที่สุดนี้คิวลำดับความสำคัญจะเป็นกองRแต่ที่มากเกินไปในจุกจิก ทำตามคำแนะนำในการเขียนโปรแกรมศิลปะของนอร์แมน Matloff (ผู้เสนอการจำลองคิวที่มีความยืดหยุ่นมากขึ้น แต่ จำกัด คิว) ฉันได้ใช้กรอบข้อมูลเพื่อจัดกิจกรรมและค้นหาเวลาขั้นต่ำในบันทึกของมัน

get.next.event <- function() {
  if (length(EVENTS$time) <= 0) new.customer()               # Wait for a customer$
  if (length(EVENTS$time) <= 0) return(NULL)                 # Nothing's going on!$
  if (min(EVENTS$time) > next.customer.time()) new.customer()# See text
  i <- which.min(EVENTS$time)
  e <- EVENTS[i, ]; EVENTS <<- EVENTS[-i, ]
  return (e)
}

มีหลายวิธีที่สามารถเข้ารหัสได้ รุ่นสุดท้ายที่แสดงที่นี่สะท้อนให้เห็นถึงทางเลือกที่ฉันทำในการเขียนรหัสว่าprocessปฏิกิริยาตอบสนองต่อเหตุการณ์ "ผู้ช่วย" และวิธีการnew.customerทำงาน: get.next.eventเพียงนำลูกค้าออกจากคิวการพักไว้จากนั้นจึงนั่งรออีกเหตุการณ์หนึ่ง บางครั้งมันจำเป็นต้องมองหาลูกค้าใหม่ในสองวิธี: อันดับแรกเพื่อดูว่ามีใครรออยู่ที่หน้าประตู (ตามเดิม) และที่สองหรือไม่ว่ามีลูกค้าเข้ามาบ้างหรือไม่เมื่อเราไม่ได้มองหา

ชัดเจนnew.customerและnext.customer.timeเป็นกิจวัตรที่สำคัญดังนั้นเรามาดูแลพวกเขาต่อไป

new.customer <- function() {  
  if (CUSTOMER.COUNT < dim(CUSTOMERS)[2]) {
    CUSTOMER.COUNT <<- CUSTOMER.COUNT + 1
    insert.event(CUSTOMER.COUNT, "Customer", 
                 CUSTOMERS["Arrived", CUSTOMER.COUNT])
  }
  return(CUSTOMER.COUNT)
}
next.customer.time <- function() {
  if (CUSTOMER.COUNT < dim(CUSTOMERS)[2]) {
    x <- CUSTOMERS["Arrived", CUSTOMER.COUNT]
  } else {x <- Inf}
  return(x) # Time when the next customer will arrive
}

CUSTOMERSคืออาร์เรย์ 2 มิติที่มีข้อมูลสำหรับลูกค้าแต่ละรายในคอลัมน์ มันมีสี่แถว (ทำหน้าที่เป็นสาขา) ที่อธิบายลูกค้าและบันทึกประสบการณ์ของพวกเขาในระหว่างการจำลอง : "ถึงแล้ว", "เสิร์ฟ", "ระยะเวลา" และ "ผู้ช่วย" (ตัวระบุตัวเลขที่เป็นบวกของผู้ช่วยถ้ามี และ-1สำหรับสัญญาณไม่ว่าง) ในการจำลองที่มีความยืดหยุ่นสูงคอลัมน์เหล่านี้จะถูกสร้างขึ้นแบบไดนามิก แต่เนื่องจากวิธีRการทำงานมันสะดวกที่จะสร้างลูกค้าทั้งหมดในตอนแรกในเมทริกซ์ขนาดใหญ่เพียงครั้งเดียวโดยมีเวลามาถึงแล้วโดยการสุ่ม next.customer.timeสามารถดูที่คอลัมน์ถัดไปของเมทริกซ์นี้เพื่อดูว่าใครกำลังมา ตัวแปรทั่วโลกCUSTOMER.COUNTหมายถึงลูกค้าคนสุดท้ายที่จะมาถึง ลูกค้าได้รับการจัดการอย่างง่ายดายผ่านตัวชี้นี้ซึ่งจะทำให้ลูกค้าใหม่ได้รับและมองไปไกลกว่านั้นโดยไม่ต้องมองหาลูกค้ารายต่อไป

serve ใช้กฎทางธุรกิจในการจำลอง

serve <- function(i, x, time.now) {
  #
  # Serve customer `x` with assistant `i`.
  #
  a <- ASSISTANTS[i, ]
  r <- rexp(1, a$rate)                       # Simulate the duration of service
  r <- round(r, 2)                           # (Make simple numbers)
  ASSISTANTS[i, ]$available <<- time.now + r # Update availability
  #
  # Log this successful service event for later analysis.
  #
  CUSTOMERS["Assistant", x] <<- i
  CUSTOMERS["Served", x] <<- time.now
  CUSTOMERS["Duration", x] <<- r
  #
  # Queue the moment the assistant becomes free, so they can check for
  # any customers on hold.
  #
  insert.event(i, "Assistant", time.now + r)
  if (VERBOSE) cat(time.now, ": Assistant", i, "is now serving customer", 
                   x, "until", time.now + r, "\n")
  return (TRUE)
}

ตรงไปตรงมานี้ ASSISTANTSคือ dataframe ที่มีสองฟิลด์: capabilities(ระบุอัตราการให้บริการ) และavailableซึ่งจะตั้งค่าสถานะในครั้งถัดไปที่ผู้ช่วยจะว่าง ลูกค้าจะได้รับบริการโดยการสร้างระยะเวลาการให้บริการแบบสุ่มตามความสามารถของผู้ช่วยอัปเดตเวลาที่ผู้ช่วยคนต่อไปพร้อมใช้งานและบันทึกช่วงเวลาบริการในCUSTOMERSโครงสร้างข้อมูล การVERBOSEตั้งค่าสถานะมีประโยชน์สำหรับการทดสอบและการดีบัก: เมื่อเป็นจริงจะปล่อยกระแสประโยคภาษาอังกฤษที่อธิบายจุดการประมวลผลที่สำคัญ

วิธีการกำหนดผู้ช่วยให้กับลูกค้ามีความสำคัญและน่าสนใจ เราสามารถจินตนาการได้ว่ามีหลายขั้นตอน: การมอบหมายโดยการสุ่มโดยการสั่งซื้อคงที่หรือตามผู้ที่ได้รับเวลาฟรี (หรือสั้นที่สุด) ที่ยาวที่สุด หลายสิ่งเหล่านี้แสดงอยู่ในรหัสที่แสดงความคิดเห็น:

find.assistant <- function(time.now) {
  j <- which(ASSISTANTS$available <= time.now)
  #if (length(j) > 0) {
  #  i <- j[ceiling(runif(1) * length(j))]
  #} else i <- NULL                                    # Random selection
  #if (length(j) > 0) i <- j[1] else i <- NULL         # Pick first assistant
  #if (length(j) > 0) i <- j[length(j)] else i <- NULL # Pick last assistant
  if (length(j) > 0) {
    i <- j[which.min(ASSISTANTS[j, ]$available)]
  } else i <- NULL                                     # Pick most-rested assistant
  return (i)
}

ส่วนที่เหลือของการจำลองเป็นเพียงการออกกำลังกายตามปกติในการโน้มน้าวใจRให้ใช้โครงสร้างข้อมูลมาตรฐานส่วนใหญ่จะเป็นบัฟเฟอร์แบบวงกลมสำหรับคิวที่รอสาย เพราะคุณไม่ต้องการที่จะทำงานอาละวาดกับ Globals simผมใส่สิ่งเหล่านี้เป็นขั้นตอนเดียว ข้อโต้แย้งอธิบายถึงปัญหา: จำนวนลูกค้าที่จะจำลอง ( n.events), อัตราการมาถึงของลูกค้า, ความสามารถของผู้ช่วยและขนาดของคิวการพัก (ซึ่งสามารถกำหนดให้เป็นศูนย์เพื่อกำจัดคิวทั้งหมด)

r <- sim(n.events=250, arrival.rate=60/45, capabilities=1:5/10, hold.queue.size=10)

ส่งคืนรายการโครงสร้างข้อมูลที่เก็บรักษาไว้ในระหว่างการจำลอง หนึ่งในสิ่งที่น่าสนใจที่สุดคือCUSTOMERSอาร์เรย์ Rทำให้ค่อนข้างง่ายในการพล็อตข้อมูลสำคัญในอาเรย์นี้ในวิธีที่น่าสนใจ นี่คือผลลัพธ์หนึ่งรายการที่แสดงลูกค้ารายสุดท้ายในการจำลองลูกค้าอีกราย25050250

รูปที่ 1

ประสบการณ์ของลูกค้าแต่ละคนถูกพล็อตเป็นเส้นเวลาในแนวนอนโดยมีสัญลักษณ์วงกลมในเวลาที่มาถึงเส้นทึบสีดำสำหรับรอสายและเส้นสีในช่วงระยะเวลาของการโต้ตอบกับผู้ช่วย (สีและชนิดของเส้น แยกความแตกต่างระหว่างผู้ช่วย) ใต้พล็อตลูกค้านี้เป็นหนึ่งในการแสดงประสบการณ์ของผู้ช่วยทำเครื่องหมายเวลาที่พวกเขาและไม่ได้มีส่วนร่วมกับลูกค้า จุดสิ้นสุดของแต่ละช่วงเวลาของกิจกรรมถูกคั่นด้วยแถบแนวตั้ง

เมื่อรันด้วยverbose=TRUEเอาต์พุตข้อความของการจำลองจะมีลักษณะดังนี้:

...
160.71 : Customer 211 put on hold at position 1 
161.88 : Customer 212 put on hold at position 2 
161.91 : Assistant 3 is now serving customer 213 until 163.24 
161.91 : Customer 211 put on hold at position 2 
162.68 : Assistant 4 is now serving customer 212 until 164.79 
162.71 : Assistant 5 is now serving customer 211 until 162.9 
163.51 : Assistant 5 is now serving customer 214 until 164.05 
...

(ตัวเลขที่ด้านซ้ายเป็นครั้งแต่ละข้อความถูกปล่อยออกมา.) คุณสามารถจับคู่คำอธิบายเหล่านี้บางส่วนของลูกค้าที่พล็อตครั้งนอนอยู่ระหว่างและ165165160165

เราสามารถศึกษาประสบการณ์การพักของลูกค้าโดยการวางแผนระยะเวลาการพักโดยใช้ตัวระบุลูกค้าโดยใช้สัญลักษณ์พิเศษ (สีแดง) เพื่อแสดงให้ลูกค้าได้รับสัญญาณไม่ว่าง

รูปที่ 2

(แผนการทั้งหมดนี้จะไม่สร้างแดชบอร์ดตามเวลาจริงที่ยอดเยี่ยมสำหรับใครก็ตามที่จัดการคิวบริการนี้!)

simมันเป็นที่น่าสนใจเพื่อเปรียบเทียบแปลงและสถิติที่คุณได้รับเมื่อแตกต่างกันพารามิเตอร์ที่ส่งผ่านไปยัง จะเกิดอะไรขึ้นเมื่อลูกค้ามาถึงเร็วเกินกว่าที่จะประมวลผล จะเกิดอะไรขึ้นเมื่อคิวการพักถูกทำให้เล็กลงหรือถูกกำจัด? การเปลี่ยนแปลงเมื่อผู้ช่วยจะถูกเลือกในลักษณะที่แตกต่างกันอย่างไร ตัวเลขและความสามารถของผู้ช่วยมีอิทธิพลต่อประสบการณ์ของลูกค้าอย่างไร อะไรคือจุดสำคัญที่ลูกค้าบางคนเริ่มหันเหความสนใจหรือเริ่มถูกพักไว้เป็นเวลานาน?


โดยปกติสำหรับคำถามที่ศึกษาด้วยตนเองอย่างชัดเจนเช่นนี้เราจะหยุดที่นี่และปล่อยให้รายละเอียดที่เหลือเป็นแบบฝึกหัด อย่างไรก็ตามฉันไม่ต้องการผิดหวังผู้อ่านที่อาจได้รับสิ่งนี้มาไกลและมีความสนใจที่จะลองใช้ด้วยตัวเอง (และอาจแก้ไขและสร้างมันเพื่อวัตถุประสงค์อื่น ๆ ) ดังนั้นสิ่งที่แนบมาด้านล่างนี้คือรหัสการทำงานที่สมบูรณ์

TEX$

sim <- function(n.events, verbose=FALSE, ...) {
  #
  # Simulate service for `n.events` customers.
  #
  # Variables global to this simulation (but local to the function):
  #
  VERBOSE <- verbose         # When TRUE, issues informative message
  ASSISTANTS <- list()       # List of assistant data structures
  CUSTOMERS <- numeric(0)    # Array of customers that arrived
  CUSTOMER.COUNT <- 0        # Number of customers processed
  EVENTS <- list()           # Dynamic event queue   
  HOLD <- list()             # Customer on-hold queue
  #............................................................................#
  #
  # Start.
  #
  initialize <- function(arrival.rate, capabilities, hold.queue.size) {
    #
    # Create common data structures.
    #
    ASSISTANTS <<- data.frame(rate=capabilities,     # Service rate
                              available=0            # Next available time
    )
    CUSTOMERS <<- matrix(NA, nrow=4, ncol=n.events, 
                         dimnames=list(c("Arrived",  # Time arrived
                                         "Served",   # Time served
                                         "Duration", # Duration of service
                                         "Assistant" # Assistant id
                         )))
    EVENTS <<- data.frame(x=integer(0),              # Assistant or customer id
                          type=character(0),         # Assistant or customer
                          time=numeric(0)            # Start of event
    )
    HOLD <<- list(first=1,                           # Index of first in queue
                  last=1,                            # Next available slot
                  customers=rep(NA, hold.queue.size+1))
    #
    # Generate all customer arrival times in advance.
    #
    CUSTOMERS["Arrived", ] <<- cumsum(round(rexp(n.events, arrival.rate), 2))
    CUSTOMER.COUNT <<- 0
    if (VERBOSE) cat("Started.\n")
    return(TRUE)
  }
  #............................................................................#
  #
  # Dispatching.
  #
  # Argument `e` represents an event, consisting of an assistant/customer 
  # identifier `x`, an event type `type`, and its time of occurrence `time`.
  #
  # Depending on the event, a customer is either served or an attempt is made
  # to put them on hold.
  #
  # Returns TRUE until no more events occur.
  #
  process <- function(e) {
    if (is.null(e)) return(FALSE)
    if (e$type == "Customer") {
      i <- find.assistant(e$time)
      if (is.null(i)) put.on.hold(e$x, e$time) else serve(i, e$x, e$time)
    } else {
      release.hold(e$time)
    }
    return(TRUE)
  }#$
  #............................................................................#
  #
  # Event queuing.
  #
  get.next.event <- function() {
    if (length(EVENTS$time) <= 0) new.customer()
    if (length(EVENTS$time) <= 0) return(NULL)
    if (min(EVENTS$time) > next.customer.time()) new.customer()
    i <- which.min(EVENTS$time)
    e <- EVENTS[i, ]; EVENTS <<- EVENTS[-i, ]
    return (e)
  }
  insert.event <- function(x, type, time.occurs) {
    EVENTS <<- rbind(EVENTS, data.frame(x=x, type=type, time=time.occurs))
    return (NULL)
  }
  # 
  # Customer arrivals (called by `get.next.event`).
  #
  # Updates the customers pointer `CUSTOMER.COUNT` and returns the customer
  # it newly points to.
  #
  new.customer <- function() {  
    if (CUSTOMER.COUNT < dim(CUSTOMERS)[2]) {
      CUSTOMER.COUNT <<- CUSTOMER.COUNT + 1
      insert.event(CUSTOMER.COUNT, "Customer", 
                   CUSTOMERS["Arrived", CUSTOMER.COUNT])
    }
    return(CUSTOMER.COUNT)
  }
  next.customer.time <- function() {
    if (CUSTOMER.COUNT < dim(CUSTOMERS)[2]) {
      x <- CUSTOMERS["Arrived", CUSTOMER.COUNT]
    } else {x <- Inf}
    return(x) # Time when the next customer will arrive
  }
  #............................................................................#
  #
  # Service.
  #
  find.assistant <- function(time.now) {
    #
    # Select among available assistants.
    #
    j <- which(ASSISTANTS$available <= time.now) 
    #if (length(j) > 0) {
    #  i <- j[ceiling(runif(1) * length(j))]
    #} else i <- NULL                                    # Random selection
    #if (length(j) > 0) i <- j[1] else i <- NULL         # Pick first assistant
    #if (length(j) > 0) i <- j[length(j)] else i <- NULL # Pick last assistant
    if (length(j) > 0) {
      i <- j[which.min(ASSISTANTS[j, ]$available)]
    } else i <- NULL # Pick most-rested assistant
    return (i)
  }#$
  serve <- function(i, x, time.now) {
    #
    # Serve customer `x` with assistant `i`.
    #
    a <- ASSISTANTS[i, ]
    r <- rexp(1, a$rate)                       # Simulate the duration of service
    r <- round(r, 2)                           # (Make simple numbers)
    ASSISTANTS[i, ]$available <<- time.now + r # Update availability
    #
    # Log this successful service event for later analysis.
    #
    CUSTOMERS["Assistant", x] <<- i
    CUSTOMERS["Served", x] <<- time.now
    CUSTOMERS["Duration", x] <<- r
    #
    # Queue the moment the assistant becomes free, so they can check for
    # any customers on hold.
    #
    insert.event(i, "Assistant", time.now + r)
    if (VERBOSE) cat(time.now, ": Assistant", i, "is now serving customer", 
                     x, "until", time.now + r, "\n")
    return (TRUE)
  }
  #............................................................................#
  #
  # The on-hold queue.
  #
  # This is a cicular buffer implemented by an array and two pointers,
  # one to its head and the other to the next available slot.
  #
  put.on.hold <- function(x, time.now) {
    #
    # Try to put customer `x` on hold.
    #
    if (length(HOLD$customers) < 1 || 
          (HOLD$first - HOLD$last %% length(HOLD$customers) == 1)) {
      # Hold queue is full, alas.  Log this occurrence for later analysis.
      CUSTOMERS["Assistant", x] <<- -1 # Busy signal
      if (VERBOSE) cat(time.now, ": Customer", x, "got a busy signal.\n")
      return(FALSE)
    }
    #
    # Add the customer to the hold queue.
    #
    HOLD$customers[HOLD$last] <<- x
    HOLD$last <<- HOLD$last %% length(HOLD$customers) + 1
    if (VERBOSE) cat(time.now, ": Customer", x, "put on hold at position", 
                 (HOLD$last - HOLD$first - 1) %% length(HOLD$customers) + 1, "\n")
    return (TRUE)
  }
  release.hold <- function(time.now) {
    #
    # Pick up the next customer from the hold queue and place them into
    # the event queue.
    #
    if (HOLD$first != HOLD$last) {
      x <- HOLD$customers[HOLD$first]   # Take the first customer
      HOLD$customers[HOLD$first] <<- NA # Update the hold queue
      HOLD$first <<- HOLD$first %% length(HOLD$customers) + 1
      insert.event(x, "Customer", time.now)
    }
  }$
  #............................................................................#
  #
  # Summaries.
  #
  # The CUSTOMERS array contains full information about the customer experiences:
  # when they arrived, when they were served, how long the service took, and
  # which assistant served them.
  #
  summarize <- function() return (list(c=CUSTOMERS, a=ASSISTANTS, e=EVENTS,
                                       h=HOLD))
  #............................................................................#
  #
  # The main event loop.
  #
  initialize(...)
  while (process(get.next.event())) {}
  #
  # Return the results.
  #
  return (summarize())
}
#------------------------------------------------------------------------------#
#
# Specify and run a simulation.
#
set.seed(17)
n.skip <- 200  # Number of initial events to skip in subsequent summaries
system.time({
  r <- sim(n.events=50+n.skip, verbose=TRUE, 
           arrival.rate=60/45, capabilities=1:5/10, hold.queue.size=10)
})
#------------------------------------------------------------------------------#
#
# Post processing.
#
# Skip the initial phase before equilibrium.
#
results <- r$c
ids <- (n.skip+1):(dim(results)[2])
arrived <- results["Arrived", ]
served <- results["Served", ]
duration <- results["Duration", ]
assistant <- results["Assistant", ]
assistant[is.na(assistant)] <- 0   # Was on hold forever
ended <- served + duration
#
# A detailed plot of customer experiences.
#
n.events <- length(ids)
n.assistants <- max(assistant, na.rm=TRUE) 
colors <- rainbow(n.assistants + 2)
assistant.color <- colors[assistant + 2]
x.max <- max(results["Served", ids] + results["Duration", ids], na.rm=TRUE)
x.min <- max(min(results["Arrived", ids], na.rm=TRUE) - 2, 0)
#
# Lay out the graphics.
#
layout(matrix(c(1,1,2,2), 2, 2, byrow=TRUE), heights=c(2,1))
#
# Set up the customers plot.
#
plot(c(x.min, x.max), range(ids), type="n",
     xlab="Time", ylab="Customer Id", main="Customers")
#
# Place points at customer arrival times.
#
points(arrived[ids], ids, pch=21, bg=assistant.color[ids], col="#00000070")
#
# Show wait times on hold.
#
invisible(sapply(ids, function(i) {
  if (!is.na(served[i])) lines(x=c(arrived[i], served[i]), y=c(i,i))
}))
#
# More clearly show customers getting a busy signal.
#
ids.not.served <- ids[is.na(served[ids])]
ids.served <- ids[!is.na(served[ids])]
points(arrived[ids.not.served], ids.not.served, pch=4, cex=1.2)
#
# Show times of service, colored by assistant id.
#
invisible(sapply(ids.served, function(i) {
  lines(x=c(served[i], ended[i]), y=c(i,i), col=assistant.color[i], lty=assistant[i])
}))
#
# Plot the histories of the assistants.
#
plot(c(x.min, x.max), c(1, n.assistants)+c(-1,1)/2, type="n", bty="n",
     xlab="", ylab="Assistant Id", main="Assistants")
abline(h=1:n.assistants, col="#808080", lwd=1)
invisible(sapply(1:(dim(results)[2]), function(i) {
  a <- assistant[i]
  if (a > 0) {
    lines(x=c(served[i], ended[i]), y=c(a, a), lwd=3, col=colors[a+2])
    points(x=c(served[i], ended[i]), y=c(a, a), pch="|", col=colors[a+2])
  }
}))
#
# Plot the customer waiting statistics.
#
par(mfrow=c(1,1))
i <- is.na(served)
plot(served - arrived, xlab="Customer Id", ylab="Minutes",
     main="Service Wait Durations")
lines(served - arrived, col="Gray")
points(which(i), rep(0, sum(i)), pch=16, col="Red")
#
# Summary statistics.
#
mean(!is.na(served)) # Proportion of customers served
table(assistant)

2
+1 น่าทึ่ง! คุณสามารถตอบคำถามทุกข้อด้วยความเข้าใจและความใส่ใจในรายละเอียดในระดับนั้นได้หรือไม่? ฝันแค่ฝัน ...
อเล็กซานเดอร์ Blekh

+1 ฉันจะพูดอะไรได้บ้าง วันนี้ฉันได้เรียนรู้สิ่งที่น่าสนใจมากมาย! กรุณาเพิ่มหนังสือใด ๆ สำหรับการอ่านเพิ่มเติมกรุณา?
mugen

1
@mugen ฉันพูดถึงหนังสือ Matloff ในข้อความ มันอาจจะเหมาะสมสำหรับผู้ที่ยังใหม่กับRผู้ที่ต้องการมุมมองที่แตกต่างกัน ในขณะที่เขียนตัวจำลองเล็ก ๆ นี้ฉันพบว่าตัวเองกำลังคิดมากเกี่ยวกับว่าฉันได้เรียนรู้มากแค่ไหนโดยศึกษาโค้ดใน (ฉบับพิมพ์ครั้งแรก) ของระบบปฏิบัติการ / การออกแบบและการใช้งานข้อความของ Andrew Tanenbaum ฉันยังได้เรียนรู้เกี่ยวกับโครงสร้างข้อมูลที่เป็นประโยชน์เช่นกองจากบทความของ Jon Bentley ใน CACM และหนังสือซีรีส์Programming Pearlsของเขา Tanenbaum และ Bentley เป็นนักเขียนที่ดีที่ทุกคนควรอ่าน
whuber

1
@mugen มีตำราเรียนออนไลน์ฟรีเกี่ยวกับทฤษฎีแถวคอยโดย Moshe ที่นี่ นอกจากนี้ศ. Gallager ของ stochastoc เนื่องกระบวนการหลักสูตรครอบคลุมหัวข้อเหล่านี้บน MIT OCW วิดีโอการบรรยายเป็นเรื่องที่ดีจริง
Aksakal

@whuber คำตอบที่ดี แม้ว่าฉันไม่คิดว่าคุณสามารถทำให้เด็ก ๆ สมัยนี้อ่าน Tanenbaum และ Bentley :)
Aksakal
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.