Python: Drunkard's walk - python

I have to write a code to calculate route and length of a drunkard's walk.
Excersise:
A drunkard begins walking aimlessly, starting at a lamp post. At each time step he takes one step at random, either north, east, south, or west. How far will the drunkard be
from the lamp post after N steps?
In order to emulate drunkard's steps we can encode each direction with the number so that when the random variable is equal to 0 the drunkard moves north, if random variable is equal to 1 the drunkard moves east and so on.
Write a program that takes an integer argument N and simulates the motion of a random walker for N steps. After each step, print the location of the random walker, treating the lamp post as the origin (0, 0). Also, print the final squared distance from the origin.
So far I've come up with:
import random
x = 0
y = 0
def randomWalk(N):
for i in range (1, (N)):
a = random.randint
if a == 0:
x = x+0
y = y+1
return (x, y)
print (x, y)
if a == 1:
x = x+1
y = y+0
return (x, y)
print (x, y)
if a == 3:
x = x+0
y = y-1
return (x, y)
print (x, y)
if a == 3:
x = x-1
y = y+0
return (x, y)
print (x, y)
print(randomWalk(input()))
But I get None as output, when I test this code.
I would be thankful for any help with this excersise.

It's a good start.
The main problem is that you are failing to call randint:
a = random.randint
This just turns a into an alias for random.randint. Instead, it should be
a = random.randint(0, 3)
Also, you repeat if a == 3: twice.
Also, setting x and y to zero should be done inside the function, not outside.
Finally, your loop (which, by the way, is one iteration too short) doesn't really function as a loop, since you always return during the first iteration.
P.S. Here is a little parting puzzle for you. Figure out how the following works:
dx, dy = random.choice([(-1, 0), (1, 0), (0, -1), (0, 1)])
x += dx
y += dy

def randomWalk(steps):
x = 0 # Make sure you initialize the position to 0,0 each time the function is called
y = 0
directions = ['N', 'E', 'S', 'W'] # To keep track of directions, you could use strings instead of 0, 1, 2, 3.
for i in range(steps):
a = random.choice(directions) # You can use random.choice to choose a dir
if a == 'N':
y += 1
print('Current position: ({},{})'.format(x,y)) # You can print the position using format
elif a == 'S':
y -= 1
print('Current position: ({},{})'.format(x,y))
elif a == 'E':
x += 1
print('Current position: ({},{})'.format(x,y))
else:
x -= 1
print('Current position: ({},{})'.format(x,y))
Testing
>>> randomWalk(8)
Current position: (0,-1)
Current position: (1,-1)
Current position: (1,0)
Current position: (1,-1)
Current position: (0,-1)
Current position: (-1,-1)
Current position: (-1,0)
Current position: (0,0)

Related

Manhattan distance with impassible tiles - python [duplicate]

I'm trying to understand this Breadth First Search python implementation and I understand most of it shown in my commenting but I don't get this line here:
for dx, dy in [(-1, 0), (0, +1), (+1, 0), (0, -1)]:
a, b = current[0] + dx, current[1] + dy #Start searching in a random direction
if maze.in_maze(a, b) and not maze.is_wall(a, b) and (a, b) not in parent: #Check to see if the coordinates that are searching is inside the wall is not a wall and not inside of parent
parent[(a, b)] = current #?
dist[(a, b)] = dist[current] + 1; #?
queue.append((a, b)) #Add the coordinates to the end of the queue
The whole code can be found here, please feel free to call me out on any commenting error. I'm still new to python so I don't know exactly what every line does but I get a rough idea.
from collections import deque #A double-ended queue, or deque, supports adding and removing elements from either end. Import this from collections
nodes = 0 #Initialise nodes with value 0
def solve(maze, start, end): #Solve function that takes in the maze, start and end points as arguments
global nodes #Declare nodes as a global variable
nodes = 0 #Set nodes value to 0
queue = deque() #Set queue as a double ended queue
parent, dist = dict(), dict() #Set parent and dist
queue.append(start) #Add start point to the queue
parent[start], dist[start] = start, 1
while len(queue): #While there are items in the list
current = queue.popleft() #Set current to the first thing in the queue from the left
nodes += 1 #Increment nodes by 1
if current == end: #If the current place is the end target then solution has been found and we can exit the loop
break #Exit the loop
for dx, dy in [(-1, 0), (0, +1), (+1, 0), (0, -1)]:
a, b = current[0] + dx, current[1] + dy #Start searching in a random direction
if maze.in_maze(a, b) and not maze.is_wall(a, b) and (a, b) not in parent: #Check to see if the coordinates that are searching is inside the wall is not a wall and not inside of parent
parent[(a, b)] = current #Set later
dist[(a, b)] = dist[current] + 1; #set later
queue.append((a, b)) #Add the coordinates to the end of the queue
if end not in parent: #If there is no solution
return [] #Return an empty solution
else: #Otherwise if there is a solution
path = [] #Initialise path as an empty list
while start != end: #While the starting point is not the end point, the solution has not be found so
path.append(end) #Keep appending the end node to the path queue until they meet the condition
end = parent[end] #Set end point to the position it is in the parent dictionary
path.append(start) #Insert the starting point to the end of the queue
path.reverse() #Reverse the path queue since the solution was found back to front
return path #Return the final solution
So, in the above code, after reading, I am assuming, that start and end are represented by coordinates like (x, y).
Also, if you select any coordinate in the "maze", you can only traverse in up, down, left, right directions i.e. if you are on the coordinates (x, y) you can only go to one of the following coordinates:
(x+1, y), (x-1, y), (x, y+1), (x, y-1)
So basically, the for loop is used to select the neighboring coordinates in which you can traverse, one by one.
Then a, b = current[0] + dx, current[1] + dy this line is used to get the absolute coordinates of the neighboring coordinates.
Then we check if the new coordinate exists in the maze or if it is a wall.
If it is in the maze and not a wall and also we have not already traversed through it, we update the parent dict and also update the dist dict (for the distance).
The parent dict stores the parent of the coordinate. So for (x+1, y) the parent will be (x, y) which is current.
parent[(x+1, y)] = (x, y) that means the parent of (x+1, y) is (x, y)
the dist dict stores the distance or non of steps required to reach the new coord.
dist[(x+1, y)] = dist[(x,y)] + 1 that means, distance of new coordinate is equal to distance of parent + 1 new step.
Then we add it to the queue.
parent[(a, b)] = current
is used to store the coordinates (current) of the location from which you came to coordinates (a, b). Oh, and BTW, this comment here is wrong:
for dx, dy in [(-1, 0), (0, +1), (+1, 0), (0, -1)]:
a, b = current[0] + dx, current[1] + dy #Start searching in a random direction
It should be "Search in every direction, one at a time". There is nothing random here.

How to get coordinates of a pixel if I have its RGB?

So, I want to check if there's a pixel in a certain area of my screen with the RGB "0,158,187", if so, I want to get its coordinates so that I can click there.
Here's what I tried:
im = ImageGrab.grab(bbox=(268,118,1150,866))
x = 268
y = 118
while True:
x = x + 5
xy = (x, y)
if im.getpixel(xy) == (0,158,187):
pyautogui.click(x, y)
break
if x >= 1150:
x = 268
y = y + 5
if y >= 866:
sys.exit()
However, I keep getting an error
"IndexError: image index out of range"
even though I constantly reset the X range when it reaches 1150 & end the program if Y reaches 866 since theres no point continuing them.
You are incrementing x before calling getpixel, so when it x become greater or equal than 1150, you first call getpixel, then test x to reset the range.
I think this is more appropriate to do :
while True:
xy = (x, y)
if im.getpixel(xy) == (0,158,187):
pyautogui.click(x, y)
break
x = x + 5
if x >= 1150:
x = 268
y = y + 5
if y >= 866:
sys.exit()

Understanding a python breadth-first-search algorithm

I'm trying to understand this Breadth First Search python implementation and I understand most of it shown in my commenting but I don't get this line here:
for dx, dy in [(-1, 0), (0, +1), (+1, 0), (0, -1)]:
a, b = current[0] + dx, current[1] + dy #Start searching in a random direction
if maze.in_maze(a, b) and not maze.is_wall(a, b) and (a, b) not in parent: #Check to see if the coordinates that are searching is inside the wall is not a wall and not inside of parent
parent[(a, b)] = current #?
dist[(a, b)] = dist[current] + 1; #?
queue.append((a, b)) #Add the coordinates to the end of the queue
The whole code can be found here, please feel free to call me out on any commenting error. I'm still new to python so I don't know exactly what every line does but I get a rough idea.
from collections import deque #A double-ended queue, or deque, supports adding and removing elements from either end. Import this from collections
nodes = 0 #Initialise nodes with value 0
def solve(maze, start, end): #Solve function that takes in the maze, start and end points as arguments
global nodes #Declare nodes as a global variable
nodes = 0 #Set nodes value to 0
queue = deque() #Set queue as a double ended queue
parent, dist = dict(), dict() #Set parent and dist
queue.append(start) #Add start point to the queue
parent[start], dist[start] = start, 1
while len(queue): #While there are items in the list
current = queue.popleft() #Set current to the first thing in the queue from the left
nodes += 1 #Increment nodes by 1
if current == end: #If the current place is the end target then solution has been found and we can exit the loop
break #Exit the loop
for dx, dy in [(-1, 0), (0, +1), (+1, 0), (0, -1)]:
a, b = current[0] + dx, current[1] + dy #Start searching in a random direction
if maze.in_maze(a, b) and not maze.is_wall(a, b) and (a, b) not in parent: #Check to see if the coordinates that are searching is inside the wall is not a wall and not inside of parent
parent[(a, b)] = current #Set later
dist[(a, b)] = dist[current] + 1; #set later
queue.append((a, b)) #Add the coordinates to the end of the queue
if end not in parent: #If there is no solution
return [] #Return an empty solution
else: #Otherwise if there is a solution
path = [] #Initialise path as an empty list
while start != end: #While the starting point is not the end point, the solution has not be found so
path.append(end) #Keep appending the end node to the path queue until they meet the condition
end = parent[end] #Set end point to the position it is in the parent dictionary
path.append(start) #Insert the starting point to the end of the queue
path.reverse() #Reverse the path queue since the solution was found back to front
return path #Return the final solution
So, in the above code, after reading, I am assuming, that start and end are represented by coordinates like (x, y).
Also, if you select any coordinate in the "maze", you can only traverse in up, down, left, right directions i.e. if you are on the coordinates (x, y) you can only go to one of the following coordinates:
(x+1, y), (x-1, y), (x, y+1), (x, y-1)
So basically, the for loop is used to select the neighboring coordinates in which you can traverse, one by one.
Then a, b = current[0] + dx, current[1] + dy this line is used to get the absolute coordinates of the neighboring coordinates.
Then we check if the new coordinate exists in the maze or if it is a wall.
If it is in the maze and not a wall and also we have not already traversed through it, we update the parent dict and also update the dist dict (for the distance).
The parent dict stores the parent of the coordinate. So for (x+1, y) the parent will be (x, y) which is current.
parent[(x+1, y)] = (x, y) that means the parent of (x+1, y) is (x, y)
the dist dict stores the distance or non of steps required to reach the new coord.
dist[(x+1, y)] = dist[(x,y)] + 1 that means, distance of new coordinate is equal to distance of parent + 1 new step.
Then we add it to the queue.
parent[(a, b)] = current
is used to store the coordinates (current) of the location from which you came to coordinates (a, b). Oh, and BTW, this comment here is wrong:
for dx, dy in [(-1, 0), (0, +1), (+1, 0), (0, -1)]:
a, b = current[0] + dx, current[1] + dy #Start searching in a random direction
It should be "Search in every direction, one at a time". There is nothing random here.

Seeking better dynamic programming solution to find algorithm with less complexity

I met this question in a coding interview.
You are in an infinite 2D grid where you can move in any of the 8 directions :
(x,y) to
(x+1, y),
(x - 1, y),
(x, y+1),
(x, y-1),
(x-1, y-1),
(x+1,y+1),
(x-1,y+1),
(x+1,y-1)
You are given a sequence of points you need to cover. Give the minimum number of steps in which you can achieve it. You start from the first point.
Example :
Input : [(0, 0), (1, 1), (1, 2)]
Output : 2
It takes 1 step to move from (0, 0) to (1, 1). It takes one more step to move from (1, 1) to (1, 2).
I was able to come up with a recursive solution with memoization (DP) technic with keeping the list of visited points, but still doesn't seem perfectly optimal. I am still thinking about better solution even after the interview. Can anyone come up with better solution than I did? I need help!
# #param X : list of integers
# #param Y : list of integers
# Points are represented by (X[i], Y[i])
# #return an integer
def coverPoints(self, X, Y):
if len(X) == 1:return 0
def odist(A, B): #to calculate shortest distance between a pair of points
min_d = 0 if abs(A[1]-B[1]) > abs(A[0]-B[0]) else 1
return abs(A[min_d]-B[min_d]) + (abs(A[1-min_d]-B[1-min_d])- abs(A[min_d]-B[min_d]))
D = {}
def rec(curL, last, dist):
if D.get((tuple(curL), dist), False) != False:return D[(tuple(curL),dist)]
if len(curL) == 0:return dist
else:
s = sys.maxsize
for id, i in enumerate(curL):
newL = curL[:id] + curL[id+1:]
s = min(s, rec(newL, id, odist( (X[last], Y[last]), (X[curL[id]], Y[curL[id]]) )))
D[(tuple(curL),dist)] = dist + s
return dist + s
s = rec([i for i in range(len(X))], 0, 0)
return s

How to index a list of points for faster searches of nearby points?

For a list of (x, y) points, I am trying to find the nearby points for each point.
from collections import defaultdict
from math import sqrt
from random import randint
# Generate a list of random (x, y) points
points = [(randint(0, 100), randint(0, 100)) for _ in range(1000)]
def is_nearby(point_a, point_b, max_distance=5):
"""Two points are nearby if their Euclidean distance is less than max_distance"""
distance = sqrt((point_b[0] - point_a[0])**2 + (point_b[1] - point_a[1])**2)
return distance < max_distance
# For each point, find nearby points that are within a radius of 5
nearby_points = defaultdict(list)
for point in points:
for neighbour in points:
if point != neighbour:
if is_nearby(point, neighbour):
nearby_points[point].append(neighbour)
Is there any way I can index points to make the above search faster? I feel there must be some faster way than O(len(points)**2).
Edit: the points in general could be floats, not just ints
this is a version with a fixed grid where each gridpoint holds the number of samples that are there.
the search can then be reduced to just the space around the point in question.
from random import randint
import math
N = 100
N_SAMPLES = 1000
# create the grid
grd = [[0 for _ in range(N)] for __ in range(N)]
# set the number of points at a given gridpoint
for _ in range(N_SAMPLES):
grd[randint(0, 99)][randint(0, 99)] += 1
def find_neighbours(grid, point, distance):
# this will be: (x, y): number of points there
points = {}
for x in range(point[0]-distance, point[0]+distance):
if x < 0 or x > N-1:
continue
for y in range(point[1]-distance, point[1]+distance):
if y < 0 or y > N-1:
continue
dst = math.hypot(point[0]-x, point[1]-y)
if dst > distance:
continue
if grd[x][y] > 0:
points[(x, y)] = grd[x][y]
return points
print(find_neighbours(grid=grd, point=(45, 36), distance=5))
# -> {(44, 37): 1, (45, 33): 1, ...}
# meadning: there is one neighbour at (44, 37) etc...
for further optimzation: the tests for x and y could be precalculated for a given gridsize - the math.hypot(point[0]-x, point[1]-y) would not have to be done then for every point.
and it may be a good idea to replace the grid with a numpy array.
UPDATE
if your points are floats you can still create an int grid to reduce the search space:
from random import uniform
from collections import defaultdict
import math
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
#property
def x_int(self):
return int(self.x)
#property
def y_int(self):
return int(self.y)
def __str__(self):
fmt = '''{0.__class__.__name__}(x={0.x:5.2f}, y={0.y:5.2f})'''
return fmt.format(self)
N = 100
MIN = 0
MAX = N-1
N_SAMPLES = 1000
# create the grid
grd = [[[] for _ in range(N)] for __ in range(N)]
# set the number of points at a given gridpoint
for _ in range(N_SAMPLES):
p = Point(x=uniform(MIN, MAX), y=uniform(MIN, MAX))
grd[p.x_int][p.y_int].append(p)
def find_neighbours(grid, point, distance):
# this will be: (x_int, y_int): list of points
points = defaultdict(list)
# need to cast a slightly bigger net on the upper end of the range;
# int() rounds down
for x in range(point[0]-distance, point[0]+distance+1):
if x < 0 or x > N-1:
continue
for y in range(point[1]-distance, point[1]+distance+1):
if y < 0 or y > N-1:
continue
dst = math.hypot(point[0]-x, point[1]-y)
if dst > distance + 1: # account for rounding... is +1 enough?
continue
for pt in grd[x][y]:
if math.hypot(pt.x-x, pt.y-y) <= distance:
points[(x, y)].append(pt)
return points
res = find_neighbours(grid=grd, point=(45, 36), distance=5)
for int_point, points in res.items():
print(int_point)
for point in points:
print(' ', point)
the output looks something like this:
(44, 36)
Point(x=44.03, y=36.93)
(41, 36)
Point(x=41.91, y=36.55)
Point(x=41.73, y=36.53)
Point(x=41.56, y=36.88)
...
for convenience Points is now a class. may not be necessary though...
depending on how dense or sparse your points are you could also represent the grid as a dictionary pointing to a list or Points...
also the find_neighbours function accepts a starting point consisting of ints only in that version. this might also be refined.
and there is much room for improvement: the range of the y axis can be restricted using trigonometry. and for the points way inside the circle there is no need for an individual check; detailed checking only needs to be done close to the outer rim of the circle.

Categories

Resources