def riffle_shuffle(deck):
n = len(deck)
mid = n//2 # ineteger
part1 = deck[:mid] # take first half
part2 = deck[mid:] # drop first half
s = []
for x, y in zip(part1, part2):
s.extend([x,y])
if len(part1) < len(part2):
s.extend([part2[-1]])
return s
def repeat(func, arg, n):
"""
applies function func to arg , n times
"""
for i in range(n):
arg = func(arg)
print(f"{i:2d}","".join(arg))
return arg
def repeat_count(func, arg):
newarg = arg[:]
count = 1
newarg = func(newarg)
while newarg != arg:
newarg = func(newarg)
count += 1
return count
Module III - Day 3
Python Made Easy: Science and Math Edition
Sep-Dec 2025 batch, Vikrant Patil
Date: 8th Feb 2026
Live note are here https://vikrant.dev/python-made-easy-science-math/students-module3-day1.html
Please login to https://traininghub.vikrant.dev and create a notebook with name module3-day3.ipynb
© Vikrant Patil
Seeing the patterns visually
print("length", "shuffles")
for i in range(1, 53):
print("{i:6d} {s:8d}".format(i=i, s=repeat_count(riffle_shuffle, list(range(i)))))length shuffles
1 1
2 1
3 1
4 2
5 2
6 4
7 4
8 3
9 3
10 6
11 6
12 10
13 10
14 12
15 12
16 4
17 4
18 8
19 8
20 18
21 18
22 6
23 6
24 11
25 11
26 20
27 20
28 18
29 18
30 28
31 28
32 5
33 5
34 10
35 10
36 12
37 12
38 36
39 36
40 12
41 12
42 20
43 20
44 14
45 14
46 12
47 12
48 23
49 23
50 21
51 21
52 8
counts = [repeat_count(riffle_shuffle, list(range(i))) for i in range(1, 401)]len(counts)400
import matplotlib.pyplot as plt--------------------------------------------------------------------------- ModuleNotFoundError Traceback (most recent call last) Cell In[7], line 1 ----> 1 import matplotlib.pyplot as plt ModuleNotFoundError: No module named 'matplotlib'
!pip install matplotlibCollecting matplotlib Using cached matplotlib-3.10.8-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl.metadata (52 kB) Collecting contourpy>=1.0.1 (from matplotlib) Using cached contourpy-1.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.metadata (5.5 kB) Collecting cycler>=0.10 (from matplotlib) Using cached cycler-0.12.1-py3-none-any.whl.metadata (3.8 kB) Collecting fonttools>=4.22.0 (from matplotlib) Using cached fonttools-4.61.1-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl.metadata (114 kB) Collecting kiwisolver>=1.3.1 (from matplotlib) Using cached kiwisolver-1.4.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl.metadata (6.3 kB) Requirement already satisfied: numpy>=1.23 in /home/vikrant/usr/local/default/lib/python3.13/site-packages (from matplotlib) (2.4.2) Requirement already satisfied: packaging>=20.0 in /home/vikrant/usr/local/default/lib/python3.13/site-packages (from matplotlib) (25.0) Collecting pillow>=8 (from matplotlib) Downloading pillow-12.1.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.metadata (8.8 kB) Collecting pyparsing>=3 (from matplotlib) Using cached pyparsing-3.3.2-py3-none-any.whl.metadata (5.8 kB) Requirement already satisfied: python-dateutil>=2.7 in /home/vikrant/usr/local/default/lib/python3.13/site-packages (from matplotlib) (2.9.0.post0) Requirement already satisfied: six>=1.5 in /home/vikrant/usr/local/default/lib/python3.13/site-packages (from python-dateutil>=2.7->matplotlib) (1.17.0) Using cached matplotlib-3.10.8-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl (8.7 MB) Using cached contourpy-1.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (362 kB) Using cached cycler-0.12.1-py3-none-any.whl (8.3 kB) Using cached fonttools-4.61.1-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl (4.9 MB) Using cached kiwisolver-1.4.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl (1.5 MB) Downloading pillow-12.1.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (7.0 MB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 7.0/7.0 MB 12.0 MB/s eta 0:00:0031m15.8 MB/s eta 0:00:01 Using cached pyparsing-3.3.2-py3-none-any.whl (122 kB) Installing collected packages: pyparsing, pillow, kiwisolver, fonttools, cycler, contourpy, matplotlib ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 7/7 [matplotlib] 6/7 [matplotlib]ow] Successfully installed contourpy-1.3.3 cycler-0.12.1 fonttools-4.61.1 kiwisolver-1.4.9 matplotlib-3.10.8 pillow-12.1.1 pyparsing-3.3.2
import matplotlib.pyplot as plt#show images from matplotlib in the same HTML page
%matplotlib inlineplt.plot(counts)
Random Walk
You start from home. You can walk unit distance in one step. In every step you choose the direction randomly. Availables directions are east, west, north, south. So after 500 steps how much distance you would be away from home?
import math
import random
class RandomWalk:
def __init__(self, home=(0,0)):
self.x, self.y = home
self.home = home
def north(self):
self.y += 1
def south(self):
self.y -= 1
def east(self):
self.x += 1
def west(self):
self.x -= 1
def distance(self):
x1, y1 = self.home
x2, y2 = self.x, self.y
return math.sqrt((x1-x2)**2 + (y1-y2)**2)
def reset(self):
self.x, self.y = self.home
def execute_random_step(self):
direction = [self.north, self.south, self.east, self.west]
walk = random.choice(direction)
walk()
class RandomWalkExecuter:
def __init__(self, iterations, walk: RandomWalk): # this is called as typehint it is just for information
self.iterations = iterations
self.walk = walk
def execute(self):
for i in range(self.iterations):
self.walk.execute_random_step()
def get_walk(self):
return self.walk
def simulate(experiments=100):
distances = []
for i in range(experiments):
walk = RandomWalk()
executer = RandomWalkExecuter(500, walk)
executer.execute()
d = executer.get_walk().distance()
distances.append(d)
return distancesdef add_number(a, b=5): # default value of b is 5
return a + badd_number(10)15
add_number(10, 4)14
import randomrandom.choice("hello world")'o'
random.choice("hello world")'l'
random.choice("hello world")'w'
random.choice("hello world")'e'
x = [add_number, 23, 43, 5, 5]x[0]<function __main__.add_number(a, b=5)>
x[0](100)105
x = 10x = [1 , 2, 3, 4]x = add_numberdef add_numbers(a: int, b: int=5):
"""where a in an integer
"""
return a + badd_numbers("a" , "b")'ab'
simulate(100)[21.2602916254693,
11.661903789690601,
4.47213595499958,
16.1245154965971,
28.319604517012593,
21.400934559032695,
16.55294535724685,
25.96150997149434,
18.867962264113206,
28.071337695236398,
14.212670403551895,
9.055385138137417,
5.830951894845301,
21.587033144922902,
31.622776601683793,
26.076809620810597,
35.4400902933387,
23.53720459187964,
13.038404810405298,
13.92838827718412,
22.80350850198276,
15.556349186104045,
10.198039027185569,
32.31098884280702,
28.178005607210743,
26.570660511172846,
7.615773105863909,
26.076809620810597,
17.029386365926403,
10.198039027185569,
29.732137494637012,
6.324555320336759,
15.231546211727817,
37.73592452822641,
15.297058540778355,
25.495097567963924,
11.661903789690601,
4.47213595499958,
7.211102550927978,
22.80350850198276,
26.419689627245813,
40.496913462633174,
5.656854249492381,
11.045361017187261,
30.886890422961002,
3.1622776601683795,
5.656854249492381,
38.47076812334269,
33.015148038438355,
28.319604517012593,
7.0710678118654755,
16.1245154965971,
45.5411901469428,
19.849433241279208,
43.08131845707603,
24.698178070456937,
29.832867780352597,
8.94427190999916,
28.844410203711913,
34.058772731852805,
21.95449840010015,
22.0,
5.0990195135927845,
8.94427190999916,
8.94427190999916,
18.973665961010276,
10.295630140987,
7.0710678118654755,
4.0,
21.95449840010015,
35.4682957019364,
5.656854249492381,
12.083045973594572,
14.7648230602334,
12.806248474865697,
10.770329614269007,
45.60701700396552,
11.661903789690601,
23.40939982143925,
17.029386365926403,
35.4682957019364,
42.37924020083418,
9.486832980505138,
12.727922061357855,
4.0,
17.4928556845359,
8.246211251235321,
22.67156809750927,
15.811388300841896,
23.021728866442675,
2.8284271247461903,
27.018512172212592,
40.0,
3.1622776601683795,
12.0,
17.029386365926403,
13.92838827718412,
6.0,
9.899494936611665,
25.059928172283335]
plt.plot(simulate(100))
import statisticsdistances = simulate(100)statistics.stdev(distances)9.89799976218993
statistics.mean(distances)19.237705286769813
distances1 = simulate(100)statistics.mean(distances1)19.55662881014937
statistics.stdev(distances1)9.039658390682582
ones = [1 for i in range(100)]statistics.stdev(ones)0.0
statistics.mean(ones)1
def simulate(experiments=100, steps=500):
distances = []
for i in range(experiments):
walk = RandomWalk()
executer = RandomWalkExecuter(steps, walk)
executer.execute()
d = executer.get_walk().distance()
distances.append(d)
return distancesresults = [simulate(100, s) for s in range(50, 501, 25)]
len(results)19
plt.plot(results[0]) # 50
plt.plot(results[5])
results = [simulate(100, s) for s in range(50, 501, 25)]means = [statistics.mean(r) for r in results]
stddev = [statistics.stdev(r) for r in results]plt.plot(range(50, 501, 25), means)
plt.plot(range(50, 501, 25), stddev)