Adjacency Matrix In Java - java

I'm so confused by graphs and adjacency matrices. I'm doing an assignment for a class where I have a text file of nodes and a text file of edges and I have to read each of them and make them a graph onto which I can then perform operations such as determining if the graph is connected, finding a minimal spanning tree, traversals and finding paths. I've never worked with graphs before though, and I'm really confused by the whole thing, and I was wondering if someone could help explain some of this to me.
Firstly, do I build a graph on its own (with node and edges classes perhaps?) and then construct an adjacency matrix from that? Or is the adjacency matrix itself the graph?
And then I'm confused on how to implement the adjacent matrix into the program. The nodes are names things like "ND5" and "NR7" and so I would have to set and read the edges of [ND5][NR7] but I'm not sure how to set up a 2d array like that with strings for the outside and numbers on the inside.
I've been searching all over the internet and read through the whole chapter on graphs in my textbook, and I'm really not understanding just the first basic steps of getting this graph set up. I'd really appreciate the help. Thanks.

Firstly, do I build a graph on its own (with node and edges classes perhaps?) and then construct an adjacency matrix from that? Or is the adjacency matrix itself the graph?
There is no way anyone can answer that question for sure without actually reading the instructions for your assignment. However, unless the assignment specifically mentions Node and Edge classes or something, my guess is that you're just supposed to use the adjacency matrix to represent your graph.
And then I'm confused on how to implement the adjacent matrix into the program. The nodes are names things like "ND5" and "NR7" and so I would have to set and read the edges of [ND5][NR7] but I'm not sure how to set up a 2d array like that with strings for the outside and numbers on the inside.
I can totally understand your confusion here. What you actually want to do is create a bijection (a one-to-one relationship) between your node names and the indices of your matrix. For example, if you have n nodes in your graph, then you need an n×n matrix (i.e. new boolean[n][n]), and each of your nodes would correspond to a single integer in the range 0 until n (not inclusive of n).
I'm not sure what data structures you've covered in your class so far, but the easiest way to do this would probably be to use a Map<String, Integer>, which would let you look up a name like "ND5" and get back an integer (the index).
Another nice alternative might be to use an array. You could put all your node names into an array, sort it with Arrays.sort, and then once it's sorted you can use Arrays.binarySearch to find the index of a particular node name in that array. I think this solution is actually better than using a Map because it lets you do the lookups both ways. You use Arrays.binarySearch to do name-to-index lookups, and you just index into the array to do an index-to-name lookup.
Example: Let's say we have this graph:
Given that graph, here's some sample code of how you could do this: (warning! it's untested)
import java.util.Arrays;
// Add all your node names to an array
String[] nameLookup = new String[4];
nameLookup[0] = "A";
nameLookup[1] = "B";
nameLookup[2] = "C";
nameLookup[3] = "D";
// Our array is already properly sorted,
// but yours might not be, so you should sort it.
// (if it's not sorted then binarySearch won't work)
Arrays.sort(nameLookup);
// I'm assuming your edges are unweighted, so I use boolean.
// If you have weighted edges you should use int or double.
// true => connected, false => not connected
// (entries in boolean arrays default to false)
boolean[][] matrix = new boolean[4];
for (int i=0; i<matrix.length; i++) matrix[i] = new boolean[4];
// I don't want to call Arrays.binarySearch every time I want an index,
// so I'm going to cache the indices here in some named variables.
int nodeA = Arrays.binarySearch(nameLookup, "A");
int nodeB = Arrays.binarySearch(nameLookup, "B");
int nodeC = Arrays.binarySearch(nameLookup, "C");
int nodeD = Arrays.binarySearch(nameLookup, "D");
// I'm assuming your edges are undirected.
// If the edges are directed then the entries needn't be semmetric.
// A is connected to B
matrix[nodeA][nodeB] = true;
matrix[nodeB][nodeA] = true;
// A is connected to D
matrix[nodeA][nodeD] = true;
matrix[nodeD][nodeA] = true;
// B is connected to D
matrix[nodeB][nodeD] = true;
matrix[nodeD][nodeB] = true;
// C is connected to D
matrix[nodeC][nodeD] = true;
matrix[nodeD][nodeC] = true;
// Check if node X is connected to node Y
int nodeX = Arrays.binarySearch(nameLookup, stringNameOfX);
int nodeY = Arrays.binarySearch(nameLookup, stringNameOfY);
if (matrix[nodeX][nodeY]) { /* They're connected */ }
// Print all of node Z's neighbors' names
int nodeZ = Arrays.binarySearch(nameLookup, stringNameOfZ);
for (int i=0; i<matrix.length; i++) {
if (matrix[nodeZ][i]) {
System.out.println(nameLookup[nodeZ] + " is connected to " + nameLookup[i]);
}
}

Related

Really slow Dijkstra algorithm, what am I doing wrong?

I was tasked to perform the Dijkstra Algorithm on big graphs (25 million nodes). These are represented as a 2D array: -each node as a double[] with latitude, longitude and offset (offset meaning index of the first outgoing edge of that node)
-each edge as a int[] with sourceNodeId,targetNodeId and weight of that edge
Below is the code, I used int[] as a tupel for the comparison in the priority queue.
The algorithm is working and gets the right results HOWEVER it is required to be finished in 15s but takes like 8min on my laptop. Is my algorithm fundamentally slow? Am I using the wrong data structures? Am I missing something? I tried my best optimizing as far as I saw fit.
Any help or any ideas would be greatly appreciated <3
public static int[] oneToAllArray(double[][]nodeList, int[][]edgeList,int sourceNodeId) {
int[] distance = new int[nodeList[0].length]; //the array that will be returned
//the priorityQueue will use arrays with the length 2, representing [index, weight] for each node and order them by their weight
PriorityQueue<int[]> prioQueue = new PriorityQueue<>((a, b) -> ((int[])a)[1] - ((int[])b)[1]);
int offset1; //used for determining the amount of outgoing edges
int offset2;
int newWeight; //declared here so we dont need to declare it a lot of times later (not sure if that makes a difference)
//currentSourceNode here means the node that will be looked at for OUTGOING edges
int[] currentSourceNode= {sourceNodeId,0};
prioQueue.add(currentSourceNode);
//at the start we only add the sourceNode, then we start the actual algorithm
while(!prioQueue.isEmpty()) {
if(prioQueue.size() % 55 == 2) {
System.out.println(prioQueue.size());
}
currentSourceNode=prioQueue.poll();
int sourceIndex = currentSourceNode[0];
if(sourceIndex == nodeList[0].length-1) {
offset1= (int) nodeList[2][sourceIndex];
offset2= edgeList[0].length;
} else {
offset1= (int) nodeList[2][sourceIndex];
offset2= (int) nodeList[2][sourceIndex+1];
}
//checking every outgoing edge for the currentNode
for(int i=offset1;i<offset2;i++) {
int targetIndex = edgeList[1][i];
//if the node hasnt been looked at yet, the weight is just the weight of this edge + distance to sourceNode
if(distance[targetIndex]==0&&targetIndex!=sourceNodeId) {
distance[targetIndex] = distance[sourceIndex] + edgeList[2][i];
int[]targetArray = {targetIndex, distance[targetIndex]};
prioQueue.add(targetArray);
} else if(prioQueue.stream().anyMatch(e -> e[0]==targetIndex)) {
//above else if checks if this index is already in the prioQueue
newWeight=distance[sourceIndex]+edgeList[2][i];
//if new weight is better, we have to update the distance + the prio queue
if(newWeight<distance[targetIndex]) {
distance[targetIndex]=newWeight;
int[] targetArray;
targetArray=prioQueue.stream().filter(e->e[0]==targetIndex).toList().get(0);
prioQueue.remove(targetArray);
targetArray[1]=newWeight;
prioQueue.add(targetArray);
}
}
}
}
return distance;
}
For each node that you process, you are doing a linear scan of the priority queue to see if something is already queued, and a second scan to find all the things that are queued if you have to update the distance. Instead, keep a separate multi-set of things that are in the queue.
This is not a proper Dijkstra's implementation.
One of the key elements of Dijkstra is that you mark nodes as "visited" when they have been evaluated and prevent looking at them again because you can't do any better. You are not doing that, so your algorithm is doing many many more computations than necessary. The only place where a priority queue or sort is required is to pick the next node to visit, from amongst the unvisited. You should re-read the algorithm, implement the "visitation tracking" and re-formulate.

Connecting nodes in a matrix for a graph

Hi I'm working on this little project which requires me to build a matrix in Java which resembles a chess board. I'm supposed to get the Knight to get from a point to another(In the way Knight moves). So I need to find the shortest way to get there in the end.
My problem is, I can't get to connect the edges to get to that point. I can find out if the vertex is a valid move but I can't seem to find a way to create nodes to get to that point. For Example,
0 XXXXX
1 XXXOX
2 XXXXX
3 XXKXX
4 XXXXX
5 XXXXX
I need to create nodes that connect K to O to find out shortest distance later.
PS. I'll be okay with just hints of how to get there or just some tips. Don't really need the exact code. Thank you very much!
I know it's a bad representation of matrix up there but spare me the critique please
A classic Breadth-First-Search is probably the simplest approach:
class Location {
int x;
int y;
List<Location> adjacent() {
// TODO return list of locations reachable in a single step
}
}
List<Location> findShortestPath(Location start, Location destination) {
Location[][] previous = new Location[8][8];
Deque<Location> queue = new ArrayDeque<>();
queue.add(start);
do {
Location loc = queue.poll();
for (Location n : loc.neighbors()) {
if (previous[n.x][n.y] == null) {
previous[n.x][n.y] = loc;
queue.add(n);
if (n.x == destination.x && n.y == destination.y) {
// we've found a way, let's reconstruct the list of steps
List<Location> path = new ArrayList<>();
for (Location l = n; l != start; l = previous[l.x][l.y]) {
path.add(l);
}
path.reverse();
return path;
}
}
}
} while (!queue.isEmpty());
return null; // no path exists
}
This code enumerates all paths from the start location. Therefore, if there is a path to destination, it will find it. In addition, because paths are enumerated in order or ascending length, the first such path will be a shortest one.
The chess board can be implemented by a 2d array. Each cell in the matrix can be considered to be a node (or vertex) in the graph. Edge is composed of two nodes (in this case two cells) one being the from or source [ lets call it Nod A] and other being the to or neighbor or destination node [ Lets call it node B].
Edge exits if there is a possibility of moving from node A to node B.
You can use Dijkstra's algorithm.
http://krishnalearnings.blogspot.in/2015/07/implementation-in-java-for-dijkstras.html
For Node with the Knight's position you can see the possibilities of the cells where Knight can move to and add in the Min Heap. The weight of each edge is constant. You just need to update the cost of the Node.

How to hard-code legal moves for fast lookup?

I have created a gameboard (5x5) and I now want to decide when a move is legal as fast as possible. For example a piece at (0,0) wants to go to (1,1), is that legal? First I tried to find this out with computations but that seemed bothersome. I would like to hard-code the possible moves based on a position on the board and then iterate through all the possible moves to see if they match the destinations of the piece. I have problems getting this on paper. This is what I would like:
//game piece is at 0,0 now, decide if 1,1 is legal
Point destination = new Point(1,1);
destination.findIn(legalMoves[0][0]);
The first problem I face is that I don't know how to put a list of possible moves in an array at for example index [0][0]. This must be fairly obvious but I am stuck at this for some time. I would like to create an array in which there is a list of Point objects. So in semi-code: legalMoves[0][0] = {Point(1,1),Point(0,1),Point(1,0)}
I am not sure if this is efficient but it makes logically move sense than maybe [[1,1],[0,1],[1,0]] but I am not sold on this.
The second problem I have is that instead of creating the object at every start of the game with an instance variable legalMoves, I would rather have it read from disk. I think that it should be quicker this way? Is the serializable class the way to go?
My 3rd small problem is that for the 25 positions the legal moves are unbalanced. Some have 8 possible legal moves, others have 3. Maybe this is not a problem at all.
You are looking for a structure that will give you the candidate for a given point, i.e. Point -> List<Point>.
Typically, I would go for a Map<Point, List<Point>>.
You can initialise this structure statically at program start or dynamically when needing. For instance, here I use 2 helpers arrays that contains the possible translations from a point, and these will yield the neighbours of the point.
// (-1 1) (0 1) (1 1)
// (-1 0) (----) (1 0)
// (-1 -1) (0 -1) (1 -1)
// from (1 0) anti-clockwise:
static int[] xOffset = {1,1,0,-1,-1,-1,0,1};
static int[] yOffset = {0,1,1,1,0,-1,-1,-1};
The following Map contains the actual neighbours for a Point with a function that compute, store and return these neighbours. You can choose to initialise all neighbours in one pass, but given the small numbers, I would not think this a problem performance wise.
static Map<Point, List<Point>> neighbours = new HashMap<>();
static List<Point> getNeighbours(Point a) {
List<Point> nb = neighbours.get(a);
if (nb == null) {
nb = new ArrayList<>(xOffset.length); // size the list
for (int i=0; i < xOffset.length; i++) {
int x = a.getX() + xOffset[i];
int y = a.getY() + yOffset[i];
if (x>=0 && y>=0 && x < 5 && y < 5) {
nb.add(new Point(x, y));
}
}
neighbours.put(a, nb);
}
return nb;
}
Now checking a legal move is a matter of finding the point in the neighbours:
static boolean isLegalMove(Point from, Point to) {
boolean legal = false;
for (Point p : getNeighbours(from)) {
if (p.equals(to)) {
legal = true;
break;
}
}
return legal;
}
Note: the class Point must define equals() and hashCode() for the map to behave as expected.
The first problem I face is that I don't know how to put a list of possible moves in an array at for example index [0][0]
Since the board is 2D, and the number of legal moves could generally be more than one, you would end up with a 3D data structure:
Point legalMoves[][][] = new legalMoves[5][5][];
legalMoves[0][0] = new Point[] {Point(1,1),Point(0,1),Point(1,0)};
instead of creating the object at every start of the game with an instance variable legalMoves, I would rather have it read from disk. I think that it should be quicker this way? Is the serializable class the way to go?
This cannot be answered without profiling. I cannot imagine that computing legal moves of any kind for a 5x5 board could be so intense computationally as to justify any kind of additional I/O operation.
for the 25 positions the legal moves are unbalanced. Some have 8 possible legal moves, others have 3. Maybe this is not a problem at all.
This can be handled nicely with a 3D "jagged array" described above, so it is not a problem at all.

Best way to put 20 elements in a coordinate system with neighbouring elements unique

This question is regarding libGDX, but I think it's in fact more Java/algorithm related.
Part of my game includes placing 20 elements out of predefined 30 elements list on a screen (so effectively a coordinate system) in 20 partially-predefined places.
By partially predefined I mean that they are predefined for each screen, but there can be dozens of screens, so they can be as well treated as random.
The elements will be selected randomly, but the elements close to each other must be unique. By close I mean in range of some arbitrary defined distance X. Effectively each place will have around 3 'close neightbours'.
The best way I can think of so far is as follows:
Calculate the distance between all places. If a given distance between A and B is lower than X put two entries in a map - one (A,B) and one (B,A)
Now start filling the places with elements
For each place create a list with all neightbours using the map from point 1 (let's call it N-list)
For each place create a temporary list with all possible (30) elements (let's call it E-list)
Get a random element from E-list
Iterate through N-list. For each place from the list get an element currently there (if there's any). For this a (place, element) map is needed, so it will be filled as the algorithm progresses.
If the found element is equal to the current random element remove this element from E-list and this place from N-list and come back to point 5
Proceed until all places are filled
Step 1 is in fact a separate algorithm, that probably can be tweaked, ex. if we calculated the A->B distance we don't need to calculate B->A, but that needs an additional map to store calculation info, etc.
I would like to know what you think of this way and if you have any ideas for a better one.
Thanks in advance for your answers.
P.S. Perhaps the terms I used could be better chosen, but I'm not a native speaker and I don't know English math terms :-)
Ok, I think I understood your solution and this is what I thought of initially. But I think it can be slightly optimized by eliminating extra pairs and maps (or maybe not :)
First, create a map of locations where key is location position (or the location itself) and value is a list of location's parents who fall within the close range. Yes it will have multiple parents, not children, it is actually the same but parents are more fitting here as we'll see.
ArrayList<Place> place_list; // your list of places here
ArrayList<Element> element_list; // your list of elements here
HashMap<Place,ArrayList<Place>> parent_map = new HashMap<Place,ArrayList<Place>>;
ArrayList<Place> a;
for (int i = 0; i < place_list.size() - 1; i++) {
Place place1 = place_list.get(i);
for (int j = i + 1; j < place_list.size(); j++) {
Place place2 = place_list.get(j);
int dist = getDistance(place1, place2);
if (dist > DISTANCE_THRESHOLD) continue;
// if this place is within range,
// add parent place to its list and put/update it to the map
a = parent_map.get(place2);
if (a == null) a = new ArrayList<Place>();
a.add(place1);
parent_map.put(place2, a);
}
}
Now we have a map of all places that have parents. Next we do the following: if place does not have parents, it can choose any random element freely. If it does have parents, it checks what elements parents own and reduces the available set of elements. After the set was reduced, any random element can be chosen from it.
HashMap<Place,Element> used_place_map = new HashMap<Place,Element>(); // key is place, value is assigned element
ArrayList<Element> tmp_element_list;
for (i = 0; i < place_list.size(); i++) {
Place place = place_list.get(i);
a = parent_map.get(place);
if (a == null) { // this place has no parents, use elements freely
tmp_element_list = element_list;
} else { // if it has parents, they have already registered their elements in used_place_map
tmp_element_list = new ArrayList<Element>();
// create list of available elements, lame
for (j = 0; j < element_list.size(); j++) tmp_element_list.add(element_list.get(j));
// now reduce it, very lame, sorry
for (Place pl : a) {
Element used_element = used_place_map.get(pl);
for (j = 0; j < tmp_element_list.size(); j++) {
if (used_element.equals(tmp_element_list.get(j)) {
tmp_element_list.remove(j);
break;
}
}
}
}
// finally, get the random index on (probably reduced) array
int element_id = Random.nextInt(tmp_element_list.size());
Element element = element_list.get(element_id);
// store our choice as future parent
used_place_map.put(place, element);
}

Efficient algorithm to find all the paths from A to Z?

With a set of random inputs like this (20k lines):
A B
U Z
B A
A C
Z A
K Z
A Q
D A
U K
P U
U P
B Y
Y R
Y U
C R
R Q
A D
Q Z
Find all the paths from A to Z.
A - B - Y - R - Q - Z
A - B - Y - U - Z
A - C - R - Q - Z
A - Q - Z
A - B - Y - U - K - Z
A location cannot appear more than once in the path, hence A - B - Y - U - P - U - Z is not valid.
Locations are named AAA to ZZZ (presented here as A - Z for simplicity) and the input is random in such a way that there may or may not be a location ABC, all locations may be XXX (unlikely), or there may not be a possible path at all locations are "isolated".
Initially I'd thought that this is a variation of the unweighted shortest path problem, but I find it rather different and I'm not sure how does the algorithm there apply here.
My current solution goes like this:
Pre-process the list such that we have a hashmap which points a location (left), to a list of locations (right)
Create a hashmap to keep track of "visited locations". Create a list to store "found paths".
Store X (starting-location) to the "visited locations" hashmap.
Search for X in the first hashmap, (Location A will give us (B, C, Q) in O(1) time).
For-each found location (B, C, Q), check if it is the final destination (Z). If so store it in the "found paths" list. Else if it doesn't already exist in "visited locations" hashmap, Recurl to step 3 now with that location as "X". (actual code below)
With this current solution, it takes forever to map all (not shortest) possible routes from "BKI" to "SIN" for this provided data.
I was wondering if there's a more effective (time-wise) way of doing it. Does anyone know of a better algorithm to find all the paths from an arbitrary position A to an arbitrary position Z ?
Actual Code for current solution:
import java.util.*;
import java.io.*;
public class Test {
private static HashMap<String, List<String>> left_map_rights;
public static void main(String args[]) throws Exception {
left_map_rights = new HashMap<>();
BufferedReader r = new BufferedReader(new FileReader("routes.text"));
String line;
HashMap<String, Void> lines = new HashMap<>();
while ((line = r.readLine()) != null) {
if (lines.containsKey(line)) { // ensure no duplicate lines
continue;
}
lines.put(line, null);
int space_location = line.indexOf(' ');
String left = line.substring(0, space_location);
String right = line.substring(space_location + 1);
if(left.equals(right)){ // rejects entries whereby left = right
continue;
}
List<String> rights = left_map_rights.get(left);
if (rights == null) {
rights = new ArrayList<String>();
left_map_rights.put(left, rights);
}
rights.add(right);
}
r.close();
System.out.println("start");
List<List<String>> routes = GetAllRoutes("BKI", "SIN");
System.out.println("end");
for (List<String> route : routes) {
System.out.println(route);
}
}
public static List<List<String>> GetAllRoutes(String start, String end) {
List<List<String>> routes = new ArrayList<>();
List<String> rights = left_map_rights.get(start);
if (rights != null) {
for (String right : rights) {
List<String> route = new ArrayList<>();
route.add(start);
route.add(right);
Chain(routes, route, right, end);
}
}
return routes;
}
public static void Chain(List<List<String>> routes, List<String> route, String right_most_currently, String end) {
if (right_most_currently.equals(end)) {
routes.add(route);
return;
}
List<String> rights = left_map_rights.get(right_most_currently);
if (rights != null) {
for (String right : rights) {
if (!route.contains(right)) {
List<String> new_route = new ArrayList<String>(route);
new_route.add(right);
Chain(routes, new_route, right, end);
}
}
}
}
}
As I understand your question, Dijkstras algorithm cannot be applied as is, since shortest path problem per definition finds a single path in a set of all possible paths. Your task is to find all paths per-se.
Many optimizations on Dijkstras algorithm involve cutting off search trees with higher costs. You won't be able to cut off those parts in your search, as you need all findings.
And I assume you mean all paths excluding circles.
Algorithm:
Pump network into a 2dim array 26x26 of boolean/integer. fromTo[i,j].
Set a 1/true for an existing link.
Starting from the first node trace all following nodes (search links for 1/true).
Keep visited nodes in a some structure (array/list). Since maximal
depth seems to be 26, this should be possible via recursion.
And as #soulcheck has written below, you may think about cutting of paths you have aleady visted. You may keep a list of paths towards the destination in each element of the array. Adjust the breaking condition accordingly.
Break when
visiting the end node (store the result)
when visiting a node that has been visted before (circle)
visiting a node for which you have already found all paths to the destination and merge your current path with all the existing ones from that node.
Performance wise I'd vote against using hashmaps and lists and prefer static structures.
Hmm, while re-reading the question, I realized that the name of the nodes cannot be limited to A-Z. You are writing something about 20k lines, with 26 letters, a fully connected A-Z network would require far less links. Maybe you skip recursion and static structures :)
Ok, with valid names from AAA to ZZZ an array would become far too large. So you better create a dynamic structure for the network as well. Counter question: regarding performance, what is the best data structure for a less popuplate array as my algorithm would require? I' vote for an 2 dim ArrayList. Anyone?
What you're proposing is a scheme for DFS, only with backtracking.It's correct, unless you want to permit cyclic paths (you didn't specify if you do).
There are two gotchas, though.
You have to keep an eye on nodes you already visited on current path (to eliminate cycles)
You have to know how to select next node when backtracking, so that you don't descend on the same subtree in the graph when you already visited it on the current path.
The pseudocode is more or less as follows:
getPaths(A, current_path) :
if (A is destination node): return [current_path]
for B = next-not-visited-neighbor(A) :
if (not B already on current path)
result = result + getPaths(B, current_path + B)
return result
list_of_paths = getPaths(A, [A])
which is almost what you said.
Be careful though, as finding all paths in complete graph is pretty time and memory consuming.
edit
For clarification, the algorithm has Ω(n!) time complexity in worst case, as it has to list all paths from one vertex to another in complete graph of size n, and there are at least (n-2)! paths of form <A, permutations of all nodes except A and Z, Z>. No way to make it better if only listing the result would take as much.
Your data is essentially an adjacency list which allows you to construct a tree rooted at the node corresponding to A. In order to obtain all the paths between A & Z, you can run any tree traversal algorithm.
Of course, when you're building the tree you have to ensure that you don't introduce cycles.
I would proceed recursively where I would build a list of all possible paths between all pairs of nodes.
I would start by building, for all pairs (X, Y), the list L_2(X, Y) which is the list of paths of length 2 that go from X to Y; that's trivial to build since that's the input list you are given.
Then I would build the lists L_3(X, Y), recursively, using the known lists L_2(X, Z) and L_2(Z, Y), looping over Z. For example, for (C, Q), you have to try all Z in L_2(C, Z) and L_2(Z, Q) and in this case Z can only be R and you get L_3(C, Q) = {C -> R -> Q}. For other pairs, you might have an empty L_3(X, Y), or there could be many paths of length 3 from X to Y.
However you have to be careful here when building the paths here since some of them must be rejected because they have cycles. If a path has twice the same node, it is rejected.
Then you build L_4(X, Y) for all pairs by combining all paths L_2(X, Z) and L_3(Z, Y) while looping over all possible values for Z. You still remove paths with cycles.
And so on... until you get to L_17576(X, Y).
One worry with this method is that you might run out of memory to store those lists. Note however that after having computed the L_4's, you can get rid of the L_3's, etc. Of course you don't want to delete L_3(A, Z) since those paths are valid paths from A to Z.
Implementation detail: you could put L_3(X, Y) in a 17576 x 17576 array, where the element at (X, Y) is is some structure that stores all paths between (X, Y). However if most elements are empty (no paths), you could use instead a HashMap<Pair, Set<Path>>, where Pair is just some object that stores (X, Y). It's not clear to me if most elements of L_3(X, Y) are empty, and if so, if it is also the case for L_4334(X, Y).
Thanks to #Lie Ryan for pointing out this identical question on mathoverflow. My solution is basically the one by MRA; Huang claims it's not valid, but by removing the paths with duplicate nodes, I think my solution is fine.
I guess my solution needs less computations than the brute force approach, however it requires more memory. So much so that I'm not even sure it is possible on a computer with a reasonable amount of memory.

Categories