Skip to content

Commit

Permalink
fix memory explosion in typescript variant
Browse files Browse the repository at this point in the history
  • Loading branch information
neferin12 committed Jan 22, 2024
1 parent 9bbdd39 commit 42a31cf
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 59 deletions.
47 changes: 12 additions & 35 deletions ts/tsism-lib/src/algorithm.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {NumberToNumberMapInterface, Student} from './student'
import {Student} from './student'
import {Seminar} from './seminar'
import {Iteration} from "./iteration";

export enum Points {
FIRST_SELECTION = 0,
Expand All @@ -8,29 +9,6 @@ export enum Points {
NO_SELECTION = 30
}

interface AssignmentMapInterface {
get: (s: Student) => { wSeminar?: Seminar, pSeminar?: Seminar } | undefined
set: (s: Student, value: { wSeminar?: Seminar, pSeminar?: Seminar }) => void
}

class AssignmentMapArray implements AssignmentMapInterface {
readonly a: Array<{ wSeminar?: Seminar; pSeminar?: Seminar }> = []

get(s: Student): { wSeminar?: Seminar; pSeminar?: Seminar } | undefined {
return this.a[s.id];
}

set(s: Student, value: { wSeminar?: Seminar; pSeminar?: Seminar }): void {
this.a[s.id] = value;
}
}

export type Iteration = {
id: number,
points: number,
assignments: AssignmentMapInterface
}

function shuffle<T>(a: Array<T>) {
for (let i = a.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
Expand All @@ -46,14 +24,13 @@ export async function runAlgorithm(iterations: number, students: Student[]): Pro
}

function tryAssignment(seminar: Seminar, student: Student, currentIteration: Iteration, seminarType: 'W' | 'P', points: Points) {
let cap = seminar.remainingCapacityPerRun.get(currentIteration.id)
let cap = currentIteration.seminarCapacity.get(seminar.id)
if (cap === undefined) {
cap = seminar.capacity
}

if (cap > 0) {
seminar.remainingCapacityPerRun.set(currentIteration.id, cap - 1)
currentIteration.points += points
currentIteration.seminarCapacity.set(seminar.id, cap - 1);

const previousAssignment = currentIteration.assignments.get(student) || {}
if (seminarType === 'W') {
Expand All @@ -64,7 +41,7 @@ function tryAssignment(seminar: Seminar, student: Student, currentIteration: Ite
currentIteration.assignments.set(student, {...previousAssignment, pSeminar: seminar})
}

student.pointsPerRun.set(currentIteration.id, (student.pointsPerRun.get(currentIteration.id) || 0) + points)
currentIteration.pointsPerStudent.set(student.id, (currentIteration.pointsPerStudent.get(student.id) || 0) + points)

return true
}
Expand All @@ -75,21 +52,22 @@ function tryAssignment(seminar: Seminar, student: Student, currentIteration: Ite
function execute(iterations: number, students: Student[]): Iteration {
let best: Iteration | null = null
for (let i = 0; i < iterations; i++) {
const currentIteration: Iteration = {id: i, points: 0, assignments: new AssignmentMapArray()}
const algRun = new Iteration();
const currentIteration = new Iteration();
const studentsCopy = shuffle([...students])
for (const student of studentsCopy) {
if (
!tryAssignment(student.wWishes[0], student, currentIteration, 'W', Points.FIRST_SELECTION) &&
!tryAssignment(student.wWishes[1], student, currentIteration, 'W', Points.SECOND_SELECTION) &&
!tryAssignment(student.wWishes[2], student, currentIteration, 'W', Points.THIRD_SELECTION)
) {
student.pointsPerRun.set(i, Points.NO_SELECTION)
currentIteration.points += Points.NO_SELECTION
currentIteration.pointsPerStudent.set(student.id, Points.NO_SELECTION)
algRun.pointsPerStudent.set(student.id, Points.NO_SELECTION)
}
}

studentsCopy.sort((a, b) => {
return (b.pointsPerRun.get(i) || 0) - (a.pointsPerRun.get(i) || 0)
return (currentIteration.pointsPerStudent.get(b.id) || 0) - (currentIteration.pointsPerStudent.get(a.id) || 0)
})

for (const student of studentsCopy) {
Expand All @@ -98,12 +76,11 @@ function execute(iterations: number, students: Student[]): Iteration {
!tryAssignment(student.pWishes[1], student, currentIteration, 'P', Points.SECOND_SELECTION) &&
!tryAssignment(student.pWishes[2], student, currentIteration, 'P', Points.THIRD_SELECTION)
) {
student.pointsPerRun.set(i, Points.NO_SELECTION + (student.pointsPerRun.get(i) || 0))
currentIteration.points += Points.NO_SELECTION
currentIteration.pointsPerStudent.set(student.id, Points.NO_SELECTION + (currentIteration.pointsPerStudent.get(student.id) || 0))
}
}

if (best === null || best.points > currentIteration.points) {
if (best === null || best.totalPoints() > currentIteration.totalPoints()) {
best = currentIteration
}
}
Expand Down
12 changes: 7 additions & 5 deletions ts/tsism-lib/src/io.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {Seminar, SeminarType} from './seminar'
import * as fs from 'node:fs'
import {parse} from 'csv-parse'
import {Student} from './student'
import {Iteration} from './algorithm'
import {Iteration} from './iteration'

export type SeminarData = { wSeminars: Seminar[], pSeminars: Seminar[] }

Expand All @@ -17,6 +17,7 @@ export function importSeminars(file: string): Promise<SeminarData> {
wSeminars: [],
pSeminars: [],
}
let currID = 0;
for (const result of results) {
if (!Array.isArray(result) || result.length < 3) {
console.warn(`Skip invalid line: "${result}"`)
Expand All @@ -25,14 +26,15 @@ export function importSeminars(file: string): Promise<SeminarData> {

switch (result[2]) {
case 'W':
ret.wSeminars.push(new Seminar(result[0], Number.parseInt(result[1], 10), SeminarType.W_SEMINAR))
ret.wSeminars.push(new Seminar(currID, result[0], Number.parseInt(result[1], 10), SeminarType.W_SEMINAR))
break
case 'P':
ret.pSeminars.push(new Seminar(result[0], Number.parseInt(result[1], 10), SeminarType.P_SEMINAR))
ret.pSeminars.push(new Seminar(currID, result[0], Number.parseInt(result[1], 10), SeminarType.P_SEMINAR))
break
default:
throw new Error('Unknown Seminar Type ' + result[3])
}
currID++;
}

resolve(ret)
Expand Down Expand Up @@ -72,8 +74,8 @@ export function importStudents(file: string, seminars: SeminarData): Promise<Stu
}

export function printStudents(students: Student[], iteration: Iteration): void {
console.log(`---------|${iteration.points}|---------`)
console.log(`---------|${iteration.totalPoints()}|---------`)
for (const [i, student] of students.entries()) {
console.log(`(${i + 1}) ${student.name}, ${student.pointsPerRun.get(iteration.id)}, (W: ${iteration.assignments.get(student)?.wSeminar?.name || 'none'} | P: ${iteration.assignments.get(student)?.pSeminar?.name || 'none'})`)
console.log(`(${i + 1}) ${student.name}, ${iteration.pointsPerStudent.get(student.id)}, (W: ${iteration.assignments.get(student)?.wSeminar?.name || 'none'} | P: ${iteration.assignments.get(student)?.pSeminar?.name || 'none'})`)
}
}
20 changes: 20 additions & 0 deletions ts/tsism-lib/src/iteration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import {NumberToNumberArray, NumberToNumberMapInterface} from "./maps";
import {AssignmentMapArray, AssignmentMapInterface} from "./maps";

export class Iteration {
private points: number | null = null;
assignments: AssignmentMapInterface = new AssignmentMapArray();
seminarCapacity: NumberToNumberMapInterface = new NumberToNumberArray();
pointsPerStudent: NumberToNumberMapInterface = new NumberToNumberArray();

/**
* Returns the total points of this iteration. Once calculated, the value is cached.
*/
totalPoints(): number {
if (!this.points) {
this.points = this.pointsPerStudent.getAllValues().reduce((a, b) => a + b, 0);
}
return this.points;
}

}
40 changes: 40 additions & 0 deletions ts/tsism-lib/src/maps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import {Student} from "./student";
import {Seminar} from "./seminar";

export interface AssignmentMapInterface {
get: (s: Student) => { wSeminar?: Seminar, pSeminar?: Seminar } | undefined
set: (s: Student, value: { wSeminar?: Seminar, pSeminar?: Seminar }) => void
}

export class AssignmentMapArray implements AssignmentMapInterface {
readonly a: Array<{ wSeminar?: Seminar; pSeminar?: Seminar }> = []

get(s: Student): { wSeminar?: Seminar; pSeminar?: Seminar } | undefined {
return this.a[s.id];
}

set(s: Student, value: { wSeminar?: Seminar; pSeminar?: Seminar }): void {
this.a[s.id] = value;
}
}

export interface NumberToNumberMapInterface {
get: (id: number) => number | undefined
set: (id: number, value: number) => void
getAllValues: () => number[]
}

export class NumberToNumberArray implements NumberToNumberMapInterface {
readonly a: number[] = []
get(id: number): number | undefined {
return (this.a)[id];
}

set(id: number, value: number): void {
this.a[id] = value;
}

getAllValues(): number[] {
return this.a;
}
}
7 changes: 3 additions & 4 deletions ts/tsism-lib/src/seminar.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import {NumberToNumberMapInterface, NumberToNumberArray} from "./student";

export enum SeminarType {
W_SEMINAR,
P_SEMINAR,
}

export class Seminar {
readonly id: number
readonly name: string
readonly capacity: number
readonly type: SeminarType
readonly remainingCapacityPerRun: NumberToNumberMapInterface = new NumberToNumberArray()

constructor(name: string, capacity: number, type: SeminarType) {
constructor(id: number, name: string, capacity: number, type: SeminarType) {
this.id = id
this.name = name
this.capacity = capacity
this.type = type
Expand Down
15 changes: 0 additions & 15 deletions ts/tsism-lib/src/student.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,12 @@
import {Seminar} from './seminar'

export interface NumberToNumberMapInterface {
get: (id: number) => number | undefined
set: (id: number, value: number) => void
}

export class NumberToNumberArray implements NumberToNumberMapInterface {
readonly a: number[] = []
get(id: number): number | undefined {
return (this.a)[id];
}

set(id: number, value: number): void {
this.a[id] = value;
}
}

export class Student {
readonly name: string
readonly id: number
readonly wWishes: [Seminar, Seminar, Seminar]
readonly pWishes: [Seminar, Seminar, Seminar]
readonly pointsPerRun: NumberToNumberMapInterface = new NumberToNumberArray()

constructor(id: number, name: string, wWishes: [Seminar, Seminar, Seminar], pWishes: [Seminar, Seminar, Seminar]) {
this.id = id
Expand Down

0 comments on commit 42a31cf

Please sign in to comment.