-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Rishabh
committed
Nov 29, 2022
0 parents
commit bcff076
Showing
5 changed files
with
603 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
class Empty(Exception): | ||
pass | ||
class HeapPriorityQueue(): | ||
#------------------------------ nonpublic behaviors ------------------------------ | ||
def _parent(self, j): | ||
return (j-1) // 2 #returns the index of parent element of jth element in the heap | ||
def _left(self, j): | ||
return 2*j+1 #returns the index of left element of jth element in the heap | ||
def _right(self, j): | ||
return 2*j+2 #returns the index of right element of jth element in the heap | ||
def _has_left(self, j): #returns whether the jth element has a left element in the heap or not | ||
return self._left(j) < len(self._data) # if index of left element of jth element is less than the length of list contaning heap elements then left element will exist and returns true otherwise false | ||
def _has_right(self, j): #returns whether the jth element has a right element in the heap or not | ||
return self._right(j) < len(self._data) # if index of right element of jth element is less than the length of list contaning heap elements then right element will exist and returns true otherwise false | ||
def _swap(self, i, j): #swaps the elements ith and jth index in the heap | ||
self._data[i], self._data[j] = self._data[j], self._data[i] #interchanging ith and jth elements | ||
def _upheap(self, j): #pushes an element from bottom to suitable position up inside the heap | ||
parent = self._parent(j) #accessign the parent of jth element | ||
if j > 0 and self._data[j] < self._data[parent]: #if parent is larger than jth element then jth element needs to be pushed up so we interchange jth element and its parent and recursively upheap it again | ||
self._swap(j, parent) | ||
self._upheap(parent) # recur at position of parent | ||
def _downheap(self, j): #pushes an element from top to suitable position down the heap | ||
if self._has_left(j): | ||
left = self._left(j) | ||
small_child = left # initally assuming left child is smaller than right | ||
if self._has_right(j): | ||
right = self._right(j) | ||
if self._data[right] < self._data[left]: #then checking the smaller among right and left child assigning the index of smaller value to the small_child variable | ||
small_child = right | ||
if self._data[small_child] < self._data[j]: #if the jth element is greater than small_child(i.e. greater than both children) then we need to push it down so swap j with small_child index | ||
self._swap(j, small_child) | ||
self._downheap(small_child) # recur at position of small child | ||
#------------------------------ public behaviors ------------------------------ | ||
def __init__(self,l): | ||
self._data=l #initially taking a list l which needs to be converted into a binary heap later | ||
def __len__(self): | ||
return len(self._data) #returns lenth of the list containing elements of the binary heap | ||
def is_empty(self): | ||
return len(self) == 0 #returns true if heap is empty | ||
def enqueue(self,key): #function to insert an element inside the heap | ||
self._data.append(key) | ||
self._upheap(len(self._data)-1) #since it was appended at the last index so we upheap the last index of the list | ||
def min(self): #rerurns the minimum element of the heap without removing it | ||
if self.is_empty(): | ||
raise Empty('Priority queue is empty.') | ||
key = self._data[0] #1st element of the list is the least element | ||
return key | ||
def remove_min(self): #returns the least element as well as removes it from the heap | ||
if self.is_empty(): | ||
raise Empty('Priority queue is empty.') | ||
self._swap(0,len(self._data)-1) #swapping the 1st and last element of the list | ||
item=self._data.pop() #accessing the last element which is the smallest after swapping | ||
self._downheap(0) #downheaping the 1st element which is the last element after swapping to suitale position so that next smallesst element of the heap comes at top again | ||
return item #returning the smallest element | ||
|
||
def listCollisions(M,x,v,m,T): | ||
upcoming_coll=[-1]*len(M) #a list to store the times of upcoming collisions of any ith index | ||
times=[] #a list to store the values of times at which collision can take place initially to be used to build the heap at t=0 | ||
ans=[] #final output list | ||
time=0 #variable storing the value of time elapsed at the moment a collision has completed which keeps on updating on every collision | ||
count=0 #variable storing the number of collisons that have took place | ||
prev_coll=[0]*len(M) # list storing the times of just previous collision of any ith index collision | ||
h=HeapPriorityQueue(times) # initiating heap using the initial time list | ||
for i in range(len(v)- 1): #appending possible collision times in the times list initially | ||
if v[i]-v[i+1]>0: #only in this case collison will be possible | ||
times.append([(x[i+1]-x[i])/(v[i]-v[i+1]),i]) #storing time of collsion and the index as a list | ||
upcoming_coll[i]=(x[i+1]-x[i])/(v[i]-v[i+1]) #updating the upcoming collision list | ||
for i in range(len(times)-1,-1,-1): #fast buildheap method to convert the times list into heap | ||
h._downheap(i) | ||
while time<T and count<m and not(h.is_empty()): | ||
coll=h.remove_min() #extracting the collision with least time i.e. one which will happen at first among all the possiblities inside the heap | ||
i=coll[1] #index of collision | ||
time=coll[0] #updating the time variable since the 0th index of heap element stores the time at which that collision will occur | ||
if time==upcoming_coll[i]: #for handling those cases when the time of collision of any ith index might have changed in between and the heap might be containing multiple collisions of a single index. So we continue only if the value of time(same as 0th index of collision list stored in heap) is same as the upcoming value of time of collision which keeps on updating so has the correct time at which collision of ith index will take place. | ||
if time<=T: # we continue only if the elapsed time till this collsion is less than or equal to T. | ||
x[i]=x[i]+v[i]*(time-prev_coll[i]) #updating the position of ith particle using formula final position=intial position+velocity(difference between time of this collison and previous collsion) we the the difference of time because last time we updated the position when its collion took place so we need to update according to time difference till then only. | ||
x[i+1]=x[i] #during collsion position of both particles will be same | ||
v1=v[i] #storing the value of v[i] to be used while updating v[i+1] because v[i] will be changed before v[i+1] | ||
v[i]=(M[i]-M[i+1])*v[i]/(M[i]+M[i+1])+(2*M[i+1])*v[i+1]/(M[i]+M[i+1]) #using formula given in reference link of assignment | ||
v[i+1]=(2*M[i])*v1/(M[i]+M[i+1])-(M[i]-M[i+1])*v[i+1]/(M[i]+M[i+1]) #using formula given in reference link of assignment | ||
prev_coll[i]=time #updating the prev_coll list to be used when collision takes place next time | ||
prev_coll[i+1]=time #updating the prev_coll list to be used when collision takes place next time | ||
ans.append((round(time,4),i,round(x[i],4))) #appending the time,index,potion of collsion in the final ans list | ||
count+=1 #incresing the count variable since collsion has took place | ||
if i>=1: #checking if the collsion of (i-1)th and ith particle after collsion of ith and (i+1)th is possible or not | ||
if v[i-1]-v[i]>0: #if collision is possible | ||
h.enqueue([time+(x[i]-x[i-1]-v[i-1]*(time-prev_coll[i-1]))/(v[i-1]-v[i]),i-1]) #we enqueue the time and index of (i-1)th index collision into the heap | ||
upcoming_coll[i-1]=time+abs(x[i]-x[i-1]-v[i-1]*(time-prev_coll[i-1]))/(v[i-1]-v[i]) #and update the upcoming coll list as this collsion is not yet happened but can happen in future | ||
if i<=len(v)-3: #checking the same thing for possibility of (i+1)th and (i+2)th particle collision | ||
if v[i+1]-v[i+2]>0: | ||
h.enqueue([time+(x[i+2]+v[i+2]*(time-prev_coll[i+2])-x[i+1])/(v[i+1]-v[i+2]),i+1]) | ||
upcoming_coll[i+1]=time+(x[i+2]+v[i+2]*(time-prev_coll[i+2])-x[i+1])/(v[i+1]-v[i+2]) | ||
else: #if the time value stored in heap element and upcoming coll list do not match that means, time of this collsion was updated and hence collision at this time is not possible and this element was removed from the heap so we continue to the next iteration without making any changes to position or velocity or heap or final ans list | ||
continue | ||
|
||
return(ans) #returning the final ans list |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
class Empty(Exception): | ||
pass | ||
class HeapPriorityQueue(): #heap class for extarcting the max capcacity of packets | ||
#------------------------------ nonpublic behaviors ------------------------------ | ||
def _parent(self, j): | ||
return (j-1) // 2 #returns the index of parent element of jth element in the heap | ||
def _left(self, j): | ||
return 2*j+1 #returns the index of left element of jth element in the heap | ||
def _right(self, j): | ||
return 2*j+2 #returns the index of right element of jth element in the heap | ||
def _has_left(self, j): #returns whether the jth element has a left element in the heap or not | ||
return self._left(j) < len(self._data) # if index of left element of jth element is less than the length of list contaning heap elements then left element will exist and returns true otherwise false | ||
def _has_right(self, j): #returns whether the jth element has a right element in the heap or not | ||
return self._right(j) < len(self._data) # if index of right element of jth element is less than the length of list contaning heap elements then right element will exist and returns true otherwise false | ||
def _swap(self, i, j): #swaps the elements ith and jth index in the heap | ||
self._data[i], self._data[j] = self._data[j], self._data[i] #interchanging ith and jth elements | ||
def _upheap(self, j): #pushes an element from bottom to suitable position up inside the heap | ||
parent = self._parent(j) #accessign the parent of jth element | ||
if j > 0 and self._data[j][1] > self._data[parent][1]: #if parent is samller than jth element then jth element needs to be pushed up so we interchange jth element and its parent and recursively upheap it again | ||
self._swap(j, parent) | ||
self._upheap(parent) # recur at position of parent | ||
def _downheap(self, j): #pushes an element from top to suitable position down the heap | ||
if self._has_left(j): | ||
left = self._left(j) | ||
large_child = left # initally assuming left child is larger than right | ||
if self._has_right(j): | ||
right = self._right(j) | ||
if self._data[right][1] > self._data[left][1]: #then checking the larger among right and left child assigning the index of larger value to the large_child variable | ||
large_child = right | ||
if self._data[large_child][1] > self._data[j][1]: #if the jth element is samller than large_child then we need to push it down so swap j with large_child index | ||
self._swap(j, large_child) | ||
self._downheap(large_child) # recur at position of large child | ||
#------------------------------ public behaviors ------------------------------ | ||
def __init__(self,l): | ||
self._data=l #initially taking a list l which needs to be converted into a binary heap later | ||
def __len__(self): | ||
return len(self._data) #returns lenth of the list containing elements of the binary heap | ||
def is_empty(self): | ||
return len(self) == 0 #returns true if heap is empty | ||
def enqueue(self,key): #function to insert an element inside the heap | ||
self._data.append(key) | ||
self._upheap(len(self._data)-1) #since it was appended at the last index so we upheap the last index of the list | ||
def max(self): #rerurns the maximum element of the heap without removing it | ||
if self.is_empty(): | ||
raise Empty('Priority queue is empty.') | ||
key = self._data[0] #1st element of the list is the max element | ||
return key | ||
def remove_max(self): #returns the maximum element as well as removes it from the heap | ||
if self.is_empty(): | ||
raise Empty('Priority queue is empty.') | ||
self._swap(0,len(self._data)-1) #swapping the 1st and last element of the list | ||
item=self._data.pop() #accessing the last element which is the largest after swapping | ||
self._downheap(0) #downheaping the 1st element which is the last element after swapping to suitale position so that next largest element of the heap comes at top again | ||
return item #returning the largest element | ||
def findMaxCapacity(n,l,a,b): #main function | ||
if a==b: | ||
return (float('inf'),[a]) | ||
l1=[] #adjacency list | ||
for i in range(n): | ||
l1.append([]) #empty lists for each vertex | ||
for x in l: | ||
l1[x[0]].append((x[1],x[2],x[0])) #appending the edges to both adjacency lists of two vertices | ||
l1[x[1]].append((x[0],x[2],x[1])) | ||
cap=float('inf') #initliasing the capacity of packet that needs to be returned with infinity | ||
prev_list=[-1]*n #a list which stores the source vertices of all the edges that we came across to be used later while printing the final path | ||
prev_list[a]=-2 #initialising the source vertex with -2 | ||
ans=[] #path list | ||
h=HeapPriorityQueue(l1[a]) #creating heap list of the edges originating from the source vertex a | ||
for i in range(len(l1[a])-1,-1,-1): #fast buildheap method | ||
h._downheap(i) | ||
while True: #loop runs till we get the destination vertex(then we break it) | ||
node=h.remove_max() # #extracting max capacity packets from the heap | ||
cap=min(cap,node[1]) #updating the cap variable since it is the min of all the capacities along the best(larget capacity) path | ||
if node[0]==b: #when we reach the destination vertex | ||
prev_list[b]=node[2] #update the source of last edge to reach dest | ||
break #exit from loop | ||
elif prev_list[node[0]]!=-1: #if the vertex is already visited then nothings needs to be done | ||
continue | ||
else: | ||
for i in l1[node[0]]: # if we get a new vertex other than dest vertex then we enquque the edges of that vertex in the heap | ||
h.enqueue(i) | ||
prev_list[node[0]]=node[2] #update the sorce vertex of the edge | ||
temp=prev_list[b] #temporary variable which stores the value of soruce vertices for tracking the path through which we reached the dest, initialising it with source of dest vertex | ||
ans.append(b) #appending the dest vertex in fianl path list | ||
while prev_list[temp]!=-2: #backtracking the source vertices to get the path and stopping when reached the vertex a(since it's source was stored as -2) | ||
ans.append(temp) #appending the path in ans list | ||
temp=prev_list[temp] #updating temp | ||
ans.append(temp) #appending a when loop stops | ||
ans.reverse() #reversing to get the path from a-b | ||
return (cap,ans) #returning cap and path |
Oops, something went wrong.