Here is my current code:
#define uint uint32_t // unsigned integer
#define sint int32_t // signed integer
// Declaration of the bellman_ford function
int bellman_ford(sint links[][3], uint links_size, uint s, int64_t *dist, uint *path, uint nb_nodes);
int bellman_ford(sint links[][3], uint links_size, uint s, int64_t
*dist, uint *path, uint nb_nodes)
/**
* Computes the shortest path between a given source node and all other nodes in a weighted graph.
*
* Arguments:
* links: A 2D array of integers representing the links between nodes. Each row represents a link and contains the indices of the two nodes and the cost of the link.
* links_size: The number of links in the links array.
* s: The index of the starting node.
* dist: An array of size nb_nodes to store the shortest distances from each node to s.
* path: An array of size nb_nodes to store the shortest paths from each node to s.
* nb_nodes: The total number of nodes in the graph.
*
* Returns:
* 0 if the computation succeeded, 1 if a negative cycle was detected in the graph.
**/ {
int updates;
// Initialize dist and path arrays with memset
memset(dist, 0x7f, nb_nodes * sizeof(int64_t));
memset(path, -1, nb_nodes * sizeof(sint));
dist[s] = 0;
// Perform nb_nodes - 1 iterations of Bellman-Ford algorithm
for (uint _ = 0; _ < nb_nodes - 1; _++)
{
updates = 0;
// For each link in the links array, attempt to update shortest distances for each node.
for (uint j = 0; j < links_size; j++)
{
uint node_from = links[j][0];
uint node_to = links[j][1];
int64_t cost = links[j][2];
// If a shorter distance is found, store it in the dist array and update the path array
if ((dist[node_from] != INT64_MAX) && (dist[node_from] + cost < dist[node_to]))
{
dist[node_to] = dist[node_from] + cost;
path[node_to] = node_from;
updates++;
}
}
// If no update was made during the previous iteration, exit the loop
if (updates == 0)
{
break;
}
}
// Check for negative cycle in the graph
for (uint k = 0; k < links_size; k++)
{
uint node_from = links[k][0];
uint node_to = links[k][1];
int64_t cost = links[k][2];
// If a shorter distance is still found, there is a negative cycle
if ((dist[node_from] != INT64_MAX) && (dist[node_from] + cost < dist[node_to]))
{
printf("Negative cycle detected\n");
return 1;
}
}
return 0;}
I would like to optimize performance as much as possible, with the limitation that the code should remain readable and fluid and the algorithm should not become overly complicated. Also, it is important to keep the same input and output data as now, the code must detect negative cycles and cover corner cases.
I have read interesting stuff which I'd like to implement, (full descriptions are here: https://en.wikipedia.org/wiki/Shortest_path_faster_algorithm)
The basic idea of SPFA is the same as the Bellman-Ford algorithm in that each vertex is used as a candidate to relax its adjacent vertices. The improvement over the latter is that instead of trying all vertices blindly, SPFA maintains a queue of candidate vertices and adds a vertex to the queue only if that vertex is relaxed. This process repeats until no more vertex can be relaxed.
The performance of the algorithm is strongly determined by the order in which candidate vertices are used to relax other vertices. In fact, if Q is a priority queue, then the algorithm pretty much resembles Dijkstra's. However, since a priority queue is not used here, two techniques are sometimes employed to improve the quality of the queue, which in turn improves the average-case performance (but not the worst-case performance). Both techniques rearrange the order of elements in Q so that vertices closer to the source are processed first. Therefore, when implementing these techniques, Q is no longer a first-in, first-out queue, but rather a normal doubly linked list or double-ended queue.
Also, I have read that binary heap and Fibonacci heap might be interesting structures to keep element ordered. Is it a good idea to investigate this direction? (wouldn't it be even better than the trick with the queue they talk about?)
More generally, what would you advise?
EDIT: This post has a continuation here: Bellman-Ford optimisation in C with Shortest Path Algorithm (SPFA) and Small Label first (SLF)
#define
s likeuint
andsint
and variable names like_
are good for readability? \$\endgroup\$