e-neko aka e-cat of IRC told me that buses come in pairs for some reason. I did not believe him. I still find it hard to believe. It looks so strange.

Figured made into a mooovie:

https://kaka.farm/pub/images/2025-12-06-buses-simulation/bus.mp4

Simulation code:

https://kaka.farm/pub/images/2025-12-06-buses-simulation/waiting-for-the-bus.scm

Jupyter notebook on:

https://kaka.farm/pub/images/2025-12-06-buses-simulation/bus.ipynb

(import
 (srfi srfi-1)
 (srfi srfi-9)

 (ice-9 match)
 )

(define *bus-capacity* 50)
(define *bus-departure-interval* 100)
(define *bus-route-length* 10000)
(define *bus-speed* 10)
(define *number-of-stations* 100)
(define *station-fill-rate* 1)
(define *time-passenger-on* 1)
(define *time-passenger-off* 1)

(define-record-type <event>
  (make-event time data)
  event?
  (time event-time)
  (data event-data))

(define-record-type <bus-departure>
  (make-bus-departure)
  bus-departure?)

(define-record-type <bus-at-station>
  (make-bus-at-station station-number number-of-passengers)
  bus-at-station?
  (station-number bus-at-station-station-number)
  (number-of-passengers bus-at-station-number-of-passsangers))

(define-record-type <passenger-waiting>
  (make-passenger-waiting station-number)
  passenger-waiting?
  (station-number passenger-waiting-station-number))

(define-record-type <world>
  (make-world events stations)
  world?
  (events world-events)
  (stations world-stations))

(define (initialise-world)
  (make-world '()
              (make-vector *number-of-stations* 0)))

(define (sort-event-queue event-queue)
  (sort event-queue
        (lambda (event-a event-b)
          (< (event-time event-a)
             (event-time event-b)))))

(define (tick world)
  (match (world-events world)
    ['() (make-world (list (make-event 0 (make-bus-departure))
                           (make-event 0 (make-passenger-waiting (random *number-of-stations*))))
                     (world-stations world))]
    [(and events (head . rest))
     (let* ([sorted-event-queue (sort-event-queue events)]
            [earliest-event (car sorted-event-queue)]
            [rest-of-events (cdr sorted-event-queue)])
       (match earliest-event
         [($ <event> time data)
          (match data
            [($ <passenger-waiting> station-number)
             (let ([new-events (list (make-event (+ time (* (random:uniform)
                                                            *station-fill-rate*))
                                                 (make-passenger-waiting (random *number-of-stations*))))]
                   [stations (world-stations world)])
               (vector-set! stations station-number (1+ (vector-ref stations station-number)))
               (make-world (append new-events rest-of-events)
                           stations))]
            [($ <bus-departure>)
             (let ([new-events (list (make-event (+ time *bus-departure-interval*)
                                                 (make-bus-departure))
                                     (make-event (+ time (/ *bus-route-length* *number-of-stations*))
                                                 (make-bus-at-station 1 0)))])
               (make-world (append new-events rest-of-events)
                           (world-stations world)))]
            [($ <bus-at-station> station-number number-of-passengers)
             (format #t "~A,~A~%" time station-number)
             (cond
              [(= station-number *number-of-stations*)
               (make-world rest-of-events (world-stations world))]
              [else
               (let* ([stations (world-stations world)]
                      [waiting-at-station (vector-ref stations station-number)]
                      [passengers-off (round (* number-of-passengers (random:uniform)))]
                      [number-of-passengers-after-off (- number-of-passengers passengers-off)]
                      [free-seats-after-off (- *bus-capacity* number-of-passengers-after-off)]
                      [passengers-on (min free-seats-after-off waiting-at-station)]
                      [number-of-passengers-after-off-on (+ number-of-passengers-after-off passengers-on)]
                      [time-bus-waiting-in-station (+ (* passengers-off *time-passenger-off*)
                                                      (* passengers-on *time-passenger-on*))]
                      [next-time (+ time
                                    time-bus-waiting-in-station
                                    (/ *bus-route-length* *number-of-stations*))]
                      [new-events (list (make-event next-time
                                                    (make-bus-at-station (1+ station-number)
                                                                         number-of-passengers-after-off-on)))])
                 (vector-set! stations station-number (- (vector-ref stations station-number)
                                                         passengers-on))
                 (make-world (append new-events rest-of-events)
                             stations))])]
            [($ <event> time 'bus-final-arrival data)
             (make-world rest-of-events
                         (world-stations world))])]))]))

(let loop ([world (tick (make-world '() (make-vector *number-of-stations* 0)))]
           [n 0])
  (cond
   [(= n 100000)
    '()]
   [else
    (loop (tick world)
          (1+ n))]))

Jupyter notebook source, sorta:

from matplotlib import pyplot as plt
import numpy as np

s = open('data-file.csv', 'r').read()

l = [[float(n.split(',')[0]), int(n.split(',')[1])] for n in s.split("\n")]

def f(station_number):
    last = [n for n in l if n[1] == station_number]
    last = [n_1[0] - n_0[0] for n_0, n_1 in zip(last, last[1:])]
    fig, ax = plt.subplots(1, 1)
    ax.set_title(f'bus station #{station_number:03}')
    ax.set_xlabel('time difference between two consecutive buses')
    ax.set_ylabel('number of busess')
    ax.hist(last, bins=100)
    fig.savefig(f'bus-{station_number:03}.png')

for range(1, 101):
    f(n)
  • 🇮🇱🦬OP
    link
    fedilink
    arrow-up
    8
    ·
    1 个月前

    Holy hell! Thank you! I am too tired to understand anything, but when I wake up and read it I will still be too dumb to understand anything. I will try and read it anyway. Thank you!

    • BartyDeCanter
      link
      fedilink
      arrow-up
      8
      ·
      1 个月前

      Thanks! I suspect this is not too difficult to Monte Carlo model, with different routes, number of busses and delay distribution curves, possible adaptations, and impact on costs.