Hey , i have been developing this kind of a "project management tool" for study it is supposed to calculate tasks durations and cost to determine a critical path for any given project like this for example :
en example of a project and tasks relations
I implemented a Task Class as follow :
public class Task {
private String name;
private int duration;
private int earlyStart;
private int earlyFinish;
private int lateStart;
private int lateFinish;
private int totalFloat;
private HashSet<Task> predecessors;
private HashSet<Task> successors;
private String[] dependencies;
public Task(String taskName, int taskDuration, String[] dependencies) {
// Initialize Attributes
this.name = taskName;
this.duration = taskDuration;
this.dependencies = dependencies;
this.predecessors = new HashSet<Task>();
this.successors = new HashSet<Task>();
}
}
Ps : i didn't include the getters and setters
and i have also a class called project implemented as follow :
public class Project {
private HashSet<Task> tasks;
private HashSet<Task> initialTasks;
private HashSet<Task> finalTasks;
private int maxDuration;
public Project() {
this.tasks = new HashSet<Task>();
}
public void initialize(){
this.calculateTasksRelation();
this.calculateInitialTasks();
this.calculateInitialTasksEarlies();
this.forwardPass();
this.calculateFinalTasks();
this.calculateMaxDuration();
this.calculateFinalTasksLates();
this.backwardPass();
}
public void addTask(Task task) {
this.tasks.add(task);
}
public Task getTaskByName(String taskName) {
for (Task task : tasks) {
if(task.getName().equals(taskName)){
return task;
}
}
return null;
}
public HashSet<Task> getAllTasks() {
return tasks;
}
/**
* Private Methods internal Usage Only
* */
private void calculateTasksRelation() {
for (Task current : tasks) {
if ( current.getDependencies() != null ) {
for (String string : current.getDependencies() ) {
if (this.getTaskByName(string) != null) {
Task dependencie = this.getTaskByName(string);
current.addPredecessor(dependencie);
dependencie.addSuccessor(current);
}
}
}
}
}
// Return only the tasks that dosn't have predecessors
private void calculateInitialTasks(){
HashSet<Task> remaining = new HashSet<Task>(this.tasks);
// itertare over the remaining and remove all tasks
// that are successors = they have predecessor
for (Task current : tasks) {
for (Task successor : current.getSuccessors()) {
remaining.remove(successor);
}
}
this.initialTasks = new HashSet<>(remaining);
}
private void calculateInitialTasksEarlies() {
for (Task initialTask : this.initialTasks) {
initialTask.setEarlyStart(0);
initialTask.setEarlyFinish(initialTask.getEarlyStart() + initialTask.getDuration());
}
}
private void calculateMaxDuration() {
for (Task task : finalTasks) {
if(task.getEarlyFinish() > this.maxDuration) {
this.maxDuration = task.getEarlyFinish();
}
}
}
// Return only the tasks that dosn't have any successors
private void calculateFinalTasks() {
HashSet<Task> remaining = new HashSet<Task>(this.tasks);
// itertare over the remaining and remove all tasks
// that are predecessors = they have successor
for (Task current : tasks) {
for (Task predecessor : current.getPredecessors()) {
remaining.remove(predecessor);
}
}
this.finalTasks = new HashSet<>(remaining);
}
private void calculateFinalTasksLates() {
for (Task endTask : this.finalTasks) {
endTask.setLateFinish(this.maxDuration);
endTask.setLateStart(endTask.getLateFinish() - this.maxDuration);
}
}
private void forwardPass() {
// tasks whose early starts has been calculated
HashSet<Task> completed = new HashSet<Task>(initialTasks);
// tasks whose early starts has not been calculated yet
HashSet<Task> remaining = new HashSet<Task>(tasks);
remaining.removeAll(initialTasks);
// Backflow algorithm
// while there are tasks whose early start isn't calculated.
while (!remaining.isEmpty()) {
boolean progress = false;
for (Task currentTask : this.tasks) {
if(completed.containsAll(currentTask.getPredecessors())){
int temp = 0 ;
for ( Task dependencie : currentTask.getPredecessors() ) {
if( dependencie.getEarlyFinish() > temp ){
// update the temp variable
temp = dependencie.getEarlyFinish();
}
}
currentTask.setEarlyStart(temp);
currentTask.setEarlyFinish(currentTask.getEarlyStart() + currentTask.getDuration());
// set the task as completed and remove it from the remaining
completed.add(currentTask);
remaining.remove(currentTask);
// note that we are making a progress
progress = true;
}
}
// If we haven't made any progress then a cycle must exist in
// the graph and we wont be able to calculate the critical path
if (!progress)
throw new RuntimeException("Cyclic dependency, algorithm stopped!");
}
}
private void backwardPass() {
// tasks whose early starts has been calculated
HashSet<Task> completed = new HashSet<Task>(this.finalTasks);
// tasks whose early starts has not been calculated yet
HashSet<Task> remaining = new HashSet<Task>(tasks);
remaining.removeAll(finalTasks);
// Backflow algorithm
// while there are tasks whose early start isn't calculated.
while (!remaining.isEmpty()) {
boolean progress = false;
for (Task currentTask : this.tasks) {
if(completed.containsAll(currentTask.getSuccessors())){
int temp = this.maxDuration;
for ( Task successor : currentTask.getSuccessors() ) {
if( successor.getLateStart() < temp ){
// update the temp variable
temp = successor.getLateStart();
}
}
currentTask.setLateFinish(temp);
currentTask.setLateStart(currentTask.getLateFinish() - currentTask.getDuration());
// set the task as completed and remove it from the remaining
completed.add(currentTask);
remaining.remove(currentTask);
// note that we are making a progress
progress = true;
}
}
// If we haven't made any progress then a cycle must exist in
// the graph and we wont be able to calculate the critical path
if (!progress)
throw new RuntimeException("Cyclic dependency, algorithm stopped!");
}
}
}
Ps : Sorry for the long code :)
Finding the longest path from one source node to another one is an NP-hard problem in the general form, so there is no polynomial solution to it. Contrary to it, finding the shortest path is kind of easy to solve using Dijkstra's or Bellman-Ford's algorithm. I am not an expert, but I would really suggest to reconsider what you can and what you cannot do in the exercise.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.