I have a code that, using the algorithm of rotating calipers, defines two points with the longest distance.
The code takes in the first line the number of points N. And then N times takes the coordinates of the points X, Y. After displays the length of the longest distance.
For example:
INPUT
6
1 1
-1 0
-3 -1
-2 -2
2 3
4 -2
OUTPUT
7.0710678119
INPUT
6
2 2
0 -3
5 7
3 3
2 1
-1 1
OUTPUT
4.47213595499958 #my comment: from (3,3) to (5,7)
But there may be cases when 3 or more points are located on one straight line.
And then how should I act?
from math import *
def rast(x1, x2, y1, y2):
x = x2-x1
y = y2-y1
l = sqrt(pow(fabs(x), 2)+pow(fabs(y), 2));
return l
def orientation(p,q,r):
'''Return positive if p-q-r are clockwise, neg if ccw, zero if colinear.'''
return (q[1]-p[1])*(r[0]-p[0]) - (q[0]-p[0])*(r[1]-p[1])
def hulls(Points):
'''Graham scan to find upper and lower convex hulls of a set of 2d points.'''
U = []
L = []
Points.sort()
for p in Points:
while len(U) > 1 and orientation(U[-2],U[-1],p) <= 0: U.pop()
while len(L) > 1 and orientation(L[-2],L[-1],p) >= 0: L.pop()
U.append(p)
L.append(p)
return U,L
def rotatingCalipers(Points):
'''Given a list of 2d points, finds all ways of sandwiching the points
between two parallel lines that touch one point each, and yields the sequence
of pairs of points touched by each pair of lines.'''
U,L = hulls(Points)
i = 0
j = len(L) - 1
while i < len(U) - 1 or j > 0:
yield U[i],L[j]
# if all the way through one side of hull, advance the other side
if i == len(U) - 1: j -= 1
elif j == 0: i += 1
# still points left on both lists, compare slopes of next hull edges
# being careful to avoid divide-by-zero in slope calculation
elif (U[i+1][1]-U[i][1])*(L[j][0]-L[j-1][0]) > \
(L[j][1]-L[j-1][1])*(U[i+1][0]-U[i][0]):
i += 1
else: j -= 1
def diameter(Points):
'''Given a list of 2d points, returns the pair that's farthest apart.'''
diam,pair = max([((p[0]-q[0])**2 + (p[1]-q[1])**2, (p,q))
for p,q in rotatingCalipers(Points)])
return pair
n=int(input())
dots = []
for i in range(n):
tmp = [int(j) for j in input().split()]
dots.append([tmp[0],tmp[1]])
tmp = diameter(dots)
d1,d2=tmp[0],tmp[1]
print(rast(d1[0],d2[0],d1[1],d2[1]))
Once you have made the convex hull, you will need to sort the lines by the longest. In my example you will have the red line and after this the blue with arrow.
Take the longest line (red) and get its angle. Foreach point in the convex hull, check if the line between S and P is equal to the angle. If so calculate the distance of both lines SP & EP, if one is longer than the blue line, that is the longest line, you can stop. Else, ignore the red line and take the next longest. When there are no equal angles, you can stop.
Related
I'm working on the Island Perimeter Problem from LeetCode.
I tried to use the DFS algorithm, based on LeetCode#200, to solve this problem. For example 1, the output should be 16 but my output was 10. It seemed that not all neighbor cells were counted. Can anyone see any problems with the algorithm below?
class Solution(object):
def islandPerimeter(self, grid):
"""
:type grid: List[List[int]]
:rtype: int
"""
# plus 4 edges for the first cell
cnt = 4
def dfs(grid,i,j,cnt):
# return if the cell is out of the grid or equals to 0
if not 0 <= i < len(grid) or not 0 <= j < len(grid[0]) or grid[i][j] == 0: return
# put counted cell to 0 for avoiding duplicate count
grid[i][j] = 0
# plus 2 edges for each neighbor cell
cnt += 2
# search all neighbor nodes
dfs(grid,i-1,j,cnt)
dfs(grid,i+1,j,cnt)
dfs(grid,i,j-1,cnt)
dfs(grid,i,j+1,cnt)
return cnt
for i in range (len(grid)):
for j in range (len(grid[0])):
# find the first node equals to 1
if grid[i][j] == 1:
cnt += dfs(grid,i,j,cnt)
return cnt
from typing import List
# perimeter means count the edges that touches the water
class Solution:
def islandPerimeter(self,grid:List[List[int]])->int:
ROWS,COLS=len(grid),len(grid[0])
visited=set()
def dfs(r,c):
# base case we are out of bound or we reached water
if r<0 or r==ROWS or c==COLS or c<0 or grid[r][c]==0:
# that means we are on the border. so we return 1
return 1
if (r,c) in visited:
return 0
visited.add((r,c))
return dfs(r+1,c)+dfs(r-1,c)+dfs(r,c+1)+dfs(r,c-1)
# we start from land portion
for r in range(ROWS):
for c in range(COLS):
if grid[r][c]==1:
return dfs(r,c)
You can simplify your code to the following
def island_perimeter(grid):
island_size = sum([i for l in grid for i in l])
perimeter_size = island_size * 2 + 2
# correct for inner tiles
for i in range(len(grid) - 1):
for j in range(len(grid[i]) - 1):
if (grid[i][j] == 1 and grid[i+1][j] == 1
and grid[i][j+1] == 1 and grid[i+1][j+1] == 1):
perimeter_size -= 2
return perimeter_size
Given the problem description in the link you provided, both the island and the surrounding see are connect spaces. There is only one island and there are no lakes.
You can show by induction that an island of size one has a perimeter of four. And by adding a piece of island (size 1) to the some arbitrary but fixed sized island (size n) it will extend the perimeter by 2. As a correction, whenever an island of size 2x2 is created the perimeter size needs to be decreased again by two. Hence the equation given in the function above.
I am given an (nxm) matrix of positive and negative integers.
The goal is to try and convert all negative numbers into positive ones, and if this is possible, return the number of passes of the matrix required to do this, otherwise return -1.
Here are the rules:
You can only convert a negative number into a positive one, if the negative number has an adjacent (directly above, below, left or right) entry that is positive.
If you convert a number from negative to positive in a certain pass, you cannot then use this (recently) turned positive number to turn other negative numbers positive- at least for this pass.
I have written an accepted solution to the problem, but I can't seem to work out the Time Complexity of the solution.
My solution:
Create a Boolean matrix to mark the negative entries that have been turned positive in the current pass (we will reset this matrix after each pass)
Iterate over all entries of the matrix
For every negative number we stumble across check all 4 of its adjacent entries and see if there are any positive entries.
If so, convert it to positive and mark it in the boolean matrix.
Increment number of passes each time we iterate over the whole matrix.
We are done when we iterate over the entire matrix and have not made a single change to it (i.e. conversion from negative to positive).
If there are any negative entries left, return -1, otherwise return the number of passes.
I can't seem to think of a worst case- any suggestions on the time complexity of this solution?
My initial thought are that it is O(n), where n is the size of the matrix.
For reference, here is my solution:
def minimumPassesOfMatrix(matrix):
numberOfPasses = 0
ChangesMade = True
while ChangesMade:
negToPosMarket = [[False for _ in range(len(matrix[0]))] for _ in range(len(matrix))]
ChangesMade = False
for row in range(len(matrix)):
for col in range(len(matrix[0])):
if matrix[row][col] < 0:
positiveAdjacent = checkAdjacentEntriesForPositive(matrix, row, col, negToPosMarket)
if positiveAdjacent:
matrix[row][col] *= -1
negToPosMarket[row][col] = True
ChangesMade = True
if not ChangesMade:
break
numberOfPasses += 1
if all(matrix[row][col] >= 0 for row in range(len(matrix)) for col in range(len(matrix[0]))): #notebook double for loop list comp!
return numberOfPasses
print(matrix)
return -1
def checkAdjacentEntriesForPositive(matrix, row, col, negToPosMarket):
matrixHeight = len(matrix) - 1
matrixWidth = len(matrix[0]) - 1
if not OutOfBounds(row + 1, col, matrixHeight, matrixWidth) and not negToPosMarket[row + 1][col]:
if matrix[row + 1][col] > 0:
return True
if not OutOfBounds(row - 1, col, matrixHeight, matrixWidth) and not negToPosMarket[row - 1][col]:
if matrix[row - 1][col] > 0:
return True
if not OutOfBounds(row, col + 1, matrixHeight, matrixWidth) and not negToPosMarket[row][col + 1]:
if matrix[row][col + 1] > 0:
return True
if not OutOfBounds(row, col - 1, matrixHeight, matrixWidth) and not negToPosMarket[row][col - 1]:
if matrix[row][col - 1] > 0:
return True
return False
def OutOfBounds(row, col, matrixHeight, matrixWidth):
return row < 0 or col < 0 or row > matrixHeight or col > matrixWidth
The worst case scenario would be a positive number at one corner of the matrix with everything else being negative. This will require (n+m-2) passes to flip the opposite corner. The time complexity would be (n+m)xnxm if you go through every position on each pass.
If you use a set of coordinates instead, this can be reduced to nxm
Place the coordinates of positive values in a set. At each pass convert their negative neighbours to positive and place these former negative's coordinates into a new set for the next pass. This way, you only process each item once.
Here's a example in Python:
def makePos(matrix):
m = len(matrix)
n = len(matrix[0])
plusCoords = {(r,c) for r,row in enumerate(matrix)
for c,val in enumerate(row)
if val>0} # initial set of + coordinates
passes = 0
iterations = 0
while plusCoords: # more passes for new + coordinates
passes += 1 # count passes
nextCoords = set() # coordinates of new positives
for r,c in plusCoords: # go through this pass's coords
iterations += 1
for dr,dc in [(-1,0),(0,-1),(0,1),(1,0)]: # neigbors
nr,nc = r+dr, c+dc
if nr not in range(m): continue
if nc not in range(n): continue
if matrix[nr][nc] < 0: # flip to positive
nextCoords.add((nr,nc)) # track flips
for nr,nc in nextCoords:
matrix[nr][nc] *= -1 # update matrix
plusCoords = nextCoords # coords for next pass
return passes - 1, iterations
# passes
M = [ [10,-1,-1,-1,-1], # 0 1 2 3 4
[-1,-1,-1,-1,-1], # 1 2 3 4 5
[-1,-1,-1,-1,-1]] # 2 3 4 5 6
print(*makePos(M)) # 6 15 (6 passes,15 iterations)
Note that the for dr,dc in [(-1,0),(0,-1),(0,1),(1,0)]: loop counts as O(1) here because it does a fixed amount of work for the r,c coordinates. The nextCoords.add((nr,nc)) function is O(1) because nextCoords is a set.
I have NxN chessboard. I have a pawn that can move 1 box up,down,right,left. There is a random exit point. What I want to do is calculate the shortest path from pawn location to exit location.
Pawn is located at random place
Obstacles are located at random place
Exit place is located at random place
I already created adjaceny array for chessboard. I did it with 2D array. For instance, obstacles are 0 where others are 1. It looks like 2D array. R is pawn. O is obstacle. X are ways that robot can go. E is exit point.
E X X O O
X X X X X
X X R O X
X X X X X
X X X X X
Array representation is
1 1 1 0 0
1 1 1 1 1
1 1 1 0 1
1 1 1 1 1
1 1 1 1 1
My question is should I traverse from R to E and create Tree implementation then calculate the minimum path (DFS, BFS) ? If so how should I do it ?. Or is there another method for that ? For instance using graphs ?
You are right, you should use BFS (Breadth-first Search). It is easy to see that DFS (Depth-first Search) would not necessarily return the shortest. However, it will still tell you whether there exists a path between your pawn and the exit.
To apply the BFS algorithm you should think of your problem as a graph without changing the actual representation of the problem:
vertices of the graphs are all tuples (x, y) that are in your 2D list and are not obstacles
two vertices are connected by an edge if they are "neighbors" in the 2D array
Here's a standard implementation of BFS in which we replace the vertices and edges as explained above.
def BFS(board, start):
queue = list()
queue.append(start)
visited = set()
# this keeps track of where did we get to each vertex from
# so that after we find the exit we can get back
parents = dict()
parents[start] = None
while queue:
v = queue[0]
if board[v[0]][v[1]] == 'E':
break
queue = queue[1:] # this is inefficient, an actual queue should be used
visited.add(v)
for u in neighbors(board, v):
if u not in visited:
parents[u] = v
queue.append(u)
# we found the exit, now we have to go through the parents
# up to the start vertex to return the path
path = list()
while v != None:
path.append(v)
v = parents[v]
# the path is in the reversed order so we reverse it
path = reverse(path)
return path
def neighbors(board, v):
diff = [(0, 1), (0, -1), (1, 0), (-1, 0)]
retval = list()
for d in diff:
newr = d[0] + v[0]
newc = d[1] + v[1]
if newr < 0 or newr >= len(board) or newc < 0 or newc >= len(board[0]):
continue
if board[newr][newc] == 'X':
continue
retval.append((newr, newc))
return retval
File input.txt consists of two lines: first has integer number N space then integer number K (1 ≤ N,K ≤ 250000). Second has N space-delimeted integers, where each integer is less than or equal to K. It is guaranteed that each integer from 1 to K is in the array. The task is to find subarray of minimum length, that contains all integers. And print its start and end. Note, that indexing starts from 1.
Examples:
Input Output
5 3 2 4
1 2 1 3 2
6 4 2 6
2 4 2 3 3 1
I had this task in a recent programming competition. It is over, and I am not cheating. I've implemented it using python 3:
with open('input.txt') as file:
N, K = [int(x) for x in file.readline().split()]
alley = [int(x) for x in file.readline().split()]
trees = {}
min_idx = (1, N)
min_length = N
for i in range(N):
trees[alley[i]] = i
if len(trees) == K:
idx = (min(trees.values())+1, max(trees.values())+1)
length = idx[1] - idx[0] + 1
if length < min_length:
min_idx = idx
min_length = length
if min_length == K:
break
print (str(min_idx[0]) + " " + str(min_idx[1]))
The idea is to save last position of i-th tree into a dictionary and if dictionary contains all items, check if this subarray is minimum.
16th test showed that my algorithm exceeded time limit, which was 1 second. I think, that my algorithm is O(N), because it finishes in one run across array, and map access costs O(1).
How can one speed up this algorithm? Can be complexity reduced or is it my misunderstanding of some Python which takes much time?
Your algorithm is good but ignoring the time that len(trees) < K, it's O(NK) because every call to min and max is O(K). There's no need to call max because max(trees.values()) == i. Dealing with min is trickier, but if you keep track of which key corresponds to the minimum index then you can recalculate it only when that key is updated.
A minor point is that your last if doesn't always need to be checked.
Overall:
trees = {}
min_idx = (1, N)
min_length = N
first_index = -1
for i in range(N):
trees[alley[i]] = i
if len(trees) == K:
if first_index == -1 or alley[first_index] == alley[i]:
first_index = min(trees.values())
idx = (first_index+1, i+1)
length = idx[1] - idx[0] + 1
if length < min_length:
min_idx = idx
min_length = length
if min_length == K:
break
Make integer array Counts[K], fill with zeros.
Keep some variables - left index L, right index R (like your idx[0] and idx[1]), zero count Z.
Set L and R to 1, increment Counts[A[1]], set Z to K-1
Move R, incrementing Counts[A[1]], and decrement Z if zero entry is updated, until Z becomes 0
At this moment subarray [L..R] contains all values from to K
Now move L, decrementing Counts entry for values leaving the window. Increment Z if some entry becomes 0. When Z becomes non-zero, stop moving L and move R again.
When R reaches N and L stops, process is over. Minimum length is minimum from valid (R-L+1) pairs
Example run for your [1 2 1 3 2]
Move R
1 0 0 Z=2
1 1 0 Z=1
2 1 0 Z=1
2 1 1 Z=0
Move L
1 1 1 Z=0
1 0 1 Z=1 Stop moving L, check previous L,R pair 2,4
Move R
1 1 1 Z=0
move L
9 1 1 Z=1 Stop moving L, check previous L,R pair 3,5
Given n points, choose a point in the given list such that the sum of distances to this point is minimum ,compared to all others.
Distance is measured in the following manner.
For a point (x,y) all 8 adjacent points have distance 1.
(x+1,y)(x+1,y+1),(x+1,y-1),(x,y+1),(x,y-1),(x-1,y)(x-1,y+1),(x-1,y-1)
EDIT
More clearer explanation.
A function foo is defined as
foo(point_a,point_b) = max(abs(point_a.x - point_b.x),abs(point_a.y - point_b.y))
Find a point x such that sum([foo(x,y) for y in list_of_points]) is minimum.
Example
Input:
12 -14
-3 3
-14 7
-14 -3
2 -12
-1 -6
Output
-1 -6
Eg:
Distance between (4,5) and 6,7) is 2.
This can be done in O(n^2) time, by checking the sum of each pair.
Is there any better algorithm to do it?
Update: it sometimes fails to find the optimum, I'll leave this here till I find the problem.
this is O(n): nth is O(n) (expected, not worst), iterating over the list is O(n). If you need strict O() then pick the middle element with sorting but then it's going to be O(n*log(n)).
Note: it's easy to modifiy it to return all the optimal points.
import sys
def nth(sample, n):
pivot = sample[0]
below = [s for s in sample if s < pivot]
above = [s for s in sample if s > pivot]
i, j = len(below), len(sample)-len(above)
if n < i: return nth(below, n)
elif n >= j: return nth(above, n-j)
else: return pivot
def getbest(li):
''' li is a list of tuples (x,y) '''
l = len(li)
lix = [x[0] for x in li]
liy = [x[1] for x in li]
mid_x1 = nth(lix, l/2) if l%2==1 else nth(lix, l/2-1)
mid_x2 = nth(lix, l/2)
mid_y1 = nth(liy, l/2) if l%2==1 else nth(liy, l/2-1)
mid_y2 = nth(liy, l/2)
mindist = sys.maxint
minp = None
for p in li:
dist = 0 if mid_x1 <= p[0] <= mid_x2 else min(abs(p[0]-mid_x1), abs(p[0]-mid_x2))
dist += 0 if mid_y1 <= p[1] <= mid_y2 else min(abs(p[1]-mid_y1), abs(p[1]-mid_y2))
if dist < mindist:
minp, mindist = p, dist
return minp
It's based on the solution of the one dimensional problem - for a list of numbers find a number for which the sum distance is the minimum.
The solution for this is the middle element of the (sorted) list or any number between the two middle elements (including these two elements) if there are an even number of elements in the list.
Update: my nth algorithm seems to be very slow, probably there is a better way to rewrite it, sort outperforms it with < 100000 elements, so if you do speed comparison, just add sort(lix); sort(liy); and
def nth(sample, n):
return sample[n]
For anyone out there who wants to test his solution, here is what I use. Just run a loop, generate input and compare your solution with the output of bruteforce.
import random
def example(length):
l = []
for x in range(length):
l.append((random.randint(-100, 100), random.randint(-100,100)))
return l
def bruteforce(li):
bestsum = sys.maxint
bestp = None
for p in li:
sum = 0
for p1 in li:
sum += max(abs(p[0]-p1[0]), abs(p[1]-p1[1]))
if sum < bestsum:
bestp, bestsum = p, sum
return bestp
I can imagine a scheme better than O(n^2), at least in the common case.
Build a quadtree out of your input points. For each node in the tree, compute the number and average position of the points within that node. Then for each point, you can use the quadtree to compute its distance to all other points in less than O(n) time. If you're computing the distance from a point p to a distant quadtree node v, and v doesn't overlap the 45 degree diagonals from p, then the total distance from p to all the points in v is easy to compute (for v which are more horizontally than vertically separated from p, it is just v.num_points * |p.x - v.average.x|, and similarly using y coordinates if v is predominately vertically seperated). If v overlaps one of the 45 degree diagonals, recurse on its components.
That should beat O(n^2), at least when you can find a balanced quadtree to represent your points.