This commit is contained in:
2026-07-02 13:26:28 +02:00
parent 706306d8b6
commit e0f9dd8da3
5 changed files with 1489 additions and 0 deletions

103
matching.js Normal file
View File

@@ -0,0 +1,103 @@
// Algoritmo di assegnazione persone -> stanze.
// Vincolo: ogni stanza è mono-genere (M con M, F con F).
// Strategia: 1) partiziona le stanze tra i due generi in modo che ciascun
// genere ci stia (capienza sufficiente); 2) distribuisce casualmente le
// persone nelle stanze del proprio genere, cercando di non lasciare stanze
// completamente vuote.
(function (global) {
function shuffle(arr, rng) {
const a = arr.slice();
for (let i = a.length - 1; i > 0; i--) {
const j = Math.floor(rng() * (i + 1));
[a[i], a[j]] = [a[j], a[i]];
}
return a;
}
// Sceglie quali stanze vanno alle femmine e quali ai maschi.
// Preferisce partizioni in cui la capienza femminile combacia esattamente
// col numero di femmine (zero letti sprecati da quel lato => niente stanze
// vuote lato femmine). Fallback: qualsiasi partizione valida.
function chooseGenderPartition(rooms, nF, nM, rng) {
const n = rooms.length;
const total = rooms.reduce((s, r) => s + r.posti, 0);
const valid = [];
const exact = [];
// esclude mask 0 e mask "tutte" così ogni genere ha almeno una stanza
for (let mask = 1; mask < (1 << n) - 1; mask++) {
let capF = 0;
for (let i = 0; i < n; i++) if (mask & (1 << i)) capF += rooms[i].posti;
const capM = total - capF;
if (capF >= nF && capM >= nM) {
valid.push(mask);
if (capF === nF) exact.push(mask);
}
}
const pool = exact.length ? exact : valid;
if (!pool.length) {
throw new Error(
"Nessuna partizione valida: capienza insufficiente per il vincolo di genere."
);
}
return pool[Math.floor(rng() * pool.length)];
}
// Distribuisce le persone nelle stanze del proprio genere.
// Prima assegna 1 persona a stanza (copertura => niente stanze vuote se
// le persone sono >= delle stanze), poi riempie i letti restanti a caso.
function fillRooms(roomsG, peopleG, rng) {
const rooms = shuffle(roomsG, rng);
const people = shuffle(peopleG, rng);
const assign = {};
rooms.forEach((r) => (assign[r.id] = []));
let idx = 0;
if (people.length >= rooms.length) {
for (const r of rooms) assign[r.id].push(people[idx++]);
}
const slots = [];
for (const r of rooms) {
const free = r.posti - assign[r.id].length;
for (let k = 0; k < free; k++) slots.push(r.id);
}
const freeSlots = shuffle(slots, rng);
while (idx < people.length) {
const roomId = freeSlots.shift();
assign[roomId].push(people[idx++]);
}
return assign;
}
function assign(rooms, participants, rng) {
rng = rng || Math.random;
const females = participants.filter((p) => p.genere === "F");
const males = participants.filter((p) => p.genere === "M");
const mask = chooseGenderPartition(rooms, females.length, males.length, rng);
const fRooms = rooms.filter((r, i) => mask & (1 << i));
const mRooms = rooms.filter((r, i) => !(mask & (1 << i)));
const fAssign = fillRooms(fRooms, females.map((p) => p.nome), rng);
const mAssign = fillRooms(mRooms, males.map((p) => p.nome), rng);
const byPerson = {};
const roomGender = {};
fRooms.forEach((r) => (roomGender[r.id] = "F"));
mRooms.forEach((r) => (roomGender[r.id] = "M"));
for (const rid in fAssign) fAssign[rid].forEach((nome) => (byPerson[nome] = rid));
for (const rid in mAssign) mAssign[rid].forEach((nome) => (byPerson[nome] = rid));
return {
byPerson, // nome -> roomId
roomGender, // roomId -> "F" | "M"
fRooms,
mRooms,
};
}
const Matching = { assign, shuffle };
if (typeof module !== "undefined" && module.exports) module.exports = Matching;
global.Matching = Matching;
})(typeof window !== "undefined" ? window : globalThis);