initial commit
This commit is contained in:
commit
e92edcb8a9
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
*.txt
|
17
docs/index.md
Normal file
17
docs/index.md
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# Welcome to MkDocs
|
||||||
|
|
||||||
|
For full documentation visit [mkdocs.org](https://www.mkdocs.org).
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
* `mkdocs new [dir-name]` - Create a new project.
|
||||||
|
* `mkdocs serve` - Start the live-reloading docs server.
|
||||||
|
* `mkdocs build` - Build the documentation site.
|
||||||
|
* `mkdocs -h` - Print help message and exit.
|
||||||
|
|
||||||
|
## Project layout
|
||||||
|
|
||||||
|
mkdocs.yml # The configuration file.
|
||||||
|
docs/
|
||||||
|
index.md # The documentation homepage.
|
||||||
|
... # Other markdown pages, images and other files.
|
234
library.go
Normal file
234
library.go
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
"log/slog"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Participant struct
|
||||||
|
type Participant struct {
|
||||||
|
ID int
|
||||||
|
Preferences []int
|
||||||
|
AssignedTo int
|
||||||
|
RejectedFrom map[int]bool
|
||||||
|
mu sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// Workshop struct
|
||||||
|
type Workshop struct {
|
||||||
|
ID int
|
||||||
|
Capacity int
|
||||||
|
Participants []int
|
||||||
|
mu sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// Concurrent Stable Matching Algorithm
|
||||||
|
func StableMatch(participants []Participant, workshops []Workshop) {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
numWorkers := 10
|
||||||
|
tasks := make(chan *Participant, len(participants))
|
||||||
|
|
||||||
|
worker := func() {
|
||||||
|
defer wg.Done()
|
||||||
|
for p := range tasks {
|
||||||
|
assignParticipant(p, &workshops, participants)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < numWorkers; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go worker()
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range participants {
|
||||||
|
tasks <- &participants[i]
|
||||||
|
}
|
||||||
|
close(tasks)
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign a participant to a workshop
|
||||||
|
func assignParticipant(p *Participant, workshops *[]Workshop, participants []Participant) {
|
||||||
|
p.mu.Lock()
|
||||||
|
defer p.mu.Unlock()
|
||||||
|
|
||||||
|
assigned := false
|
||||||
|
|
||||||
|
for _, workshopID := range p.Preferences {
|
||||||
|
if p.RejectedFrom[workshopID] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if workshopID-1 < 0 || workshopID-1 >= len(*workshops) {
|
||||||
|
continue // Prevent index out of range
|
||||||
|
}
|
||||||
|
|
||||||
|
w := &(*workshops)[workshopID-1]
|
||||||
|
w.mu.Lock()
|
||||||
|
|
||||||
|
if len(w.Participants) < w.Capacity {
|
||||||
|
p.AssignedTo = workshopID
|
||||||
|
w.Participants = append(w.Participants, p.ID)
|
||||||
|
w.mu.Unlock()
|
||||||
|
assigned = true
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
weakestID, weakestIndex := findWeakestParticipant(w.Participants, participants, workshopID)
|
||||||
|
if weakestIndex != -1 && isPreferred(p.ID, weakestID, workshopID, participants) {
|
||||||
|
replaceParticipant(weakestID, p, w, weakestIndex, participants)
|
||||||
|
w.mu.Unlock()
|
||||||
|
assigned = true
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
p.RejectedFrom[workshopID] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !assigned {
|
||||||
|
for i := range *workshops {
|
||||||
|
w := &(*workshops)[i]
|
||||||
|
w.mu.Lock()
|
||||||
|
if len(w.Participants) < w.Capacity {
|
||||||
|
p.AssignedTo = w.ID
|
||||||
|
w.Participants = append(w.Participants, p.ID)
|
||||||
|
w.mu.Unlock()
|
||||||
|
slog.Info("Participant is assigned to Workshop as fallback\n", slog.Int("ParticipantId", p.ID), slog.Int("WorkshopId", w.ID))
|
||||||
|
assigned = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
w.mu.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !assigned {
|
||||||
|
slog.Info("❌ Participant could not be assigned\n", slog.Int("ParticipantId", p.ID))
|
||||||
|
p.AssignedTo = -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace weakest participant in a workshop
|
||||||
|
func replaceParticipant(weakestID int, newP *Participant, w *Workshop, weakestIndex int, participants []Participant) {
|
||||||
|
for i, id := range w.Participants {
|
||||||
|
if id == weakestID {
|
||||||
|
w.Participants = append(w.Participants[:i], w.Participants[i+1:]...)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.Participants[weakestIndex] = newP.ID
|
||||||
|
newP.AssignedTo = w.ID
|
||||||
|
participants[weakestID-1].AssignedTo = -1
|
||||||
|
participants[weakestID-1].RejectedFrom[w.ID] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the weakest participant in a workshop
|
||||||
|
func findWeakestParticipant(participantIDs []int, participants []Participant, workshopID int) (int, int) {
|
||||||
|
weakestID := -1
|
||||||
|
weakestIndex := -1
|
||||||
|
lowestRank := 4
|
||||||
|
|
||||||
|
for i, id := range participantIDs {
|
||||||
|
if id-1 < 0 || id-1 >= len(participants) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for rank, pref := range participants[id-1].Preferences {
|
||||||
|
if pref == workshopID && rank < lowestRank {
|
||||||
|
lowestRank = rank
|
||||||
|
weakestID = id
|
||||||
|
weakestIndex = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return weakestID, weakestIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if participant A is preferred over B for a workshop
|
||||||
|
func isPreferred(aID, bID, workshopID int, participants []Participant) bool {
|
||||||
|
if aID-1 < 0 || aID-1 >= len(participants) || bID-1 < 0 || bID-1 >= len(participants) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
aRank, bRank := 4, 4
|
||||||
|
|
||||||
|
for rank, pref := range participants[aID-1].Preferences {
|
||||||
|
if pref == workshopID {
|
||||||
|
aRank = rank
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for rank, pref := range participants[bID-1].Preferences {
|
||||||
|
if pref == workshopID {
|
||||||
|
bRank = rank
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return aRank < bRank
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print final assignments
|
||||||
|
func PrintAssignments(participants []Participant, workshops []Workshop) {
|
||||||
|
fmt.Println("\nFinal Assignments:")
|
||||||
|
for _, p := range participants {
|
||||||
|
fmt.Printf("Participant %d → Workshop %d\n", p.ID, p.AssignedTo)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("\nWorkshops and their Participants:")
|
||||||
|
for _, w := range workshops {
|
||||||
|
fmt.Printf("Workshop %d: %v\n", w.ID, w.Participants)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print statistics
|
||||||
|
func PrintStatistics(participants []Participant, workshops []Workshop, duration time.Duration) {
|
||||||
|
totalParticipants := len(participants)
|
||||||
|
totalWorkshops := len(workshops)
|
||||||
|
assignedCount := 0
|
||||||
|
unassignedCount := 0
|
||||||
|
firstChoiceCount := 0
|
||||||
|
secondChoiceCount := 0
|
||||||
|
thirdChoiceCount := 0
|
||||||
|
fallbackCount := 0
|
||||||
|
|
||||||
|
// Count participant assignment stats
|
||||||
|
for _, p := range participants {
|
||||||
|
if p.AssignedTo != -1 {
|
||||||
|
assignedCount++
|
||||||
|
|
||||||
|
// Check which preference was assigned
|
||||||
|
if p.AssignedTo == p.Preferences[0] {
|
||||||
|
firstChoiceCount++
|
||||||
|
} else if p.AssignedTo == p.Preferences[1] {
|
||||||
|
secondChoiceCount++
|
||||||
|
} else if p.AssignedTo == p.Preferences[2] {
|
||||||
|
thirdChoiceCount++
|
||||||
|
} else {
|
||||||
|
fallbackCount++
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unassignedCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate workshop utilization
|
||||||
|
fmt.Println("\nWorkshop Utilization:")
|
||||||
|
for _, w := range workshops {
|
||||||
|
usage := float64(len(w.Participants)) / float64(w.Capacity) * 100
|
||||||
|
fmt.Printf("Workshop %d: %d/%d participants (%.2f%% full)\n", w.ID, len(w.Participants), w.Capacity, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print summary statistics
|
||||||
|
fmt.Println("\n🔹 Matching Statistics:")
|
||||||
|
fmt.Printf("⏳ Runtime: %d ms\n", duration.Milliseconds())
|
||||||
|
fmt.Printf("📌 Total Participants: %d\n", totalParticipants)
|
||||||
|
fmt.Printf("🏫 Total Workshops: %d\n", totalWorkshops)
|
||||||
|
fmt.Printf("✅ Assigned Participants: %d (%.2f%%)\n", assignedCount, float64(assignedCount)/float64(totalParticipants)*100)
|
||||||
|
fmt.Printf("❌ Unassigned Participants: %d (%.2f%%)\n", unassignedCount, float64(unassignedCount)/float64(totalParticipants)*100)
|
||||||
|
fmt.Printf("🥇 First Choice Assigned: %d (%.2f%%)\n", firstChoiceCount, float64(firstChoiceCount)/float64(totalParticipants)*100)
|
||||||
|
fmt.Printf("🥈 Second Choice Assigned: %d (%.2f%%)\n", secondChoiceCount, float64(secondChoiceCount)/float64(totalParticipants)*100)
|
||||||
|
fmt.Printf("🥉 Third Choice Assigned: %d (%.2f%%)\n", thirdChoiceCount, float64(thirdChoiceCount)/float64(totalParticipants)*100)
|
||||||
|
fmt.Printf("⚠️ Assigned via Fallback: %d (%.2f%%)\n", fallbackCount, float64(fallbackCount)/float64(totalParticipants)*100)
|
||||||
|
}
|
47
main.go
Normal file
47
main.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"math/rand"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Initialize participants and workshops
|
||||||
|
func initializeParticipantsAndWorkshops(numParticipants, numWorkshops int) ([]Participant, []Workshop) {
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
|
||||||
|
participants := make([]Participant, numParticipants)
|
||||||
|
for i := range participants {
|
||||||
|
prefs := rand.Perm(numWorkshops)[:3] // Pick 3 unique workshops
|
||||||
|
participants[i] = Participant{
|
||||||
|
ID: i + 1,
|
||||||
|
Preferences: []int{prefs[0] + 1, prefs[1] + 1, prefs[2] + 1}, // Convert to 1-based index
|
||||||
|
AssignedTo: -1,
|
||||||
|
RejectedFrom: make(map[int]bool),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
workshops := make([]Workshop, numWorkshops)
|
||||||
|
for i := range workshops {
|
||||||
|
workshops[i] = Workshop{
|
||||||
|
ID: i + 1,
|
||||||
|
Capacity: rand.Intn(10) + 15, // Capacity between 15 and 25
|
||||||
|
Participants: []int{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return participants, workshops
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main function
|
||||||
|
func main() {
|
||||||
|
numParticipants := 600
|
||||||
|
numWorkshops := 30
|
||||||
|
|
||||||
|
participants, workshops := initializeParticipantsAndWorkshops(numParticipants, numWorkshops)
|
||||||
|
startTime := time.Now()
|
||||||
|
StableMatch(participants, workshops)
|
||||||
|
duration := time.Since(startTime)
|
||||||
|
PrintAssignments(participants, workshops)
|
||||||
|
PrintStatistics(participants, workshops, duration)
|
||||||
|
}
|
1
mkdocs.yml
Normal file
1
mkdocs.yml
Normal file
@ -0,0 +1 @@
|
|||||||
|
site_name: My Docs
|
Loading…
x
Reference in New Issue
Block a user