Implementation of the graph algorithm DFS / BFS with some obstacles - algorithm

I would like to ask you if you can give me an advice about a task that is based on a graph algorithm - either DFS or BFS.
There is a story behind the task. In the universe, there are a lot of planets. Some of these planets are infected by a dangerous virus that kills everyone.
The task is to find a path from one planet to a planet where is a medicine and so it is possible there to cure people from the virus.
To find the path is quite dangerous because of the virus. The crew may go to the planet where the virus is (they know in what planets the virus is), but they go there because they have no other option, since they have to find the medicine. On the planet that is infected the crew become infected and has some time to live (the time is a way from one planet to another planet). The result of this task should be a path from the start planet to a planet where there is the medicine. It should work for any map of known universe.
An example of a known universe with the planets. The starting planet is 0 and ending planet is 13. As you can see, the planets 1, 2, 4 are infected. In this specific example, there is one day to live after the crew become infected. So if the crew goes to the planet 1, they become infected and then on the next planet (4 or 6) dies. If they go to the planet 2, they become infected and they have just one day to live, so on the next planet dies... The result of this is the path 0 3 10 11 12 5 13.
An another example of a known universe with the planets. The starting planet is 0 and ending planet is 9. As you can see, the planets 4, 6, 7 are infected. In this specific example, there are two days to live after the crew become infected. So if the crew goes to the planet 4, they become infected and then after two days (one day = one way from one planet to another planet) dies. The result of this is either the path 0 1 2 3 5 7 6 9 or 0 1 2 3 5 7 8 9. As you can see, in some cases there might be more paths. The task is to find just one and it does not have to be the shortest one. Just one right path where the crew gets from start to end.
I have used the itterative DFS algorithm, but I do not know how to implement the part that if the crew would die on one path, the algorithm should find out another path. You can get it from the pictures of the graphs.

You can do a modified BFS to solve this problem. While doing the BFS, maintain the number of days infected as a part of the state. Then, while traversing, accept traversing to a previously visited node if we would be visiting with a better number of infected days (i.e. less).
This will also be the shortest-path, since BFS will produce shortest path in a non-weighted graph.
Here's a quick sketch in Python, applied to your second example:
from collections import deque
def bfs(graph, virus_nodes, start, target, survive_days):
survive_days = min(survive_days, len(graph))
visited = {start : 0}
queue = deque([(start, 0, (start,))]) # node, days infected, path
while queue:
node, days_infected, path = queue.popleft()
is_infected = days_infected > 0 or node in virus_nodes
nxt_days_infected = days_infected + 1 if is_infected else 0
if nxt_days_infected > survive_days:
continue
for nxt in graph[node]:
if nxt == target:
return path + (target,)
if nxt not in visited or visited[nxt] > nxt_days_infected:
queue.append((nxt, nxt_days_infected, path + (nxt,)))
visited[nxt] = nxt_days_infected
if __name__ == "__main__":
graph = {
0 : [1],
1 : [0, 2],
2 : [1, 3],
3 : [2, 4, 5],
4 : [3, 7],
5 : [3, 7],
6 : [7, 9],
7 : [4, 5, 6, 8],
8 : [7, 9],
9 : [6, 8],
}
virus_nodes = {4, 6, 7}
max_survive_days = 2
print(bfs(graph, virus_nodes, 0, 9, max_survive_days))
Output:
(0, 1, 2, 3, 5, 7, 6, 9)

The easiest way to solve this problem is with BFS, but you start from the end.
Lets say the time to live after infection is TTL days.
Run BFS from the destination, but you can only consider going to infected planets on paths less than TTL long. Once the BFS hits a path with TTL length, then you can only use clean planets from then on, all the way to the start.
This is especially easy if you do the BFS level-by-level. Then you only need to check if level < TTL.

If you don't care if it is shortest path you can use DFS with some adjustments to incorporate time to die feature. JSFiddle and code in javascript:
function findPath(path, infectedNodes, nodesBeforeDeath, initialNode, targetNode, adjacentTable) {
const lastNode = path.visitedNodes[path.visitedNodes.length - 1];
if (lastNode === targetNode) {
return path;
}
if (path.nodesLeftBeforeDeath !== null && path.nodesLeftBeforeDeath !== undefined && path.nodesLeftBeforeDeath == 0) {
return;
}
const adjacentNodes = adjacentTable[lastNode];
for (const node of adjacentNodes) {
if (path.visitedNodes.indexOf(node) > -1) {
continue;
}
const newPath = {
visitedNodes: [...path.visitedNodes, node]
};
if (path.nodesLeftBeforeDeath !== null && path.nodesLeftBeforeDeath !== undefined) {
newPath.nodesLeftBeforeDeath = path.nodesLeftBeforeDeath - 1;
}
else if (infectedNodes.indexOf(node) > - 1) {
newPath.nodesLeftBeforeDeath = nodesBeforeDeath;
}
const nodeResult = findPath(newPath, infectedNodes, nodesBeforeDeath, initialNode, targetNode, adjacentTable);
if (nodeResult) {
return nodeResult;
}
}
}
const firstExampleResult = findPath({
visitedNodes: [0]
},
[1, 2, 4],
1,
0,
13,
[[1,2,3,4],
[0,4,6],
[0, 7, 8],
[0, 9, 10],
[0, 1, 11, 12],
[6, 12, 13],
[1, 5, 7],
[2,6,14],
[2,9,14],
[3,8],
[3, 11],
[4,10,12],
[4,5,11],
[5],
[7,8]]
);
console.log(firstExampleResult.visitedNodes);
const secondExampleResult = findPath({
visitedNodes: [0]
},
[4, 6, 7],
2,
0,
9,
[
[1],
[0,2],
[1,3],
[2,4,5],
[3,7],
[3,7],
[7,9],
[4,5,6,8],
[7,9],
[6,8]
]
);
console.log(secondExampleResult.visitedNodes);

You can use use a shortest path algorithm like Dijkstra's algorithm for finding the shortest path between two nodes.
The edge weight is just the weight of the node you could be going to. In this case, all edges going to non-virus nodes would have a weight of 1. While edges going to a virus node would have a weight of number of nodes + 1. Visiting any virus planet should be easily more costly than visiting all other planets. This way the virus planets are only chosen if there is no other choice.
It also handles the case where you have to visit multiple virus planets, by preferring paths with the least amount of virus planets, not that it exactly matters in this case. Though if you want to list all equally valid solutions you could consider any additional virus planet as having a weight of 1 if the path already includes a virus planet. Then just display it as an another valid solution if it has the same weight as the best solution.
As for time to live, let's call it T, just check the current path. If the current node in the search is not the end node, and it has been T planets since the first virus planet crossed. Then reject the path as invalid.
An alternative handling for time to live would be to use an array weight, [path weight, virus age]. This means, given the same path weight, it will expand the path with the lowest virus age. Virus age is just # of days since exposed to virus.

Related

Min Path Sum in Matrix- Brute Force

I'm working on the following problem:
Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right which minimizes the sum of all numbers along its path.
Note: You can only move either down or right at any point in time.
My initial impression here was to, from each position in the grid, get the min length of going to the right vs going downward. However, this gives me the incorrect answer for the following:
Input:
[[1,2],[1,1]]
Output:
2
Expected:
3
Intuitively, not sure what I'm doing wrong. It's also very simple code (I know it's not memoized--was planning on that for the next step) but intuitively not sure what's going wrong. The recursive base case makes sense, and each number is being taken into consideration.
def min_path_sum(grid)
smallest_path(0, 0, grid)
end
def smallest_path(row, col, grid)
return 0 if (row == grid.length || col == grid.first.length)
current_val = grid[row][col]
[current_val + smallest_path(row+1, col, grid), current_val + smallest_path(row, col+1, grid)].min #memoize here
end
You didn't make a proper termination condition. You check only until you hit either the right column or bottom row. You need to stay within bounds, but continue until you hit the lower-right corner. You need to recur within bounds until you hit both limits.
Given that, your code does work okay: it finds the path of 2 to the bottom row, rather than the path of 3 to the right edge. You just have to teach it to finish the job.
Is that enough to move you to a solution?
As this is a shortest path problem on an acyclic directed graph, you could use a standard shortest path algorithm.
You could also use dynamic programming ("DP), which may be the most efficient optimization technique. My answer implements a DP algorithm.
A shortest-path or DP algorithm would be vastly superior to enumerating all paths from top-left to bottom-right. As the number of paths increases exponentially with the size of the array, simple enumeration could only be used on modest-sized arrays.
The idea of the DP algorithm is as follows. Let n and m be the numbers of rows and columns, respectively. First compute the shortest path from each column in the last row to the last column in the last row. This is an easy calculation because there is only one path to [m-1, n-1] for each of these elements. Starting with [m-1, n-2] we simply work back to [m-1, 0].
Next we compute the shortest paths from each element in each of the other rows to [m-1, n-1], starting with the penultimate row (m-2) and working back to the first row (0). The last element in each row, [i, n-1], is an easy calculation because one can only go down (to [i+1, n-1]). Therefore, the shortest path from [i, n-1] to [m-1, n-1] is first going to [i+1, n-1] and then following the shortest path from [i+1, n-1], which we've already computed (including its length, of course). The length of the shortest path from [i, n-1] is the "down" distance for [i, n-1] plus the length of the shortest path from [i+1, n-1].
For elements [i, j], n-1,i < j < m-1, we calculate the shortest paths if we go right and down, and select the shorter of the two.
We can implement this as follows.
Code
def shortest_path(distance)
last_row, last_col = distance.size-1, distance.first.size-1
h = {}
last_row.downto(0) do |i|
last_col.downto(0) do |j|
h_right = { min_path_len: distance[i][j][:r] + h[[i,j+1]][:min_path_len],
next_node: [i,j+1] } if j < last_col
h_down = { min_path_len: distance[i][j][:d] + h[[i+1,j]][:min_path_len],
next_node: [i+1,j] } if i < last_row
g =
case
when i == last_row && j == last_col
{ min_path_len: 0, next_node: nil }
when i == last_row
h_right
when j == last_col
h_down
else
[h_right, h_down].min_by { |f| f[:min_path_len] }
end
h[[i,j]] = g
end
end
build_path(h)
end
def build_path(h)
node = [0, 0]
len = h[node][:min_path_len]
arr = []
while h[node][:next_node]
arr << node
node = h[node][:next_node]
end
[len, arr]
end
Example
Suppose these are the distances between adjacent nodes.
● 4 ● 3 ● 1 ● 2 ●
6 2 5 4 5
● 3 ● 4 ● 6 ● 3 ●
1 3 4 2 3
● 6 ● 3 ● 1 ● 2 ●
It's convenient to provide this information in the form of an array of hashes.
distance = [
[{ r: 4, d: 6 }, { r: 3, d: 2 }, { r: 1, d: 5 }, { r: 2, d: 4 }, { d: 5 }],
[{ r: 3, d: 1 }, { r: 4, d: 3 }, { r: 6, d: 4 }, { r: 3, d: 2 }, { d: 3 }],
[{ r: 6 }, { r: 3 }, { r: 1 }, { r: 2 }]
]
We may now compute a shortest path.
p shortest_path distance
#=> [15, [[0, 0], [0, 1], [1, 1], [2, 1], [2, 2], [2, 3]]]
A shortest path is given by the second element of the array that is returned. 15 is the length of that path.

Breadth first search for an 8 puzzle

I was asked to implement a breadth first search for solving an eight-puzzle, representing each of its states with a vector with 9 elements, storing the tile number (or 0 for the gap) as the the data in the position.
For example [1, 3, 4, 2, 8, 0, 6, 7, 5] represents:
1 3 4
2 8 # <- Gap here
6 7 5
My pseudo-coded algorithm so far is:
startnode.configuration = start
startnode.parent = NIL
startnode.distance = 0
startnode.state = DISCOVERED
nodes_to_process = queue {}
nodes_to_process.enqueue(startnode)
while nodes_to_process is not empty
node = nodes_to_process.dequeue()
for each neighbour in NEIGHBOURS(node)
if neighbour.state == UNDISCOVERED
neighbour.distance = node.distance + 1
neighbour.parent = node
neighbour.state = DISCOVERED
The problem with this is: when I add a new neighbour how do I keep track of which nodes have been assigned as visited? Should I compare the state arrays one by one (preferaby having an ordered set of them), or is there a more refined approach, where I can skip this array-comparison step? Is the best algorithm complexity O(N * ln N) for the search?

previous most recent bigger element in array

I try to solve the following problem. Given an array of real numbers [7, 2, 4, 8, 1, 1, 6, 7, 4, 3, 1] for every element I need to find most recent previous bigger element in the array.
For example there is nothing bigger then first element (7) so it has NaN. For the second element (2) 7 is bigger. So in the end the answer looks like:
[NaN, 7, 7, NaN, 8, 8, 8, 8, 7, 4, 3, 1]. Of course I can just check all the previous elements for every element, but this is quadratic in terms of the number of elements of the array.
My another approach was to maintain the sorted list of previous elements and then select the first element bigger then current. This sounds like a log linear to me (am not sure). Is there any better way to approach this problem?
Here's one way to do it
create a stack which is initially empty
for each number N in the array
{
while the stack is not empty
{
if the top item on the stack T is greater than N
{
output T (leaving it on the stack)
break
}
else
{
pop T off of the stack
}
}
if the stack is empty
{
output NAN
}
push N onto the stack
}
Taking your sample array [7, 2, 4, 8, 1, 1, 6, 7, 4, 3, 1], here's how the algorithm would solve it.
stack N output
- 7 NAN
7 2 7
7 2 4 7
7 4 8 NAN
8 1 8
8 1 1 8
8 1 6 8
8 6 7 8
8 7 4 7
8 7 4 3 4
8 7 4 3 1 3
The theory is that the stack doesn't need to keep small numbers since they will never be part of the output. For example, in the sequence 7, 2, 4, the 2 is not needed, because any number less than 2 will also be less than 4. Hence the stack only needs to keep the 7 and the 4.
Complexity Analysis
The time complexity of the algorithm can be shown to be O(n) as follows:
there are exactly n pushes (each number in the input array is
pushed onto the stack once and only once)
there are at most n pops (once a number is popped from the stack,
it is discarded)
there are at most n failed comparisons (since the number is popped
and discarded after a failed comparison)
there are at most n successful comparisons (since the algorithm
moves to the next number in the input array after a successful
comparison)
there are exactly n output operations (since the algorithm
generates one output for each number in the input array)
Hence we conclude that the algorithm executes at most 5n operations to complete the task, which is a time complexity of O(n).
We can keep for each array element the index of the its most recent bigger element. When we process a new element x, we check the previous element y. If y is greater then we found what we want. If not, we check which is the index of the most recent bigger element of y. We continue until we find our needed element and its index. Using python:
a = [7, 2, 4, 8, 1, 1, 6, 7, 4, 3, 1]
idx, result = [], []
for i, v in enumerate(a, -1):
while i >= 0 and v >= a[i]:
i = idx[i]
idx.append(i)
result.append(a[i] if i >= 0 else None)
Result:
[None, 7, 7, None, 8, 8, 8, 8, 7, 4, 3]
The algorithm is linear. When an index j is unsuccessfully checked because we are looking for the most recent bigger element of index i > j then from now on i will point to a smaller index than j and j won't be checked again.
Why not just define a variable 'current_largest' and iterate through your array from left to right? At each element, current largest is largest previous, and if the current element is larger, assign current_largest to the current element. Then move to the next element.
EDIT:
I just re-read your question and I may have misunderstood it. Do you want to find ALL larger previous elements?
EDIT2:
It seems to me like the current largest method will work. You just need to record current_largest before you assign it a new value. For example, in python:
current_largest = 0
for current_element in elements:
print("Largest previous is "+current_largest)
if(current_element>current_largest):
current_largest = current_element
If you want an array of these, then just push the value to an array in place of the print statement.
As per my best understanding of your question. Below is a solution.
Working Example : JSFIDDLE
var item = document.getElementById("myButton");
item.addEventListener("click", myFunction);
function myFunction() {
var myItems = [7, 2, 4, 8, 1, 1, 6, 7, 4, 3, 1];
var previousItem;
var currentItem;
var currentLargest;
for (var i = 0; i < myItems.length; i++) {
currentItem = myItems[i];
if (i == 0) {
previousItem = myItems[0];
currentItem = myItems[0];
myItems[i] = NaN;
}
else {
if (currentItem < previousItem) {
myItems[i] = previousItem;
currentLargest = previousItem;
}
if (currentItem > currentLargest) {
currentLargest = currentItem;
myItems[i] = NaN;
}
else {
myItems[i] = currentLargest;
}
previousItem = currentItem;
}
}
var stringItems = myItems.join(",");
document.getElementById("arrayAnswer").innerHTML = stringItems;
}

change the info part of each node in binary tree

i have a binary tree.
2
/ \
3 4
/ \ \
5 1 8
\ /
6 9
I want to change the info part of each node such that the
nodeinfo = nodeinfo + nextInorderNodeInfo
so the actual inorder traversal
5, 6, 3, 1, 2, 4, 9, 8
will change to
5+6,6+3,3+1,1+2,2+4,4+9,9+8,8+0
11, 9, 4, 3, 6, 13, 17, 8
i need to write a function that will modify the binary tree info parts of each node.
i have done the following
calling
change(root,NULL);
function definition
void change(node* n, node *k)
{
if (n)
{
if (n->left) change(n->left,n);
if (n->right) change(n,n->right);
n->info + = k->info;
}
}
in this way i am not able to modify the nodes that are right hand leaf nodes.
can someone give the correct solution..???
thanks in advance
Write a reverse in-order traversal function (as in right, this, left rather than left, this, right) (which is technically still in-order, just with a different definition, but that's besides the point).
So this function will process the nodes in this order:
8, 9, 4, 2, 1, 3, 6, 5
This function must also remember the last processed node's value (before you added to it) and simply add this value to the current node.
And here's even some code which should work:
int last = 0;
void change(node* n)
{
if (n)
{
change(n->right);
int tempLast = n->info;
n->info += last;
last = tempLast;
change(n->left);
}
}

Recursive interlacing permutation

I have a program (a fractal) that draws lines in an interlaced order. Originally, given H lines to draw, it determines the number of frames N, and draws every Nth frame, then every N+1'th frame, etc.
For example, if H = 10 and N = 3, it draws them in order:
0, 3, 6, 9,
1, 4, 7,
2, 5, 8.
However I didn't like the way bands would gradually thicken, leaving large areas between undrawn for a long time. So the method was enhanced to recursively draw midpoint lines in each group instead of the immediately sebsequent lines, for example:
0, (32) # S (step size) = 32
8, (24) # S = 16
4, (12) # S = 8
2, 6, (10) # S = 4
1, 3, 5, 7, 9. # S = 2
(The numbers in parentheses are out of range and not drawn.) The algorithm's pretty simple:
Set S to a power of 2 greater than N*2, set F = 0.
While S > 1:
Draw frame F.
Set F = F + S.
If F >= H, then set S = S / 2; set F = S / 2.
When the odd numbered frames are drawn on the last step size, they are drawn in simple order just as an the initial (annoying) method. The same with every fourth frame, etc. It's not as bad because some intermediate frames have already been drawn.
But the same permutation could recursively be applied to the elements for each step size. In the example above, the last line would change to:
1, # the 0th element, S' = 16
9, # 4th, S' = 8
5, # 2nd, S' = 4
3, 7. # 1st and 3rd, S' = 2
The previous lines have too few elements for the recursion to take effect. But if N was large enough, some lines might require multiple levels of recursion. Any step size with 3 or more corresponding elements can be recursively permutated.
Question 1. Is there a common name for this permutation on N elements, that I could use to find additional material on it? I am also interested in any similar examples that may exist. I would be surprised if I'm the first person to want to do this.
Question 2. Are there some techniques I could use to compute it? I'm working in C but I'm more interested at the algorithm level at this stage; I'm happy to read code other language (within reason).
I have not yet tackled its implemention. I expect I will precompute the permutation first (contrary to the algorithm for the previous method, above). But I'm also interested if there is a simple way to get the next frame to draw without having to precomputing it, similar in complexity to the previous method.
It sounds as though you're trying to construct one-dimensional low-discrepancy sequences. Your permutation can be computed by reversing the binary representation of the index.
def rev(num_bits, i):
j = 0
for k in xrange(num_bits):
j = (j << 1) | (i & 1)
i >>= 1
return j
Example usage:
>>> [rev(4,i) for i in xrange(16)]
[0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15]
A variant that works on general n:
def rev(n, i):
j = 0
while n >= 2:
m = i & 1
if m:
j += (n + 1) >> 1
n = (n + 1 - m) >> 1
i >>= 1
return j
>>> [rev(10,i) for i in xrange(10)]
[0, 5, 3, 8, 2, 7, 4, 9, 1, 6]

Resources