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.
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.
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.
Then, the source code and the explanation for it are described below.
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
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)
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
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 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
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])
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]
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()
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.
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
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
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.