How to solve this by recursion? - algorithm

I required some help in solving this question by recursion.
Question:
A lazy tourist wants to visit as many interesting locations in a city as possible without going one step further than necessary. Starting from his hotel, located in the north-west corner of city, he intends to take a walk to the south-east corner of the city and then walk back. When walking to the south-east corner, he will only walk east or south, and when walking back to the north-west corner, he will only walk north or west. After studying the city map he realizes that the task is not so simple because some areas are blocked. Therefore he has kindly asked you to write a program to solve his problem.
Given the city map (a 2D grid) where walkable areas are marked by ".", interesting locations are marked by "*", and blocked areas are marked by "#", determine the maximum number of interesting locations he can visit. Locations visited twice are only counted once.
*........
.....**#.
..**...#*
..####*#.
.*.#*.*#.
...#**...
*........
Ans : 7.
My initial thoughts were to solve this problem in two parts.
1) When the tourist starts from top-left corner.
2) When the tourist starts from down-right corner.
For the answer add them both parts, and that would be it.
Here's what the method looks like for the first part.
i and j were initially 0, 0.
path(int i, int j){
if(i == H && j == W){
return (strV[i][j] == '*')?1:0;
}
if(i >H || j >W || i<0 || j<0){
return 0;
}
if(visited[i][j] == 1){
//cout<<"vis";
return 0;
}
if(strV[i][j] == '#' ){
return 0;
}
visited[i][j] =1;
cout<<i<<" "<<j<<"\n";
// as i can go only down and right ,for the first part.
// south and east.
return max(path(i+1, j), path (i, j+1) ) + (strV[i][j] == '*')?1 :0;
}
Similarly I tried calculating for the second part.
As I've maintained the visited array, there is no possibility of revisiting the same point.
Hope that should satisfy the condition as they mention in the question.
I'm not sure if this will solve the question, it's giving incorrect answer.
Here's the question link: http://www.spoj.com/problems/TOURIST/
Thanks for your time.

A few observations will help you solve this problem more easily:
In the return trip the tourist can only move left or up in the grid. This is equivalent to moving right or down in the first trip. So
basically there is no difference between the two trips.
Since both trips are basically same we only need to think of first trip. We can send 2 tourists at the same time starting from (0,0)
cell. So our state will consist of (x1,y1,x2,y2) where (x1,y1) is
position of first tourist and (x2,y2) is position of second tourist in
grid.
At each step either tourist can move right or down, so we have 4 choices for movement(2 choice for each tourist).
If both tourists are on the same cell (x1==x2 and y1==y2) then we can add only 1 to result if that cell is special.
This algorithm has time complexity of O(n^4) which won't probably run in time. We can reduce the complexity to O(n^3). If we know the
position of first tourist is (x1,y1) the x coordinate of second
tourist is x2 then we must have x1+y1=x2+y2 since they both cover same
distance in same amount of time. So y2=x1+y1-x2 and our state depends
only on (x1,y1,x2).
Code:
const int N 100+5
vector<string> board;
int dp[N][N][N]; // initialize with -1
int calc(int x1, int y1, int x2) {
int n=board.size(), m=board[0].size();
int y2=x1+y1-x2;
if(x1>=n || x2>=n || y1>=m || y2>=m) return 0; // out of range
if(board[x1][y1]=='#' || board[x2][y2]=='#') return 0; // path blocked so its an invalid move
if(dp[x1][y1][x2]!=-1) return dp[x1][y1][x2]; // avoid recalculation
int res=0;
if(board[x1][y1]=='*') res++;
if(board[x2][y2]=='*') res++;
if(board[x1][y1]=='*' && x1==x2 && y1==y2) res=1; // both tourist on same spot
int r=calc(x1+1, y1, x2); // first tourist down second tourist right
r=max(r, calc(x1+1, y1, x2+1)); // first tourist down second tourist down
r=max(r, calc(x1, y1+1, x2)); // first tourist right second tourist right
r=max(r, calc(x1, y1+1, x2+1)); // first tourist right second tourist down
res+=r;
dp[x1][y1][x2]=res; // memoize
return res;
}

Your approach has a few flaws. Some that make it give the wrong solution and some that are just lead to an inefficient search.
The flaws that lead to a wrong solution are:
Adding the interesting locations on the best paths each way will not be a valid answer because you haven't discounted the interesting sights that you already visited one way from the path you took on the way back. (This will also just mean you're answer will just be double the one way number as the best path back would be the same path in reverse). You actually need to search your way back from the end of your way forward so you can properly account for the already visited
Terminating the search in a block path with a value of 0. It needs to be highly penalized (at least H+V-2) so just reaching a block and giving up (not reaching the corner) won't be considered a valid path
Not unvisiting the locations when you are backtracking. This will lead to not counting interesting locations that may have been visited in a different path than the one being evaluated
Terminating on a visited location. There is nothing in the problem statement that prohibits crossing the same location twice. It can never happen when considering only one of the directions of travel anyway because you can only travel two directions that don't allow for going back on yourself
The flaw that leads to an inefficient search is not simplifying the search domain before searching for the best path. By creating a graph that only contains the interesting locations as nodes and connections between them when there is a valid (non-blocked) path between them you can decrease your search space considerably (depending on how sparse the interesting locations and abundant the blocks are)
Keeping as close to your attempt as possible my suggestions are:
When visiting a location simply add one so you can unvisit it by removing one before returning your best path (just setting to zero may be a problem if you also do it in the search for the return path)
When you reach the end of the path one way (i == H-1 && j == W-1, because you start at 0,0 not 1,1) do a similar search the other way keeping the visited location info
Change your interesting location increase to (visited[i][j] == 0) && (strV[i][j] == '*') so you don't count interesting locations twice in the same path
Penalise running into a block by returning at least -(H+V-2), but MIN_INT_VALUE or anything in between would also work
Don't give up when you reach a location you already visited
Hope this is helpful

Here's a solution in JavaScript. It recursively tries all valid directions SE and NW, and saves its result if it's greater than the current max result.
function solve(grid) {
var maxVisited = 0;
var rows = grid.length;
var cols = grid[0].length;
// used to make a new copy of the visited set
function copy(obj) {
return JSON.parse(JSON.stringify(obj));
}
// used to test a current location returning false or the grid value
function move(r, c) {
if(r < 0 || c < 0 || r >= rows || c >= cols) { return false; }
if(grid[r][c] === "#") { return false; }
return grid[r][c];
}
// recursively solve the problem
function solveRec(r, c, grid, goBack, visited) {
// end trip, check if visited is greater than current max
if(goBack === true && r === 0 && c === 0) {
var count = Object.keys(visited).length;
if(count > maxVisited) { maxVisited = count; }
return;
}
var m = move(r, c);
// invalid move
if(m === false) { return; }
// place of interest, add to visited set
if(m === "*") { visited["" + r + ":" + c] = true; }
// try a move, if at S.E. go back home (turn around)
if(goBack === true || (r === rows-1 && c === cols-1)) {
// north west
solveRec(r-1, c, grid, true, copy(visited));
solveRec(r, c-1, grid, true, copy(visited));
} else {
// south east
solveRec(r+1, c, grid, false, copy(visited));
solveRec(r, c+1, grid, false, copy(visited));
}
}
solveRec(0, 0, grid, false, {});
return maxVisited;
}
console.log(solve([
"*........".split(""),
".....**#.".split(""),
"..**...#*".split(""),
"..####*#.".split(""),
".*.#*.*#.".split(""),
"...#**...".split(""),
"*........".split("")
])); // 7
EDIT Here's the jsfiddle http://jsfiddle.net/reeyws86/ You may want to add logic to avoid corner cases like when the grid is empty or only has 1 square.

Related

Pathfind to edge / find hole algorithm?

I'm looking for an algorithm that can search a 3D array and figure out the quickest way to pathfind from a single point to any edge of the array. More specifically, I am working on checking if a hollow structure within a 3 dimensional space is "airtight" and if it would be possible for something within this structure to escape through a hole.
Could anyone recommend good sources, documents, or articles that I could read about this stuff? I'm not looking for a specific copy/paste solution since I really want to understand how it works but my searches have been turning up pretty dry.
There's also I high chance that I'm approaching this from a completely wrong angle and actually need to change how I go about looking for information on this, if you have any recommendations, let me know!
Also, the max size of the 3D array will be [256,256,256] but the structures within will usually be much smaller. Furthermore, each element within the array will be like an empty room with 6 possible walls on each side.
I've tried searching for some time but most of my findings have been about "shortest-path" or "path of least resistance" which is not what I need. I want the least computationally expensive way to check if I can get from the inside of the structure to the outside, even if it is not the shortest path.
#Update: Round About Solution#
I did not find an actual answer to my question but I found a way to solve one of my problems so I'm adding this as an edit rather than as an answer because I still want to know how to deal with the original question.
After some research, I discovered flood-fill algorithms which gave me some ideas, the only problem with them was that most of the flood-fill methods were recursive. While recursion was a fast and simple solution, it quickly caused a stack overflow and could not even get close to the depth that I would potentially need to reach (it could get about 18,000 but I needed > 256^3). I then discovered scan-line filling, which was good, but I was having trouble understanding it and could only find 2D implementations for pixel-based graphics or 3D point-based models. After learning information about both uses, I managed to scrounge together my own 3D scan-line filler that can completely fill in a 256^3 area in about 2 seconds.
A "block" is an element in the 3d array.
Whether or not it "isSealed" could better be about checking if that block has been iterated over yet.
The stack is used to queue up empty elements that need to be checked.
This is in c# btw.
private void MyScanLine3DFill(int x, int y, int z)
{
Stack<Block> blocks = new Stack<Block>(); //create a stack
blocks.Push(grid[x, y, z]); //initialize the stack to check from the specified starting position
bool spanWest;
bool spanEast;
bool spanSouth;
bool spanNorth;
while (blocks.Count != 0) //loop through the stack as long as it is not empty
{
Block temp = blocks.Pop(); //get the block on top of the stack
int y1 = temp.coords.Item2; //get the y coord of the block
while (y1 >= 0 && grid[temp.coords.Item1, y1, temp.coords.Item3].isSealed == false) //go down the column until you hit a sealed block
{
y1--; //go down a block in the column
}
y1++; //go up one block
spanWest = false; //reset the spans for this iteration of the loop
spanEast = false;
spanSouth = false;
spanNorth = false;
while (y1 < maxY && grid[temp.coords.Item1, y1, temp.coords.Item3].isSealed == false) //go up the column until you hit a sealed block
{
grid[temp.coords.Item1, y1, temp.coords.Item3].isSealed = true; //set the block in the current iteration to true
//check the west block
if (!spanWest && temp.coords.Item1 > 0 && grid[temp.coords.Item1 - 1, y1, temp.coords.Item3].isSealed == false) //if there is an unsealed block to the west
{
blocks.Push(new Block(airBlock, (temp.coords.Item1 - 1, y1, temp.coords.Item3))); //add the unsealed block to the west to the stack
spanWest = true;
}
else if (spanWest && temp.coords.Item1 - 1 == 0 && grid[temp.coords.Item1 - 1, y1, temp.coords.Item3].isSealed != false) //if there is a sealed block to the west
{
spanWest = false;
}
//check the east block
if (!spanEast && temp.coords.Item1 < maxX - 1 && grid[temp.coords.Item1 + 1, y1, temp.coords.Item3].isSealed == false) //if there is an unsealed block to the east
{
blocks.Push(new Block(airBlock, (temp.coords.Item1 + 1, y1, temp.coords.Item3))); //add the unsealed block to the east to the stack
spanEast = true;
}
else if (spanEast && temp.coords.Item1 < maxX - 1 && grid[temp.coords.Item1 + 1, y1, temp.coords.Item3].isSealed != false) //if there is a sealed block to the east
{
spanEast = false;
}
//check the south block
if (!spanSouth && temp.coords.Item3 > 0 && grid[temp.coords.Item1, y1, temp.coords.Item3 - 1].isSealed == false) //if there is an unsealed block to the south
{
blocks.Push(new Block(airBlock, (temp.coords.Item1, y1, temp.coords.Item3 - 1))); //add the unsealed block to the south to the stack
spanSouth = true;
}
else if (spanSouth && temp.coords.Item3 - 1 == 0 && grid[temp.coords.Item1, y1, temp.coords.Item3 - 1].isSealed != false) //if there is a sealed block to the south
{
spanSouth = false;
}
//check the north block
if (!spanNorth && temp.coords.Item3 < maxZ - 1 && grid[temp.coords.Item1, y1, temp.coords.Item3 + 1].isSealed == false) //if there is an unsealed block to the north
{
blocks.Push(new Block(airBlock, (temp.coords.Item1, y1, temp.coords.Item3 + 1))); //add the unsealed block to the north to the stack
spanNorth = true;
}
else if (spanNorth && temp.coords.Item3 < maxZ - 1 && grid[temp.coords.Item1, y1, temp.coords.Item3 + 1].isSealed != false) //if there is a sealed block to the north
{
spanNorth = false;
}
y1++; //go up a block
}
}
//refresh mesh if applicable
}
I don't think you can really beat the basic conceptual idea of a breadth-first or depth-first search in this case. The base case that terminates our search is unknown in advance. You can't really get too fancy for an unknown destination in an algorithm (a heuristic) except to linearly search for the solution one step at a time.
A Potential Optimization
I was thinking about this problem a bit further the other day, and while this is still the same overall algorithmic idea, it should be considerably more efficient than testing one cell at a time for occupancy (getting closer to 256^2 tests than 256^3 tests).
The idea is to store and maintain 3 separate sets of 3D occupancy bitsets (the second transposed in a way such that the next bit is the next element along Y, and the third along Z) requiring only one bit to indicate an occupied cell.
This way instead of testing one cell at a time in your search, you can easily test 64 at a time or more on a 64-bit architecture (32 or more on 32-bit, and up to 512 at a time with SIMD). With a 256x256x256 grid, that should consume the data extremely quickly. Using 256-bit registers or larger, you can test all the cells of a 1D slice of the grid along a axis from a given point to the nearest occupied cell in a single FFS (find first set bit) test which is generally an efficient intrinsic on lots of hardware. Some handy code I use a lot:
// Returns the index of the first set bit or 64 if there are
// no set bits.
int32_t ffs(uint64_t bits)
{
#if defined(_MSC_VER) && defined(_WIN64)
// MSVC 64-bit.
unsigned long index = 0;
return _BitScanForward64(&index, bits) ? index : 64;
#elif defined(_MSC_VER) && defined(_WIN32)
// MSVC 32-bit.
unsigned long index = 0;
if (_BitScanForward(&index, bits & 0xffffffff))
return index;
else if (_BitScanForward(&index, bits >> 32))
return index + 32;
return 64;
#else
// GCC
return bits != 0 ? ffsll(bits): 64;
#endif
}
// Returns the index of the first set bit starting from the nth
// position or 64 if there are no set bits starting from
// that position.
int32_t ffs(uint64_t bits, int32_t n)
{
if (n < 64)
{
// Clear to nth bit.
bits &= ~((1ull << n) - 1);
// Return next set bit.
return ffs(bits);
}
return 64;
}
The rest is similar to your vanilla BFS/DFS. At least I haven't been able to think up a better solution. Visual illustration:
It's admittedly a very rough idea and I'm one of those who needs to dive in and code it and profile it to start resolving things I missed on the drawing board (which I often do) and generate new algorithmic and optimization ideas. Yet I think it should be a good starting point.
If you're doing many of these tests per frame, you might just want to compute an entire distance map for the entire grid by pushing all the nodes to the processing list initially. That would avoid a lot of overlapping/redundant work finding shortest distance to edge from any given point on the grid. Precomputing a distance map for the entire grid in advance can be handy even for cases where the elements in the grid are moving each frame to cut down on the redundant work as it gives you the shortest path from any cell to a target in the minimal number of iterations (the number of iterations being equal to the shortest geodesic distance).
A Little Bit Further On Computing Entire Distance Maps
In response to the comments, assuming you don't just do the pathfinding for one cell/point in your grid per frame (even let's say just a few nodes per frame), I think you'll quickly find the precomputation of the entire distance map for the entire 256^3 grid outperforming alternatives, even if you have to invalidate the distance map every single frame.
It's because the amount of memory you have to read and write to solve this problem, even in the best-case scenarios I can possibly conceive, are quite large. In spite of my proposal to use bitsets, 256^3 is still a lot of cells, and further you need to temporarily associate data to them in parallel storing things like the shortest distance discovered so far in a direction with a BFS or DFS. You'll still read a good chunk of its memory and write to a hefty chunk of parallel memory looking for the nearest edge and keeping track of distances. So it's very redundant work for the hardware, even if it seems less redundant for humans, to throw away all that valuable data you computed and cached in L3, L2, possibly even L1 only to evict it and read and write to a good portion again by doing pathfinding a second time in the same frame.
So actually I'd start as a strategy, unless you're only doing one pathfinding operation per frame, to start with computing the entire distance map for the entire 256^3 grid to trivialize the pathfinding, and make that precomputation your primarily target for profiling and optimization. And only then if you need further performance, you start to break away from it and start computing things more on the fly for each individual pathfinding operation (and with the ability to easily throw away your new solution with, perhaps, a version control branch, since you might easily finding it performing worse). I think that's a more productive path in most cases.

Given a circular linked list, find a suitable head such that the running sum of the values is never negative

I have a linked list with integers. For example:
-2 → 2
↑ ↓
1 ← 5
How do I find a suitable starting point such that the running sum always stays non-negative?
For example:
If I pick -2 as starting point, my sum at the first node will be -2. So that is an invalid selection.
If I pick 2 as the starting point, the running sums are 2, 7, 8, 6 which are all positive numbers. This is a valid selection.
The brute force algorithm is to pick every node as head and do the calculation and return the node which satisfies the condition, but that is O(𝑛²).
Can this be done with O(𝑛) time complexity or better?
Let's say you start doing a running sum at some head node, and you eventually reach a point where the sum goes negative.
Obviously, you know that the head node you started at is invalid as an answer to the question.
But also, you know that all of nodes contributing to that sum are invalid. You've already checked all the prefixes of that sublist, and you know that all the prefixes have nonnegative sums, so removing any prefix from the total sum can only make it smaller. Also, of course, the last node you added must be negative, you can't start their either.
This leads to a simple algorithm:
Start a cumulative sum at the head node.
If it becomes negative, discard all the nodes you've looked at and start at the next one
Stop when the sum includes the whole list (success), or when you've discarded all the nodes in the list (no answer exsits)
The idea is to use a window, i.e. two node references, where one runs ahead of the other, and the sum of the nodes within that window is kept in sync with any (forward) movement of either references. As long as the sum is non-negative, enlarge the window by adding the front node's value and moving the front reference ahead. When the sum turns negative, collapse the window, as all suffix sums in that window will now be negative. The window becomes empty, with back and front referencing the same node, and the running sum (necessarily) becomes zero, but then the forward reference will move ahead again, widening the window.
The algorithm ends when all nodes are in the window, i.e. when the front node reference meets the back node reference. We should also end the algorithm when the back reference hits or overtakes the list's head node, since that would mean we looked at all possibilities, but found no solution.
Here is an implementation of that algorithm in JavaScript. It first defines a class for Node and one for CircularList. The latter has a method getStartingNode which returns the node from where the sum can start and can accumulate without getting negative:
class Node {
constructor(value, next=null) {
this.value = value;
this.next = next;
}
}
class CircularList {
constructor(values) {
// Build a circular list for the given values
let node = new Node(values[0]);
this.head = node;
for (let i = values.length - 1; i > 0; i--) {
node = new Node(values[i], node);
}
this.head.next = node; // close the cycle
}
getStartingNode() {
let looped = false;
let back = this.head;
let front = this.head;
let sum = 0;
while (true) {
// As long as the sum is not negative (or window is empty),
// ...widen the window
if (front === back || sum >= 0) {
sum += front.value;
front = front.next;
if (front === back) break; // we added all values!
if (front === this.head) looped = true;
} else if (looped) {
// avoid endless looping when there is no solution
return null;
} else { // reset window
sum = 0;
back = front;
}
}
if (sum < 0) return null; // no solution
return back;
}
}
// Run the algorithm for the example given in question
let list = new CircularList([-2, 2, 5, 1]);
console.log("start at", list.getStartingNode()?.value);
As the algorithm is guaranteed to end when the back reference has visited all nodes, and the front reference will never overtake the back reference, this is has a linear time complexity. It cannot be less as all node values need to be read to know their sum.
I have assumed that the value 0 is allowed as a running sum, since the title says it should never be negative. If zero is not allowed, then just change the comparison operators used to compare the sum with 0. In that case the comparison back === front is explicitly needed in the first if statement, otherwise you may actually drop it, since that implies the sum is 0, and the second test in that if condition does the job.

General algorithm to solve a maze with n balls

I got asked the other day about "Outlining a general algorithm for solving a maze with n balls, where the goal is to get all the balls to a given position in the maze (the maze has no exit)". The only rules are that the algorithm must be effective (better than randomly moving the balls) and that all the commands issued will affect all the balls, so that is one ball is moved north, so will all the others if they are not blocked.
To do this I made some assumptions, namely that
The maze is standard/perfect
The balls cannot block each other
The balls can get to the position asked
A command will let the balls roll until the hit a wall
A command can only be N/S/E/W
The maze is randomly constructed and the balls randomly distributed each time it is reset
All of the maze can be observed at once by the maze-operator
And, to make my algorithm work
The balls are not identical (e.g. the have a number or something on them)
The maze-operator has a photographic memory
Given this, I thought the best idea would be to
Randomly (or in a smart way) move the balls until two balls get to the target position
Save the path from their starting position to their end position.
Identify those balls and where they came from, and for each of them, do 1.
The "break" in this recursive algorithm would be when all the balls have a way to reach the given target (O(log(n)) recursions I think?)
Does this work? Does anyone else have a better algorithm for doing this?
I had another idea involving moving all the balls to the same random position and then moving them all as one ball, but that seemed like a worse algorithm.
Another idea would be to generate a graph (graph theory that is) where all the stable points for a ball would be a node, and the move would be an edge, but I can't see how that doesn't need a lot of brute force to be done.
I would suggest the following algorithm:
Create a data structure for the maze where for each free cell (square) the following is known:
a. the coordinates (row, column)
b. the target cells for the 4 moves (north, east, south, west)
c. the reverse of b: the cells from where marbles can come to this cell (if any).
Perform a BFS starting from the target cell, performing reverse moves with one imaginary marble, assigning to each visited cell, the least number of moves necessary from that cell to reach the target cell. Note that some cells might not get visited this way, meaning that if a marble would be placed there, there would be no way to get it to the target cell by performing legal moves. These cells will get an infinite distance attributed to them (initial value).
Create an evaluation function that will assign a cost to a certain configuration of marbles. The suggested evaluation function would sum up the squares of the distances of each of the cells occupied by at least one marble. By taking the square, higher distances will bring a relatively high cost, so that the algorithm will favour moves that improve the position of the marble that has the worst position. This function would not count double the cells that are occupied by more than one marble. That way, configurations where marbles share a cell are favoured.
From the starting position, generate the 4 possible moves with their evaluated cost. Sort them ascending by their cost, and in that order perform a DFS, repeating this step recursively. When the cost becomes zero, a solution is found, and during the immediate backtracking out of recursion, the "path" of moves is returned. When the cost is infinite, then the search is stopped, and the next move is tried, ...etc.
During the search keep a list of visited positions. When a position is visited again, the evaluation function will give it a value of infinity, so that the search will backtrack when this happens.
Here is a JavaScript implementation of the above algorithm:
"use strict";
function createMaze(mazeStr) {
var maze, lines, cell, row, ch, id, r, c, n, m;
maze = {
nodesRowCol: [],
nodes: [],
target: null,
marbles: []
};
id = 0;
lines = mazeStr.split("\n");
for (r = 0; r < lines.length; r++) {
maze.nodesRowCol[r] = row = [];
for (c = 0; c < lines[r].length; c++) {
ch = lines[r].charAt(c);
if (ch !== '#') {
maze.nodes[id] = row[c] = cell = {
row: r,
col: c,
id: id++,
comeFrom: [],
};
// Keep track of target and marbles
if (ch === '*') maze.target = cell;
if (ch === '.') maze.marbles.push(cell);
}
}
}
// Add neighbours
for (n = 0; n < maze.nodes.length; n++) {
cell = maze.nodes[n];
cell.neighbours = [
maze.nodesRowCol[cell.row-1][cell.col], /* north */
maze.nodesRowCol[cell.row][cell.col+1], /* east */
maze.nodesRowCol[cell.row+1][cell.col], /* south */
maze.nodesRowCol[cell.row][cell.col-1] /* west */
];
}
// Add marble moves in two steps
for (n = 0; n < maze.nodes.length; n++) {
cell = maze.nodes[n];
cell.moves = [
cell.neighbours[0] ? cell.neighbours[0].moves[0] : cell, /* north */
null,
null,
cell.neighbours[3] ? cell.neighbours[3].moves[3] : cell, /* west */
];
}
for (n = maze.nodes.length - 1; n >= 0; n--) {
cell = maze.nodes[n];
cell.moves[1] = cell.neighbours[1] ? cell.neighbours[1].moves[1] : cell; /* west */
cell.moves[2] = cell.neighbours[2] ? cell.neighbours[2].moves[2] : cell; /* south */
}
// add reverse-move ("marble can come from") data
for (n = maze.nodes.length - 1; n >= 0; n--) {
cell = maze.nodes[n];
for (m = 0; m < 4; m++) {
if (cell.moves[m] !== cell) cell.moves[m].comeFrom.push(cell);
}
}
return maze;
}
function setDistances(maze) {
var n, cell, distance, stack, newStack, i;
// clear distance information
for (n = 0; n < maze.nodes.length; n++) {
maze.nodes[n].distance = Number.POSITIVE_INFINITY;
}
// set initial distance
cell = maze.target;
cell.distance = distance = 0;
// BSF loop to set the distance for each cell that can be reached
stack = cell.comeFrom.slice();
while (stack.length) {
distance++;
newStack = [];
for (i = 0; i < stack.length; i++) {
cell = stack[i];
if (distance < cell.distance) {
cell.distance = distance;
newStack = newStack.concat(cell.comeFrom);
}
}
stack = newStack;
}
}
function evaluatedPosition(position, visited) {
// Assign heurstic cost to position
var m, ids;
position.cost = 0;
ids = []; // keep track of marble positions
for (m = 0; m < position.marbles.length; m++) {
// If mulitple marbles are at same cell, only account for that cell once.
// This will favour such positions:
if (ids[position.marbles[m].id] === undefined) {
// Make higher distances cost a lot, so that insentive
// is to improve the position of the worst placed marble
position.cost += Math.pow(position.marbles[m].distance, 2);
ids[position.marbles[m].id] = position.marbles[m].id;
}
}
// Assign some unique string, identifying the marble configuration
position.id = ids.join(',');
// If position was already visited before, give it the maximum cost
if (visited[position.id]) position.cost = Number.POSITIVE_INFINITY;
// Mark position as visited
visited[position.id] = 1;
return position;
}
function createMove(dir, marbles, visited) {
var m, movedMarbles;
movedMarbles = [];
for (m = 0; m < marbles.length; m++) {
movedMarbles[m] = marbles[m].moves[dir];
}
return evaluatedPosition({
dir: dir,
marbles: movedMarbles,
}, visited);
}
function solve(maze) {
var visited = {}; // nothing visited yet
function recurse (position) {
var ids, m, moves, i, path;
if (position.cost == 0) return []; // marbles are all on target spot.
if (!isFinite(position.cost)) return false; // no solution
// get move list
moves = [];
for (i = 0; i < 4; i++) {
moves[i] = createMove(i, position.marbles, visited);
}
// apply heuristic: sort the 4 moves by ascending cost
moves.sort(function (a,b) { return a.cost - b.cost });
for (i = 0; i < 4; i++) {
//console.log('=== move === ' + moves[i].dir);
path = recurse(moves[i]);
if (path !== false) return [moves[i].dir].concat(path);
}
return false; // no solution found
}
// Enrich initial position with cost, and start recursive search
return recurse(evaluatedPosition({
marbles: maze.marbles
}, visited));
}
// # = wall
// * = target
// . = marble
var mazeStr = `
###########
# # #*#
# # #.# .#
# #. #.# #
# # # ### #
# # #
###########
`.trim();
var maze = createMaze(mazeStr);
setDistances(maze);
console.log('#=wall, .=marble, *=target\n\n' + mazeStr);
var moves = solve(maze);
console.log('moves (0=north,1=east,2=south,3=west): ' + moves);
The found solution is not necessarily optimal. It performs an evaluation with depth 1. For better solutions, the algorithm could do an evaluation at greater depths.
The maze and allowed movements can be modelled as a deterministic finite automaton (DFA) on an alphabet of four symbols. Each cell in the maze is a DFA state, and cell x has a transition to cell y on symbol s whenever a ball at cell x would move to cell y when issued the command s.
The algorithm has three stages:
Construct a DFA consisting of only those states reachable by any ball in the maze, by some sequence of commands.
Find any synchronizing word for the DFA. A synchronizing word, or "reset word", is any sequence of symbols for which all initial states end at the same state. Note that we really only need a word which synchronizes all the initial states of the balls, not every state in the DFA.
Find a shortest sequence of moves from the reset word's end state to the target position in the maze. This can be done using any shortest-path algorithm, e.g. breadth-first search (BFS).
This needs some explanation.
Firstly, not every DFA has a reset word, but if the DFA constructed in step 1 has no reset word then, by definition, no sequence of commands can bring all balls to the same target cell. So this algorithm will solve every solvable instance of the problem.
Secondly, finding a minimum-length reset word is a hard problem which takes exponential time in the worst case. But the question says only that "the algorithm must be effective (better than randomly moving the balls)", so any reset word will do.
The simplest way to construct a reset word is probably using breadth-first search on the Cartesian product of the DFA with itself. For a DFA with n states, it takes O(n²) time to find a word which synchronizes two states; this must be repeated up to k - 1 times to synchronize the k initial states of the balls, giving an O(kn²) running time, and a reset word of length O(kn²).
Put in plainer language, the simplest form of this algorithm uses BFS to get two of the balls into the same place, then BFS again to get a third ball into the same place as those two, and so on, until all the balls are in the same place. Then it uses BFS to get them all to the target in unison. But the algorithm can be improved by plugging in a better reset-word-finding algorithm; generally, a reset word shorter than n² symbols should exist even in the worst case (this is believed but unproven), which is much better than kn².

Non recursive DFS algorithm for simple paths between two points

I am looking for a non-recursive Depth first search algorithm to find all simple paths between two points in undirected graphs (cycles are possible).
I checked many posts, all showed recursive algorithm.
seems no one interested in non-recursive version.
a recursive version is like this;
void dfs(Graph G, int v, int t)
{
path.push(v);
onPath[v] = true;
if (v == t)
{
print(path);
}
else
{
for (int w : G.adj(v))
{
if (!onPath[w])
dfs(G, w, t);
}
}
path.pop();
onPath[v] = false;
}
so, I tried it as (non-recursive), but when i check it, it computed wrong
void dfs(node start,node end)
{
stack m_stack=new stack();
m_stack.push(start);
while(!m_stack.empty)
{
var current= m_stack.pop();
path.push(current);
if (current == end)
{
print(path);
}
else
{
for ( node in adj(current))
{
if (!path.contain(node))
m_stack.push(node);
}
}
path.pop();
}
the test graph is:
(a,b),(b,a),
(b,c),(c,b),
(b,d),(d,b),
(c,f),(f,c),
(d,f),(f,d),
(f,h),(h,f).
it is undirected, that is why there are (a,b) and (b,a).
If the start and end nodes are 'a' and 'h', then there should be two simple paths:
a,b,c,f,h
a,b,d,f,h.
but that algorithm could not find both.
it displayed output as:
a,b,d,f,h,
a,b,d.
stack become at the start of second path, that is the problem.
please point out my mistake when changing it to non-recursive version.
your help will be appreciated!
I think dfs is a pretty complicated algorithm especially in its iterative form. The most important part of the iterative version is the insight, that in the recursive version not only the current node, but also the current neighbour, both are stored on the stack. With this in mind, in C++ the iterative version could look like:
//graph[i][j] stores the j-th neighbour of the node i
void dfs(size_t start, size_t end, const vector<vector<size_t> > &graph)
{
//initialize:
//remember the node (first) and the index of the next neighbour (second)
typedef pair<size_t, size_t> State;
stack<State> to_do_stack;
vector<size_t> path; //remembering the way
vector<bool> visited(graph.size(), false); //caching visited - no need for searching in the path-vector
//start in start!
to_do_stack.push(make_pair(start, 0));
visited[start]=true;
path.push_back(start);
while(!to_do_stack.empty())
{
State &current = to_do_stack.top();//current stays on the stack for the time being...
if (current.first == end || current.second == graph[current.first].size())//goal reached or done with neighbours?
{
if (current.first == end)
print(path);//found a way!
//backtrack:
visited[current.first]=false;//no longer considered visited
path.pop_back();//go a step back
to_do_stack.pop();//no need to explore further neighbours
}
else{//normal case: explore neighbours
size_t next=graph[current.first][current.second];
current.second++;//update the next neighbour in the stack!
if(!visited[next]){
//putting the neighbour on the todo-list
to_do_stack.push(make_pair(next, 0));
visited[next]=true;
path.push_back(next);
}
}
}
}
No warranty it is bug-free, but I hope you get the gist and at least it finds the both paths in your example.
The path computation is all wrong. You pop the last node before you process it's neighbors. Your code should output just the last node.
The simplest fix is to trust the compiler to optimize the recursive solution sufficiently that it won't matter. You can help by not passing large objects between calls and by avoiding allocating/deallocating many objects per call.
The easy fix is to store the entire path in the stack (instead of just the last node).
A harder fix is that you have 2 types of nodes on the stack. Insert and remove. When you reach a insert node x value you add first remove node x then push to the stack insert node y for all neighbours y. When you hit a remove node x you need to pop the last value (x) from the path. This better simulates the dynamics of the recursive solution.
A better fix is to just do breadth-first-search since that's easier to implement in an iterative fashion.

Calculating number of moves from top left corner to bottom right with move in any direction

I have a problem asked to me in an interview, this is a similar problem I found so I thought of asking here. The problem is
There is a robot situated at (1,1) in a N X N grid, the robot can move in any direction left, right ,up and down. Also I have been given an integer k, which denotes the maximum steps in the path. I had to calculate the number of possible ways to move from (1,1) to (N,N) in k or less steps.
I know how to solve simplified version of this problem, the one with moves possible in only right and down direction. That can be solved with Dynamic Programming. I tried applying the same technique here but I don't think it could be solved using 2-dimensional matrix, I tried a similar approach counting possible number of ways from left or up or right and summing up in down direction, but the problem is I don't know number of ways from down direction which should also be added. So I go in a loop. I was able to solve this problem using recursion, I could recurse on (N,N,k) call for up, left and k-1, sum them up but I think this is also not correct, and if it could be correct it has exponential complexity. I found problems similar to this so I wanted to know what would be a perfect approach for solving these types of problems.
Suppose you have an NxN matrix, where each cell gives you the number of ways to move from (1,1) to (i,j) in exactly k steps (some entries will be zero). You can now create an NxN matrix, where each cell gives you the number of ways to move from (1,1) to (i,j) in exactly k+1 steps - start off with the all-zero matrix, and then add in cell (i,j) of the previous matrix to cells (i+1, j), (i, j+1),... and so on.
The (N,N) entry in each of the k matrices gives you the number of ways to move from (1,1) to (i,j) in exactly k steps - all you have to do now is add them all together.
Here is an example for the 2x2 case, where steps outside the
matrix are not allowed, and (1,1) is at the top left.
In 0 steps, you can only get to the (1,1) cell:
1 0
0 0
There is one path to 1,1. From here you can go down or right,
so there are two different paths of length 1:
0 1
1 0
From the top right path you can go left or down, and from the
bottom left you can go right or up, so both cells have paths
that can be extended in two ways, and end up in the same two
cells. We add two copies of the following, one from each non-zero
cell
1 0
0 1
giving us these totals for paths of length two:
2 0
0 2
There are two choices from each of the non-empty cells again
so we have much the same as before for paths of length three.
0 4
4 0
Two features of this are easy checks:
1) For each length of path, only two cells are non-zero,
corresponding to the length of the path being odd or even.
2) The number of paths at each stage is a power of two, because
each path corresponds to a choice at each step as to whether to
go horizontally or vertically. (This only holds for this simple
2x2 case).
Update: This algorithm is incorrect. See the comments and mcdowella's answer. However, the corrected algorithm does not make a difference to the time complexity.
It can be done in O(k * N^2) time, at least. Pseudocode:
# grid[i,j] contains the number of ways we can get to i,j in at most n steps,
# where n is initially 0
grid := N by N array of 0s
grid[1,1] := 1
for n from 1 to k:
old := grid
for each cell i,j in grid:
# cells outside the grid considered 0 here
grid[i,j] := old[i,j] + old[i-1,j] + old[i+1,j] + old[i,j-1] + old[i,j+1]
return grid[N,N]
There might be an O(log k * (N*log N)^2) solution which is way more complex. Each iteration through the outer for loop is nothing but a convolution with a fixed kernel. So we can convolve the kernel with itself to get bigger kernels that fuse multiple iterations into one, and use FFT to compute the convolution.
Basically uniquepaths( row, column ) = 0 if row > N || column > N
1 if row ==N && column == N
uniquepaths(row+1, column) + uniquePaths(row, column+1)
i.e, the solution have optimal substructure and overlapped subproblems. So, it can be solved using Dynamic Programming. Below is memorization (lazy/on demand) version of it (related which basically returns paths as well: Algorithm for finding all paths in a NxN grid) (you may refer to my blog for more details: http://codingworkout.blogspot.com/2014/08/robot-in-grid-unique-paths.html)
private int GetUniquePaths_DP_Memoization_Lazy(int?[][] DP_Memoization_Lazy_Cache, int row,
int column)
{
int N = DP_Memoization_Lazy_Cache.Length - 1;
if (row > N)
{
return 0;
}
if (column > N)
{
return 0;
}
if(DP_Memoization_Lazy_Cache[row][column] != null)
{
return DP_Memoization_Lazy_Cache[row][column].Value;
}
if((row == N) && (column == N))
{
DP_Memoization_Lazy_Cache[N][N] = 1;
return 1;
}
int pathsWhenMovedDown = this.GetUniquePaths_DP_Memoization_Lazy(DP_Memoization_Lazy_Cache,
row + 1, column);
int pathsWhenMovedRight = this.GetUniquePaths_DP_Memoization_Lazy(DP_Memoization_Lazy_Cache,
row, column + 1);
DP_Memoization_Lazy_Cache[row][column] = pathsWhenMovedDown + pathsWhenMovedRight;
return DP_Memoization_Lazy_Cache[row][column].Value;
}
where the caller is
int GetUniquePaths_DP_Memoization_Lazy(int N)
{
int?[][] DP_Memoization_Lazy_Cache = new int?[N + 1][];
for(int i =0;i<=N;i++)
{
DP_Memoization_Lazy_Cache[i] = new int?[N + 1];
for(int j=0;j<=N;j++)
{
DP_Memoization_Lazy_Cache[i][j] = null;
}
}
this.GetUniquePaths_DP_Memoization_Lazy(DP_Memoization_Lazy_Cache, row: 1, column: 1);
return DP_Memoization_Lazy_Cache[1][1].Value;
}
Unit Tests
[TestCategory(Constants.DynamicProgramming)]
public void RobotInGridTests()
{
int p = this.GetNumberOfUniquePaths(3);
Assert.AreEqual(p, 6);
int p1 = this.GetUniquePaths_DP_Memoization_Lazy(3);
Assert.AreEqual(p, p1);
var p2 = this.GetUniquePaths(3);
Assert.AreEqual(p1, p2.Length);
foreach (var path in p2)
{
Debug.WriteLine("===================================================================");
foreach (Tuple<int, int> t in path)
{
Debug.Write(string.Format("({0}, {1}), ", t.Item1, t.Item2));
}
}
p = this.GetNumberOfUniquePaths(4);
Assert.AreEqual(p, 20);
p1 = this.GetUniquePaths_DP_Memoization_Lazy(4);
Assert.AreEqual(p, p1);
p2 = this.GetUniquePaths(4);
Assert.AreEqual(p1, p2.Length);
foreach (var path in p2)
{
Debug.WriteLine("===================================================================");
foreach (Tuple<int, int> t in path)
{
Debug.Write(string.Format("({0}, {1}), ", t.Item1, t.Item2));
}
}
}
There will be infinite no of ways. This is because you can form an infinite loop of positions and thus infinite possibilities. For ex:- You can move from (0,0) to (0,1) then to (1,1), then (1,0) and back again to (0,0). This forms a loop of positions and thus anyone can go round and round these types of loops and have infinite possibilities.

Resources