-
-
Notifications
You must be signed in to change notification settings - Fork 411
Added the implementation of the Edmond Karp algorithm along with test cases #252
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 7 commits
9b477f1
a358ea6
b560c2e
13ba5a3
ab770c3
6fed73d
b5daf9f
4b5f2b5
76dd412
2aa4555
f97e200
97adbc8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
/** | ||
* @function edmondsKarp | ||
* @description Compute the maximum flow from a source node to a sink node using the Edmonds-Karp algorithm. | ||
* @Complexity_Analysis | ||
* Time complexity: O(V * E^2) where V is the number of vertices and E is the number of edges. | ||
* Space Complexity: O(E) due to residual graph representation. | ||
* @param {[number, number][][]} graph - The graph in adjacency list form. | ||
* @param {number} source - The source node. | ||
* @param {number} sink - The sink node. | ||
* @return {number} - The maximum flow from the source node to the sink node. | ||
* @see https://en.wikipedia.org/wiki/Edmonds%E2%80%93Karp_algorithm | ||
*/ | ||
|
||
export default function edmondsKarp( | ||
graph: [number, number][][], | ||
source: number, | ||
sink: number | ||
): number { | ||
const n = graph.length | ||
|
||
// Initialize residual graph | ||
const residualGraph: [number, number][][] = Array.from( | ||
{ length: n }, | ||
() => [] | ||
) | ||
|
||
// Build residual graph from the original graph | ||
for (let u = 0; u < n; u++) { | ||
for (const [v, cap] of graph[u]) { | ||
if (cap > 0) { | ||
residualGraph[u].push([v, cap]) // Forward edge | ||
residualGraph[v].push([u, 0]) // Reverse edge with 0 capacity | ||
} | ||
} | ||
} | ||
|
||
const findAugmentingPath = (parent: (number | null)[]): number => { | ||
const visited = Array(n).fill(false) | ||
const queue: number[] = [] | ||
queue.push(source) | ||
visited[source] = true | ||
parent[source] = null | ||
|
||
while (queue.length > 0) { | ||
const u = queue.shift()! | ||
for (const [v, cap] of residualGraph[u]) { | ||
if (!visited[v] && cap > 0) { | ||
parent[v] = u | ||
visited[v] = true | ||
if (v === sink) { | ||
// Return the bottleneck capacity along the path | ||
let pathFlow = Infinity | ||
let current = v | ||
while (parent[current] !== null) { | ||
const prev = parent[current]! | ||
const edgeCap = residualGraph[prev].find( | ||
([node]) => node === current | ||
)![1] | ||
mapcrafter2048 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
pathFlow = Math.min(pathFlow, edgeCap) | ||
current = prev | ||
} | ||
return pathFlow | ||
} | ||
queue.push(v) | ||
} | ||
} | ||
} | ||
return 0 | ||
} | ||
|
||
let maxFlow = 0 | ||
const parent = Array(n).fill(null) | ||
|
||
while (true) { | ||
const pathFlow = findAugmentingPath(parent) | ||
if (pathFlow === 0) break // No augmenting path found | ||
|
||
// Update the capacities and reverse capacities in the residual graph | ||
let v = sink | ||
while (parent[v] !== null) { | ||
const u = parent[v]! | ||
// Update capacity of the forward edge | ||
const forwardEdge = residualGraph[u].find(([node]) => node === v)! | ||
forwardEdge[1] -= pathFlow | ||
// Update capacity of the reverse edge | ||
const reverseEdge = residualGraph[v].find(([node]) => node === u)! | ||
reverseEdge[1] += pathFlow | ||
|
||
v = u | ||
} | ||
|
||
maxFlow += pathFlow | ||
} | ||
|
||
return maxFlow | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import edmondsKarp from '../edmonds_karp' | ||
|
||
describe('Edmonds-Karp Algorithm', () => { | ||
it('should find the maximum flow in a simple graph', () => { | ||
const graph: [number, number][][] = [ | ||
[ | ||
[1, 3], | ||
[2, 2] | ||
], // Node 0: Edges to node 1 (capacity 3), and node 2 (capacity 2) | ||
[[3, 2]], // Node 1: Edge to node 3 (capacity 2) | ||
[[3, 3]], // Node 2: Edge to node 3 (capacity 3) | ||
[] // Node 3: No outgoing edges | ||
] | ||
const source = 0 | ||
const sink = 3 | ||
const maxFlow = edmondsKarp(graph, source, sink) | ||
expect(maxFlow).toBe(4) | ||
}) | ||
|
||
it('should find the maximum flow in a more complex graph', () => { | ||
const graph: [number, number][][] = [ | ||
[ | ||
[1, 10], | ||
[2, 10] | ||
], // Node 0: Edges to node 1 and node 2 (both capacity 10) | ||
[ | ||
[3, 4], | ||
[4, 8] | ||
], // Node 1: Edges to node 3 (capacity 4), and node 4 (capacity 8) | ||
[[4, 9]], // Node 2: Edge to node 4 (capacity 9) | ||
[[5, 10]], // Node 3: Edge to node 5 (capacity 10) | ||
[[5, 10]], // Node 4: Edge to node 5 (capacity 10) | ||
[] // Node 5: No outgoing edges (sink) | ||
] | ||
const source = 0 | ||
const sink = 5 | ||
const maxFlow = edmondsKarp(graph, source, sink) | ||
expect(maxFlow).toBe(14) | ||
}) | ||
|
||
it('should return 0 when there is no path from source to sink', () => { | ||
const graph: [number, number][][] = [ | ||
[], // Node 0: No outgoing edges | ||
[], // Node 1: No outgoing edges | ||
[] // Node 2: No outgoing edges (sink) | ||
] | ||
const source = 0 | ||
const sink = 2 | ||
const maxFlow = edmondsKarp(graph, source, sink) | ||
expect(maxFlow).toBe(0) | ||
}) | ||
|
||
it('should handle graphs with no edges', () => { | ||
const graph: [number, number][][] = [ | ||
[], // Node 0: No outgoing edges | ||
[], // Node 1: No outgoing edges | ||
[] // Node 2: No outgoing edges | ||
] | ||
const source = 0 | ||
const sink = 2 | ||
const maxFlow = edmondsKarp(graph, source, sink) | ||
expect(maxFlow).toBe(0) | ||
}) | ||
|
||
it('should handle graphs with self-loops', () => { | ||
const graph: [number, number][][] = [ | ||
[ | ||
[0, 10], | ||
[1, 10] | ||
], // Node 0: Self-loop with capacity 10, and edge to node 1 (capacity 10) | ||
[ | ||
[1, 10], | ||
[2, 10] | ||
], // Node 1: Self-loop and edge to node 2 | ||
[] // Node 2: No outgoing edges (sink) | ||
] | ||
const source = 0 | ||
const sink = 2 | ||
const maxFlow = edmondsKarp(graph, source, sink) | ||
expect(maxFlow).toBe(10) | ||
}) | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
/** | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please get rid of these unrelated changes in this PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm afraid this isn't resolved. The changes are still there. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Still there. Maybe you forgot to push a commit that gets rid of these additions ( |
||
* @function bisectionMethod | ||
* @description Bisection method is a root-finding method that applies to any continuous function for which one knows two values with opposite signs. | ||
* @param {number} a - The first value | ||
* @param {number} b - The second value | ||
* @param {number} e - The error value | ||
* @param {Function} f - The function | ||
* @return {number} - The root of the function | ||
* @see [BisectionMethod](https://en.wikipedia.org/wiki/Bisection_method) | ||
* @example bisectionMethod(1, 2, 0.01, (x) => x**2 - 2) = 1.4140625 | ||
* @example bisectionMethod(1, 2, 0.01, (x) => x**2 - 3) = 1.732421875 | ||
*/ | ||
|
||
export const bisectionMethod = (a: number, b: number, e: number, f: Function): number => { | ||
let c = a | ||
while ((b - a) >= e) { | ||
c = (a + b) / 2 | ||
if (f(c) === 0.0) { | ||
break | ||
} else if (f(c) * f(a) < 0) { | ||
b = c | ||
} else { | ||
a = c | ||
} | ||
} | ||
return c | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
/** | ||
* @function decimalConvert | ||
* @description Convert the binary to decimal. | ||
* @param {string} binary - The input binary | ||
* @return {number} - Decimal of binary. | ||
* @see [DecimalConvert](https://www.programiz.com/javascript/examples/binary-decimal) | ||
* @example decimalConvert(1100) = 12 | ||
* @example decimalConvert(1110) = 14 | ||
*/ | ||
|
||
export const decimalConvert = (binary: string): number => { | ||
let decimal = 0 | ||
let binaryArr = binary.split('').reverse() | ||
|
||
for (let i = 0; i < binaryArr.length; i++) { | ||
decimal += parseInt(binaryArr[i]) * Math.pow(2, i) | ||
} | ||
|
||
return decimal | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
/** | ||
* @function eulerMethod | ||
* @description Euler's method is a first-order numerical procedure for solving ordinary differential equations (ODEs) with a given initial value. | ||
* @param {number} x0 - The initial value of x | ||
* @param {number} y0 - The initial value of y | ||
* @param {number} h - The step size | ||
* @param {number} n - The number of iterations | ||
* @param {Function} f - The function | ||
* @return {number} - The value of y at x | ||
* @see [EulerMethod](https://en.wikipedia.org/wiki/Euler_method) | ||
* @example eulerMethod(0, 1, 0.1, 10, (x, y) => x + y) = 2.5937424601 | ||
* @example eulerMethod(0, 1, 0.1, 10, (x, y) => x * y) = 1.7715614317 | ||
*/ | ||
|
||
export const eulerMethod = (x0: number, y0: number, h: number, n: number, f: Function): number => { | ||
let x = x0 | ||
let y = y0 | ||
|
||
for (let i = 1; i <= n; i++) { | ||
y = y + h * f(x, y) | ||
x = x + h | ||
} | ||
|
||
return y | ||
} |
Uh oh!
There was an error while loading. Please reload this page.