[PYTHON] Escalator simulation

Introduction

Hello, you who are doing a three-year high school student. This is my first post, and I would like to introduce an escalator simulation. The language is Python3.

About the escalator

Escalator users are roughly divided into two types: those who stop and those who walk. In Japan, the position to stop differs depending on the region, such as eastern Japan on the left and western Japan on the right, and the same phenomenon can be seen overseas. In fact, the position where the escalator stops is determined by the Nash equilibrium in game theory. People who are standing still feel stressed when they are hit by a person walking from behind, and they want to ride on a different side from the next time onwards.

Simulation overview

In this simulation, half of the people who stop and half of the people who walk are generated, and whether the initial position is left or right is randomly set. In addition, we measure the liking of all users for the left and right positions, and determine the next user based on the magnitude of the value. By these operations, the position used by the person who stops and the position used by the person walking are converged on one side of the escalator.

Source code / description

Then, the source code and the explanation for it are described below.

User definition

First, define the classes of the person who stops (= StandHuman) and the person who walks (= WalkHuman). The only difference between the two is whether to stop or walk (= active) and speed (= v, I think speed s is more appropriate physically).

escalator.py


class StandHuman:

    def __init__(self):
        self.active = False
        self.v = 0 #speed
        self.x = 0 #position(left/right)
        self.l_like = 0 #how much a human like to ride on the left
        self.r_like = 0 #how much a human like to ride on the right

class WalkHuman:

    def __init__(self):
        self.active = True
        self.v =1 #speed
        self.x = 0 #position(left/right)
        self.l_like = 0 #how much a human like to ride on the left
        self.r_like = 0 #how much a human like to ride on the right

User generation

Number of people to generate from 0 (= NUMBER_OF_HUMANS) Prepare a list with integer sequences from -1 as elements, shuffle the elements of the list, and then judge whether the elements are odd or even, so that all users can use them. Half of the people stop and half of the people walk while giving randomness in the order of doing.

escalator.py


def human_random():
    odd_or_even = list(range(NUMBER_OF_HUMANS))
    random.shuffle(odd_or_even)
    for i in range(NUMBER_OF_HUMANS):
        if odd_or_even[i] % 2 == 0:
            human = StandHuman()
        else:
            human = WalkHuman()
        human_list.append(human)

Determining the position (left and right) to ride on the escalator

The position to ride on the escalator is determined based on the liking for each of the left and right sides at that time. If the left liking is high, the left is determined, if the right liking is high, the right is determined, and if the left and right liking are the same, the left and right are randomly determined. Since the initial values of the left and right liking are both set to 0, the left and right will be randomly determined at the first use.

escalator.py


def side_decision(human):
    l = human.l_like
    r = human.r_like
    if l > r:
        human.x = 0
    elif l < r:
        human.x = 1
    else:
        random_x = random.random()
        if random_x <= 0.5:
            human.x = 0
        else:
            human.x = 1

Put the user on the escalator

The add_human function only puts the user on the escalator. However, for convenience, the escalator has four rows: (1) left for people who stop, (2) left for people who walk, (3) right for people who stop, and (4) right for people who walk.

escalator.py


def add_human(human):
    if human.active == False:
        if human.x == 0: #if left
            escalator[0][0] = human
        else: #if right
            escalator[2][0] = human
    else:
        if human.x == 0: #if left
            escalator[1][0] = human
        else: #if right
            escalator[3][0] = human

Move the escalator

Move the escalator one step. As an actual program, users It is moved up one step, and in the case of the highest step, it is returned to 0 (initial value).

escalator.py


def shift():
    for i in range(STEPS-1):
        escalator[0][STEPS-i-1] = escalator[0][STEPS-i-2]
        escalator[1][STEPS-i-1] = escalator[1][STEPS-i-2]
        escalator[2][STEPS-i-1] = escalator[2][STEPS-i-2]
        escalator[3][STEPS-i-1] = escalator[3][STEPS-i-2]
    escalator[0][0] = 0
    escalator[1][0] = 0
    escalator[2][0] = 0
    escalator[3][0] = 0

Collision confirmation / collision processing

The crash function handles collisions. Here, the position (number of stages) at the time of collision affects the stress felt by the user, and the lower the stage, the easier it is to feel stress. In addition, both processes are separated in order to maintain the randomness of stress felt by those who are standing and those who are walking. The stress felt by the user directly leads to a decrease in favorability. The crash_checker function detects a collision and passes the colliding user to the crash function.

escalator.py


def crash(front, front_position, behind):
    x = random.random() * STEPS
    if x <= 1-(front_position + 1)/STEPS: 
        if front.x == 0: #if left
            front.l_like -= x
        else: #if right
            front.r_like -= x
    y = random.random() * STEPS
    if y <= 1-(front_position + 1)/STEPS:
        if front.x == 0: #if left
            behind.l_like -= y
        else: #if right
            behind.r_like -= y
        
def crash_checker():
    for i in range(STEPS): #left
        if escalator[0][i] != 0 and escalator[1][i] != 0:
            crash(escalator[0][i], i, escalator[1][i])
    for i in range(STEPS): #right
        if escalator[2][i] != 0 and escalator[3][i] != 0:
            crash(escalator[2][i], i, escalator[3][i])

Processing only for walking people

Since a walking person needs a "walking" process, a new walk function is provided in addition to the shift function that moves the escalator.

escalator.py


def end_checker_for_walker(): 
    escalator[1][STEPS-1] = 0
    escalator[3][STEPS-1] = 0

def walk():
    for i in range(STEPS):
        l = escalator[1][i]
        r = escalator[3][i]
        if l != 0:
            escalator[1][STEPS-i-1+(l.v)] = escalator[1][STEPS-i-1] 
        if r != 0:
            escalator[3][STEPS-i-1+(r.v)] = escalator[3][STEPS-i-1]

Whole source code

This is all about the source code. The whole source code is as follows.

escalator.py


import random
import pandas as pd

NUMBER_OF_HUMANS = 100
TIME_END = 1000000
STEPS = 100

human_list = list()
escalator = [[0] * STEPS for i in range(4)] #[[sl], [wl], [sr], [wr]]

class StandHuman:

    def __init__(self):
        self.active = False
        self.v = 0 #speed
        self.x = 0 #position(left/right)
        self.l_like = 0 #how much a human like to ride on the left
        self.r_like = 0 #how much a human like to ride on the right

class WalkHuman:

    def __init__(self):
        self.active = True
        self.v =1 #speed
        self.x = 0 #position(left/right)
        self.l_like = 0 #how much a human like to ride on the left
        self.r_like = 0 #how much a human like to ride on the right

def human_random():
    odd_or_even = list(range(NUMBER_OF_HUMANS))
    random.shuffle(odd_or_even)
    for i in range(NUMBER_OF_HUMANS):
        if odd_or_even[i] % 2 == 0:
            human = StandHuman()
        else:
            human = WalkHuman()
        human_list.append(human)

def shift():
    for i in range(STEPS-1):
        escalator[0][STEPS-i-1] = escalator[0][STEPS-i-2]
        escalator[1][STEPS-i-1] = escalator[1][STEPS-i-2]
        escalator[2][STEPS-i-1] = escalator[2][STEPS-i-2]
        escalator[3][STEPS-i-1] = escalator[3][STEPS-i-2]
    escalator[0][0] = 0
    escalator[1][0] = 0
    escalator[2][0] = 0
    escalator[3][0] = 0

def crash(front, front_position, behind):
    x = random.random() * STEPS
    if x <= 1-(front_position + 1)/STEPS: 
        if front.x == 0: #if left
            front.l_like -= x
        else: #if right
            front.r_like -= x
    y = random.random() * STEPS
    if y <= 1-(front_position + 1)/STEPS:
        if front.x == 0: #if left
            behind.l_like -= y
        else: #if right
            behind.r_like -= y
        
def crash_checker():
    for i in range(STEPS): #left
        if escalator[0][i] != 0 and escalator[1][i] != 0:
            crash(escalator[0][i], i, escalator[1][i])
    for i in range(STEPS): #right
        if escalator[2][i] != 0 and escalator[3][i] != 0:
            crash(escalator[2][i], i, escalator[3][i])

def walk():
    for i in range(STEPS):
        l = escalator[1][i]
        r = escalator[3][i]
        if l != 0:
            escalator[1][STEPS-i-1+(l.v)] = escalator[1][STEPS-i-1] 
        if r != 0:
            escalator[3][STEPS-i-1+(r.v)] = escalator[3][STEPS-i-1]

def side_decision(human):
    l = human.l_like
    r = human.r_like
    if l > r:
        human.x = 0
    elif l < r:
        human.x = 1
    else:
        random_x = random.random()
        if random_x <= 0.5:
            human.x = 0
        else:
            human.x = 1

def add_human(human):
    if human.active == False:
        if human.x == 0: #if left
            escalator[0][0] = human
        else: #if right
            escalator[2][0] = human
    else:
        if human.x == 0: #if left
            escalator[1][0] = human
        else: #if right
            escalator[3][0] = human
def main():
    for i in range(TIME_END):
        shift() 
        crash_checker()
        walk() 
        crash_checker()
        h = human_list[i % NUMBER_OF_HUMANS]
        side_decision(h)
        add_human(h) 

human_random()
main()

simulation result

This time, the number of trials was set to 1 million, the number of users was set to 100 (standing: 50, steps: 50), and the number of escalator stages was set to 100. Looking at the results below, I get the impression that they have converged well.

Escalator results

Below is a simple visualization of the escalator when the specified number of trials is reached. We also output the total number of people standing and walking in the left and right columns and the ratio of both. (S: People who are standing still, w: People who are walking, 0: Free)

| s 0 | 0 0 |
| 0 0 | 0 w |
| s 0 | 0 w |
| s 0 | 0 w |
| s 0 | 0 w |
| s 0 | 0 0 |
| 0 0 | 0 0 |
| s 0 | 0 0 |
| s 0 | 0 0 |
| s 0 | 0 0 |
| 0 0 | 0 0 |
| 0 0 | 0 0 |
| 0 0 | 0 0 |
| 0 0 | 0 0 |
| 0 0 | 0 w |
| 0 0 | 0 w |
| s 0 | 0 w |
| 0 0 | 0 w |
| 0 0 | 0 0 |
| s 0 | 0 0 |
| 0 0 | 0 0 |
| s 0 | 0 0 |
| 0 0 | 0 0 |
| 0 0 | 0 0 |
| s 0 | 0 0 |
| 0 0 | 0 0 |
| s 0 | 0 0 |
| 0 0 | 0 0 |
| 0 0 | 0 0 |
| s 0 | 0 0 |
| 0 0 | 0 0 |
| s 0 | 0 0 |
| s 0 | 0 0 |
| s 0 | 0 0 |
| 0 0 | 0 0 |
| 0 0 | 0 0 |
| 0 0 | 0 0 |
| s 0 | 0 w |
| s 0 | 0 w |
| s 0 | 0 w |
| s 0 | 0 w |
| s 0 | 0 0 |
| s 0 | 0 0 |
| s 0 | 0 0 |
| s 0 | 0 w |
| s 0 | 0 w |
| s 0 | 0 w |
| 0 0 | 0 0 |
| s 0 | 0 0 |
| 0 0 | 0 0 |
| 0 0 | 0 w |
| s 0 | 0 0 |
| s 0 | 0 0 |
| s 0 | 0 w |
| 0 0 | 0 0 |
| 0 0 | 0 0 |
| s 0 | 0 0 |
| 0 0 | 0 w |
| 0 0 | 0 w |
| s 0 | 0 0 |
| s 0 | 0 w |
| 0 0 | 0 0 |
| s 0 | 0 0 |
| s 0 | 0 0 |
| 0 0 | 0 w |
| 0 0 | 0 w |
| s 0 | 0 0 |
| 0 0 | 0 0 |
| s 0 | 0 0 |
| s 0 | 0 w |
| 0 0 | 0 0 |
| 0 0 | 0 w |
| s 0 | 0 0 |
| s 0 | 0 w |
| s 0 | 0 0 |
| 0 0 | 0 0 |
| s 0 | 0 0 |
| 0 0 | 0 w |
| 0 w | 0 w |
| 0 0 | 0 w |
| 0 w | 0 0 |
| 0 w | 0 0 |
| s 0 | 0 0 |
| 0 0 | 0 w |
| 0 0 | 0 0 |
| 0 0 | 0 0 |
| s 0 | 0 0 |
| s 0 | 0 w |
| 0 0 | 0 0 |
| 0 w | 0 0 |
| 0 0 | 0 w |
| s 0 | 0 0 |
| 0 0 | 0 0 |
| 0 w | 0 w |
| s 0 | 0 0 |
| 0 0 | 0 0 |
| s 0 | 0 w |
| 0 0 | 0 0 |
| s 0 | 0 0 |
| 0 0 | 0 w |
#----------------------------------------------#
WalkHuman/StandHuman of left:  0.1 W:S= 5 50
WalkHuman/StandHuman of right:  34.0 W:S= 34 0

Comparison of likability (stress)

All users (the first 100 people generated) compared the left and right sides of the escalator in a table (whether they dislike it).

            Left  Right
StandHuman    50      0
WalkHuman      5     45

in conclusion

As a result, I think it was a reasonable simulation. Since this was my first simulation creation, I think there are a lot of loose parts, but I would like to continue to improve as much as possible.

Recommended Posts

Escalator simulation
Bid optimization simulation
Double pendulum simulation
ambisonics simulation python
Python-Particle motion simulation
RC circuit simulation
Boost current simulation