Current using the number of times someone is on-rota two days in a row, multiplied by the standard deviation in the allocation frequencies.
195 lines
4.6 KiB
C
195 lines
4.6 KiB
C
#include "rotagen.h"
|
|
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/time.h>
|
|
|
|
#define POOL_SIZE 0x1000
|
|
#define MAX_PEOPLE 32
|
|
|
|
/* Hard-coded test data. */
|
|
static const char *people[] = {
|
|
"alice", "bob", "charlie", "dave", "eve", "fred", "george",
|
|
};
|
|
static const int num_people = sizeof(people) / sizeof(people[0]);
|
|
static const char *jobs[] = {
|
|
"washing dishes",
|
|
"putting away",
|
|
"wiping surfaces",
|
|
};
|
|
static const int num_jobs = sizeof(jobs) / sizeof(jobs[0]);
|
|
static const char *slots[] = {
|
|
"mon", "tue", "wed", "thu", "fri", "sat", "sun",
|
|
};
|
|
static const int num_slots = sizeof(slots) / sizeof(slots[0]);
|
|
static const struct constraint constraints[] = {
|
|
{
|
|
.person = 0,
|
|
.type = JOB_EXEMPTION_CONSTRAINT,
|
|
.object = { .job = 2 },
|
|
},
|
|
{
|
|
.person = 1,
|
|
.type = SLOT_EXEMPTION_CONSTRAINT,
|
|
.object = { .slot = 4 },
|
|
},
|
|
};
|
|
static const int num_constraints = sizeof(constraints) / sizeof(constraints[0]);
|
|
|
|
static uint8_t pool[POOL_SIZE];
|
|
static uint8_t *free_ptr;
|
|
|
|
static void *pool_alloc(int size)
|
|
{
|
|
if (free_ptr + size > pool + POOL_SIZE)
|
|
return NULL;
|
|
void *ptr = free_ptr;
|
|
free_ptr += size;
|
|
return ptr;
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
free_ptr = &pool[0];
|
|
|
|
unsigned seed;
|
|
struct timeval now;
|
|
if (gettimeofday(&now, NULL) == 0) {
|
|
seed = now.tv_usec;
|
|
} else {
|
|
fprintf(stderr,
|
|
"Warning: failed to get time of day to seed random number "
|
|
"generation; falling back to hard-coded seed.\n");
|
|
seed = 0xdeadbeef;
|
|
}
|
|
srand(seed);
|
|
|
|
struct slot_result *rota
|
|
= pool_alloc(num_slots * sizeof(struct slot_result));
|
|
if (rota == NULL) {
|
|
fprintf(stderr,
|
|
"Fatal error: memory pool exhausted in %s().\n",
|
|
__func__);
|
|
exit(1);
|
|
}
|
|
|
|
generate_rota(rota);
|
|
print_rota(rota);
|
|
|
|
const int badness = rota_badness(rota);
|
|
printf("Badness: %d\n", badness);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void generate_rota(struct slot_result *rota_out)
|
|
{
|
|
for (int slot = 0; slot < num_slots; ++slot) {
|
|
do {
|
|
for (int j = 0; j < num_jobs; ++j) {
|
|
struct allocation *allocation = &rota_out[slot].allocations[j];
|
|
do {
|
|
generate_allocation(slot, j, allocation);
|
|
} while (!satisfies_allocation_constraints(allocation));
|
|
}
|
|
} while (!satisfies_slot_constraints(&rota_out[slot]));
|
|
}
|
|
}
|
|
|
|
void generate_allocation(int slot, int job, struct allocation *allocation_out)
|
|
{
|
|
allocation_out->slot = slot;
|
|
allocation_out->job = job;
|
|
allocation_out->person = rand() % num_people;
|
|
}
|
|
|
|
bool satisfies_allocation_constraints(const struct allocation *allocation)
|
|
{
|
|
for (int i = 0; i < num_constraints; ++i) {
|
|
if (allocation->person != constraints[i].person)
|
|
continue;
|
|
switch (constraints[i].type) {
|
|
case JOB_EXEMPTION_CONSTRAINT:
|
|
if (allocation->job == constraints[i].object.job)
|
|
return false;
|
|
break;
|
|
case SLOT_EXEMPTION_CONSTRAINT:
|
|
if (allocation->slot == constraints[i].object.slot)
|
|
return false;
|
|
break;
|
|
default:
|
|
fprintf(stderr,
|
|
"Warning: unhandled constraint type %d in %s()",
|
|
constraints[i].type,
|
|
__func__);
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool satisfies_slot_constraints(const struct slot_result *result)
|
|
{
|
|
for (int i = 0; i < num_jobs; ++i) {
|
|
for (int j = i + 1; j < num_jobs; ++j) {
|
|
if (result->allocations[i].person == result->allocations[j].person)
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
int rota_badness(const struct slot_result *rota)
|
|
{
|
|
int freqs[MAX_PEOPLE];
|
|
memset(freqs, 0, sizeof(freqs));
|
|
for (int slot = 0; slot < num_slots; ++slot) {
|
|
for (int job = 0; job < num_jobs; ++job)
|
|
++freqs[rota[slot].allocations[job].person];
|
|
}
|
|
const int total = num_slots * num_jobs;
|
|
int sum = 0;
|
|
double ps[MAX_PEOPLE];
|
|
for (int i = 0; i < num_people; ++i) {
|
|
ps[i] = (double)freqs[i] / total;
|
|
sum += freqs[i];
|
|
}
|
|
const double mean = (double)sum / total;
|
|
double variance = 0;
|
|
for (int i = 0; i < num_people; ++i) {
|
|
const double x = ps[i] - mean;
|
|
variance += x * x;
|
|
}
|
|
variance /= num_people;
|
|
const double stddev = sqrt(variance);
|
|
|
|
int adjacency_score = 0;
|
|
for (int i = 0; i < num_slots; ++i) {
|
|
for (int j = 0; j < num_jobs; ++j) {
|
|
const int p = rota[i].allocations[j].person;
|
|
for (int k = 0; k < num_jobs; ++k) {
|
|
if (p == rota[i].allocations[k].person)
|
|
++adjacency_score;
|
|
}
|
|
}
|
|
}
|
|
|
|
return adjacency_score * stddev;
|
|
}
|
|
|
|
void print_rota(const struct slot_result *rota)
|
|
{
|
|
for (int slot = 0; slot < num_slots; ++slot) {
|
|
printf("----------------------------------------\n");
|
|
for (int job = 0; job < num_jobs; ++job) {
|
|
printf("%s\t%s\t\t%s\n",
|
|
job == 0 ? slots[slot] : " ",
|
|
jobs[job],
|
|
people[rota[slot].allocations[job].person]);
|
|
}
|
|
}
|
|
printf("----------------------------------------\n");
|
|
}
|