Home > Net >  Optaplanner - see if the same item appears in two lists
Optaplanner - see if the same item appears in two lists

Time:12-18

I am currently trying to make an exam timetabling program. I have got it so that the exams are assigned to a timeslot however the student conflict part is not working. I currently have my students in lists as attributes of subject objects. Each exam also has a subject attribute which is an instance of a subject class. The problem I'm having is that when comparing the lists of students it is being said that there are no conflicts as the list are not identical. However I need it to search through the lists and see if there is the same string appears in both. I'm very new to this so if anyone could sagest a better way of structuring my problem or a solve for the looking in the strings I would be very grateful.

private Constraint studentConflict(ConstraintFactory constraintFactory){
        // students can only sit one exam at the same time
        // selects a lesson and pairs it with another one
        return constraintFactory
                .forEach(Exam.class)
                .join(Exam.class,
                        //...in the same timeslot ...
                        Joiners.equal(Exam::getTimeslot),
                        //...has the same students ...
                        Joiners.equal(Exam::getStudentstoString))
                .penalize(HardSoftScore.ONE_HARD)
                .asConstraint("Student conflict");
    }

section of output:

section of output

CodePudding user response:

You need to add a filter.

private Constraint studentConflict(ConstraintFactory constraintFactory){
    // students can only sit one exam at the same time
    // selects a lesson and pairs it with another one
    return constraintFactory.forEach(Exam.class)
            .join(Exam.class,
                    Joiners.equal(Exam::getTimeslot))
            .filter((exam1, exam2) -> {
                 // call any code that compares the two lists and returns true on overlap
            })
            .penalize(HardSoftScore.ONE_HARD)
            .asConstraint("Student conflict");
}

This is the best you can do with the current model. However, it is probably not very efficient. Every time an Exam is evaluated, the filter is executed and the lists will be compared in their entirety.

We can do better. Assuming that a Student is a problem fact or a planning entity, you can do this, which will be much more incremental:

private Constraint studentConflict(ConstraintFactory constraintFactory){
    // students can only sit one exam at the same time
    // selects a lesson and pairs it with another one
    return constraintFactory.forEachUniquePair(Exam.class,
            Joiners.equal(Exam::getTimeslot))
        .ifExists(Student.class,
            Joiners.filtering((exam1, exam2, student) -> 
                exam1.getStudents().contains(student) &&
                exam2.getStudents().contains(student))
        .penalize(HardSoftScore.ONE_HARD)
        .asConstraint("Student conflict");
}

Needless to say, for this to be as efficient as possible, getStudents() should return a Set, not a List. (Or the lists should be very small.)

  • Related