diff --git a/algorithms-modules/algorithms-optimization/.gitignore b/algorithms-modules/algorithms-optimization/.gitignore
new file mode 100644
index 000000000000..30b2b7442c55
--- /dev/null
+++ b/algorithms-modules/algorithms-optimization/.gitignore
@@ -0,0 +1,4 @@
+/target/
+.settings/
+.classpath
+.project
\ No newline at end of file
diff --git a/algorithms-modules/algorithms-optimization/pom.xml b/algorithms-modules/algorithms-optimization/pom.xml
new file mode 100644
index 000000000000..7f2f1c5bc44a
--- /dev/null
+++ b/algorithms-modules/algorithms-optimization/pom.xml
@@ -0,0 +1,58 @@
+
+
+ 4.0.0
+ algorithms-optimization
+ 0.0.1-SNAPSHOT
+ algorithms-optimization
+
+
+ com.baeldung
+ algorithms-modules
+ 1.0.0-SNAPSHOT
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.11.0
+
+ 17
+
+
+
+
+
+
+
+ org.apache.commons
+ commons-math3
+ ${commons-math3.version}
+
+
+ commons-codec
+ commons-codec
+ ${commons-codec.version}
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+ provided
+
+
+ org.ojalgo
+ ojalgo
+ ${ojalgo.version}
+
+
+
+
+ 3.6.1
+ 56.2.0
+
+
+
\ No newline at end of file
diff --git a/algorithms-modules/algorithms-optimization/src/main/java/com/baeldung/algorithms/optimization/lp/AssignmentSolution.java b/algorithms-modules/algorithms-optimization/src/main/java/com/baeldung/algorithms/optimization/lp/AssignmentSolution.java
new file mode 100644
index 000000000000..b1bd86d07f0f
--- /dev/null
+++ b/algorithms-modules/algorithms-optimization/src/main/java/com/baeldung/algorithms/optimization/lp/AssignmentSolution.java
@@ -0,0 +1,47 @@
+package com.baeldung.algorithms.optimization.lp;
+
+public class AssignmentSolution {
+
+ private double totalCost;
+ private double[][] assignment;
+
+ public AssignmentSolution(double totalCost, double[][] assignment) {
+ this.totalCost = totalCost;
+ this.assignment = assignment;
+ }
+
+ public double getTotalCost() {
+ return totalCost;
+ }
+
+ public void setTotalCost(double totalCost) {
+ this.totalCost = totalCost;
+ }
+
+ public double[][] getAssignment() {
+ return assignment;
+ }
+
+ public void setAssignment(double[][] assignment) {
+ this.assignment = assignment;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("AssignmentSolution\n");
+ sb.append("Total Cost: ").append(totalCost).append("\n");
+ sb.append("Assignment Matrix:\n");
+ for (int i = 0; i < assignment.length; i++) {
+ sb.append("[ ");
+ for (int j = 0; j < assignment[i].length; j++) {
+ sb.append(String.format("%.0f", assignment[i][j]));
+ if (j < assignment[i].length - 1) {
+ sb.append(", ");
+ }
+ }
+ sb.append(" ]\n");
+ }
+ return sb.toString();
+ }
+}
diff --git a/algorithms-modules/algorithms-optimization/src/main/java/com/baeldung/algorithms/optimization/lp/AssignmentSolver.java b/algorithms-modules/algorithms-optimization/src/main/java/com/baeldung/algorithms/optimization/lp/AssignmentSolver.java
new file mode 100644
index 000000000000..86422b398208
--- /dev/null
+++ b/algorithms-modules/algorithms-optimization/src/main/java/com/baeldung/algorithms/optimization/lp/AssignmentSolver.java
@@ -0,0 +1,7 @@
+package com.baeldung.algorithms.optimization.lp;
+
+public interface AssignmentSolver {
+
+ AssignmentSolution solve(double[][] cost);
+
+}
diff --git a/algorithms-modules/algorithms-optimization/src/main/java/com/baeldung/algorithms/optimization/lp/CommonsMathAssignmentSolver.java b/algorithms-modules/algorithms-optimization/src/main/java/com/baeldung/algorithms/optimization/lp/CommonsMathAssignmentSolver.java
new file mode 100644
index 000000000000..a7f354a05666
--- /dev/null
+++ b/algorithms-modules/algorithms-optimization/src/main/java/com/baeldung/algorithms/optimization/lp/CommonsMathAssignmentSolver.java
@@ -0,0 +1,80 @@
+package com.baeldung.algorithms.optimization.lp;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.apache.commons.math3.optim.PointValuePair;
+import org.apache.commons.math3.optim.linear.LinearConstraint;
+import org.apache.commons.math3.optim.linear.LinearConstraintSet;
+import org.apache.commons.math3.optim.linear.LinearObjectiveFunction;
+import org.apache.commons.math3.optim.linear.NonNegativeConstraint;
+import org.apache.commons.math3.optim.linear.Relationship;
+import org.apache.commons.math3.optim.linear.SimplexSolver;
+import org.apache.commons.math3.optim.nonlinear.scalar.GoalType;
+
+public class CommonsMathAssignmentSolver implements AssignmentSolver {
+
+ public AssignmentSolution solve(double[][] t) {
+
+ int volunteers = t.length;
+ int locations = t[0].length;
+ int vars = volunteers * locations;
+
+ // Objective function coefficients
+ double[] x = new double[vars];
+ for (int i = 0; i < volunteers; i++) {
+ for (int j = 0; j < locations; j++) {
+ x[index(i, j, locations)] = t[i][j];
+ }
+ }
+
+ LinearObjectiveFunction objective = new LinearObjectiveFunction(x, 0);
+
+ Collection constraints = new ArrayList<>();
+
+ // Each volunteer assigned to exactly one location
+ for (int i = 0; i < volunteers; i++) {
+ double[] x_i = new double[vars];
+ for (int j = 0; j < locations; j++) {
+ x_i[index(i, j, locations)] = 1.0;
+ }
+ constraints.add(new LinearConstraint(x_i, Relationship.EQ, 1.0));
+ }
+
+ // Each location gets exactly one volunteer
+ for (int j = 0; j < locations; j++) {
+ double[] x_j = new double[vars];
+ for (int i = 0; i < volunteers; i++) {
+ x_j[index(i, j, locations)] = 1.0;
+ }
+ constraints.add(new LinearConstraint(x_j, Relationship.EQ, 1.0));
+ }
+
+ // Solve LP
+ SimplexSolver solver = new SimplexSolver();
+ PointValuePair solution = solver.optimize(
+ objective,
+ new LinearConstraintSet(constraints),
+ GoalType.MINIMIZE,
+ new NonNegativeConstraint(true)
+ );
+
+ double totalCost = solution.getValue();
+ double[] point = solution.getPoint();
+
+ // Rebuild assignment matrix
+ double[][] assignment = new double[volunteers][locations];
+ for (int i = 0; i < volunteers; i++) {
+ for (int j = 0; j < locations; j++) {
+ assignment[i][j] = point[index(i, j, locations)];
+ }
+ }
+
+ return new AssignmentSolution(totalCost, assignment);
+ }
+
+ private int index(int i, int j, int locations) {
+ return i * locations + j;
+ }
+
+}
diff --git a/algorithms-modules/algorithms-optimization/src/main/java/com/baeldung/algorithms/optimization/lp/OjAlgoAssignmentSolver.java b/algorithms-modules/algorithms-optimization/src/main/java/com/baeldung/algorithms/optimization/lp/OjAlgoAssignmentSolver.java
new file mode 100644
index 000000000000..1097e9cae786
--- /dev/null
+++ b/algorithms-modules/algorithms-optimization/src/main/java/com/baeldung/algorithms/optimization/lp/OjAlgoAssignmentSolver.java
@@ -0,0 +1,57 @@
+package com.baeldung.algorithms.optimization.lp;
+
+import org.ojalgo.optimisation.ExpressionsBasedModel;
+import org.ojalgo.optimisation.Expression;
+import org.ojalgo.optimisation.Variable;
+
+public class OjAlgoAssignmentSolver implements AssignmentSolver {
+
+ public AssignmentSolution solve(double[][] t) {
+
+ int volunteers = t.length;
+ int locations = t[0].length;
+
+ ExpressionsBasedModel model = new ExpressionsBasedModel();
+ Variable[][] x = new Variable[volunteers][locations];
+
+ // Create binary decision variables
+ for (int i = 0; i < volunteers; i++) {
+ for (int j = 0; j < locations; j++) {
+ x[i][j] = model
+ .newVariable("Assignment_" + i + "_" + j)
+ .binary()
+ .weight(t[i][j]);
+ }
+ }
+
+ // Each volunteer is assigned to exactly one location
+ for (int i = 0; i < volunteers; i++) {
+ Expression volunteerConstraint = model.addExpression("Volunteer_" + i).level(1);
+ for (int j = 0; j < locations; j++) {
+ volunteerConstraint.set(x[i][j], 1);
+ }
+ }
+
+ // Each location gets exactly one volunteer
+ for (int j = 0; j < locations; j++) {
+ Expression locationConstraint = model.addExpression("Location_" + j).level(1);
+ for (int i = 0; i < volunteers; i++) {
+ locationConstraint.set(x[i][j], 1);
+ }
+ }
+
+ // Solve
+ var result = model.minimise();
+ double totalCost = result.getValue();
+
+ // Extract assignment matrix
+ double[][] assignment = new double[volunteers][locations];
+ for (int i = 0; i < volunteers; i++) {
+ for (int j = 0; j < locations; j++) {
+ assignment[i][j] = x[i][j].getValue().doubleValue();
+ }
+ }
+
+ return new AssignmentSolution(totalCost, assignment);
+ }
+}
diff --git a/algorithms-modules/algorithms-optimization/src/main/resources/logback.xml b/algorithms-modules/algorithms-optimization/src/main/resources/logback.xml
new file mode 100644
index 000000000000..7d900d8ea884
--- /dev/null
+++ b/algorithms-modules/algorithms-optimization/src/main/resources/logback.xml
@@ -0,0 +1,13 @@
+
+
+
+
+ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/algorithms-modules/algorithms-optimization/src/test/java/com/baeldung/algorithms/optimization/lp/AssignmentSolverTest.java b/algorithms-modules/algorithms-optimization/src/test/java/com/baeldung/algorithms/optimization/lp/AssignmentSolverTest.java
new file mode 100644
index 000000000000..df6981b4a82c
--- /dev/null
+++ b/algorithms-modules/algorithms-optimization/src/test/java/com/baeldung/algorithms/optimization/lp/AssignmentSolverTest.java
@@ -0,0 +1,73 @@
+package com.baeldung.algorithms.optimization.lp;
+
+import java.util.stream.Stream;
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class AssignmentSolverTest {
+
+ @ParameterizedTest
+ @MethodSource("assignmentMatrices")
+ void whenSolveAssignmentMatrixByOjAlgo_thenTheTotalCostIsMinimized(double[][] cost, double expectedTotalCost, double[][] expectedAssignment) {
+ // given
+ AssignmentSolver solver = new OjAlgoAssignmentSolver();
+
+ // when
+ AssignmentSolution solution = solver.solve(cost);
+
+ // then
+ assertThat(solution.getTotalCost()).isEqualTo(expectedTotalCost);
+ assertThat(solution.getAssignment()).isEqualTo(expectedAssignment);
+ }
+
+ @ParameterizedTest
+ @MethodSource("assignmentMatrices")
+ void whenSolveAssignmentMatrixByCommonMaths_thenTheTotalCostIsMinimized(double[][] cost, double expectedTotalCost, double[][] expectedAssignment) {
+ // given
+ AssignmentSolver solver = new CommonsMathAssignmentSolver();
+
+ // when
+ AssignmentSolution solution = solver.solve(cost);
+
+ // then
+ assertThat(solution.getTotalCost()).isEqualTo(expectedTotalCost);
+ assertThat(solution.getAssignment()).isEqualTo(expectedAssignment);
+ }
+
+ static Stream assignmentMatrices() {
+ return Stream.of(
+ Arguments.of(
+ new double[][] {
+ {27, 6, 21},
+ {18, 12, 9},
+ {15, 24, 3}
+ },
+ 27.0,
+ new double[][] {
+ {0, 1, 0},
+ {1, 0, 0},
+ {0, 0, 1}
+ }
+ ),
+ Arguments.of(
+ new double[][] {
+ {9, 2, 7, 8},
+ {6, 4, 3, 7},
+ {5, 8, 1, 8},
+ {7, 6, 9, 4}
+ },
+ 13.0,
+ new double[][] {
+ {0, 1, 0, 0},
+ {1, 0, 0, 0},
+ {0, 0, 1, 0},
+ {0, 0, 0, 1}
+ }
+ )
+ );
+ }
+}
diff --git a/algorithms-modules/pom.xml b/algorithms-modules/pom.xml
index be78261bb788..b027784dfc88 100644
--- a/algorithms-modules/pom.xml
+++ b/algorithms-modules/pom.xml
@@ -26,6 +26,7 @@
algorithms-miscellaneous-9
algorithms-miscellaneous-10
algorithms-numeric
+ algorithms-optimization
algorithms-searching
algorithms-sorting
algorithms-sorting-2