Added correct algorithm! Yay!

This commit is contained in:
Andrew Lalis 2019-04-17 23:42:37 +02:00 committed by andrewlalis
parent 7dff57ac98
commit df92a2c2d5
2 changed files with 101 additions and 4 deletions

View File

@ -91,7 +91,7 @@ public abstract class Team<P extends Person> extends BasicEntity {
public String toString() { public String toString() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
for (P p : this.getMembers()) { for (P p : this.getMembers()) {
sb.append(p.toString()).append(", "); sb.append(p.getFullName()).append(", ");
} }
return sb.toString(); return sb.toString();
} }

View File

@ -11,7 +11,10 @@ import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.*; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
/** /**
* Provides some methods to streamline the process of transforming a CSV file of student data into a list of valid teams * Provides some methods to streamline the process of transforming a CSV file of student data into a list of valid teams
@ -33,19 +36,27 @@ public class StudentTeamImporter {
List<StudentRecordEntry> studentEntries = extractStudentsFromRecords(records); List<StudentRecordEntry> studentEntries = extractStudentsFromRecords(records);
System.out.println("<<< Entries (" + studentEntries.size() + ") >>>");
for (StudentRecordEntry entry : studentEntries) { for (StudentRecordEntry entry : studentEntries) {
System.out.println(entry.toString()); System.out.println(entry.toString());
} }
return new ArrayList<>(); List<StudentTeam> studentTeams = generateTeamsFromStudentEntries(studentEntries, 0);
System.out.println("<<< Teams (" + studentTeams.size() + ") >>>");
for (StudentTeam team : studentTeams) {
System.out.println("Team: " + team);
}
return studentTeams;
} }
/** /**
* Extracts all student data from a list of records, and automatically discards outdated responses (those where the * Extracts all student data from a list of records, and automatically discards outdated responses (those where the
* same student submitted more than once). * same student submitted more than once).
* @param records The list of records in the CSV file. * @param records The list of records in the CSV file.
* @return A mapping for each timestamp to * @return A list of all student entries. A student entry is an intermediate object used to organize the parsed data
* from each record in the CSV file.
*/ */
private static List<StudentRecordEntry> extractStudentsFromRecords(Iterable<CSVRecord> records) { private static List<StudentRecordEntry> extractStudentsFromRecords(Iterable<CSVRecord> records) {
List<StudentRecordEntry> studentEntries = new ArrayList<>(); List<StudentRecordEntry> studentEntries = new ArrayList<>();
@ -73,7 +84,93 @@ public class StudentTeamImporter {
return studentEntries; return studentEntries;
} }
/**
* Generates a list of teams from a list of unique student entries. The algorithm is described as follows:
*
* 1. Select the next student from the list of entries.
* 2. If the student (A) has specified a partner (B), search for that partner (B) in the list of entries.
* 3. If the partner (B) also specified the student (A) as their preferred partner, a team is created from the two.
* a. Pop both student entries from the list of entries.
* 4. Else, place student (A) in a queue of single students.
* a. Pop student (A) from the list of entries.
* 5. If the list of entries is not empty, go to 1.
* 6. The list of entries is now empty, and zero or more entries exist in a queue of single students.
* 7. Pop two (or as many as possible) students randomly from the queue, and form a team from them.
* 8. Repeat until the queue of single students is empty.
* 9. There is now a list of student teams and no student should remain unprocessed.
*
* @param entries A list of record entries.
* @param seed A seed to use for randomization of single student teams.
* @return A list of student teams.
*/
private static List<StudentTeam> generateTeamsFromStudentEntries(List<StudentRecordEntry> entries, long seed) {
List<StudentTeam> teams = new ArrayList<>();
List<StudentRecordEntry> singleStudentsEntryQueue = new ArrayList<>();
Random random = new Random(seed);
// Realize step 5. Loop until there are no more processed entries.
while (!entries.isEmpty()) {
// Step 1. Select the next student from the list of entries.
StudentRecordEntry entry = entries.remove(0);
// Step 2. If the student has a partner, search for that partner.
if (entry.hasPartner()) {
boolean partnerFound = false; // Use this to keep track of if a partner is actually found.
for (int i = 0; i < entries.size(); i++) {
StudentRecordEntry secondEntry = entries.get(i);
// Step 3. If the partner specifies their partner as the student (both want each other), then create a team.
if (
entry.getPartnerStudent().equals(secondEntry.getStudent())
&& secondEntry.hasPartner()
&& secondEntry.getPartnerStudent().equals(entry.getStudent())
) {
partnerFound = true;
entries.remove(i); // Step 3a. The first student has been popped, so pop this one.
StudentTeam team = new StudentTeam();
team.addMember(entry.getStudent());
team.addMember(secondEntry.getStudent());
teams.add(team);
break;
}
}
// Step 4. If no partner was found, then add this entry to the queue of single students.
if (!partnerFound) {
singleStudentsEntryQueue.add(entry);
}
} else {
// Also Step 4. The student chose to have no partner, so add this entry to the queue of single students.
singleStudentsEntryQueue.add(entry);
}
}
// We are now at step 6.
while (!singleStudentsEntryQueue.isEmpty()) {
StudentTeam team = new StudentTeam();
StudentRecordEntry firstRandomEntry = singleStudentsEntryQueue.remove(random.nextInt(singleStudentsEntryQueue.size()));
team.addMember(firstRandomEntry.getStudent());
// Check if there's another student in the queue.
if (!singleStudentsEntryQueue.isEmpty()) {
StudentRecordEntry secondRandomEntry = singleStudentsEntryQueue.remove(random.nextInt(singleStudentsEntryQueue.size()));
team.addMember(secondRandomEntry.getStudent());
}
teams.add(team);
}
return teams;
}
/**
* Scans a list of student entries and removes entries which are outdated by newer entries by the same student, such
* that the resulting list contains only one unique entry per student.
* @param studentEntries The list of student entries, possibly containing duplicates.
* @return A list of student entries in which all outdated duplicates have been removed.
*/
private static List<StudentRecordEntry> removeDuplicateEntries(List<StudentRecordEntry> studentEntries) { private static List<StudentRecordEntry> removeDuplicateEntries(List<StudentRecordEntry> studentEntries) {
List<StudentRecordEntry> uniqueStudentEntries = new ArrayList<>(); List<StudentRecordEntry> uniqueStudentEntries = new ArrayList<>();