Scopri chi è il turno di acquistare i cornetti, tenendo conto delle possibili assenze


13

Una squadra ha deciso che ogni mattina qualcuno dovrebbe portare cornetti per tutti. Non dovrebbe essere sempre la stessa persona, quindi dovrebbe esserci un sistema per determinare di chi è il turno successivo. Lo scopo di questa domanda è determinare un algoritmo per decidere chi sarà il turno di portare i croissant domani.

Vincoli, ipotesi e obiettivi:

  • Di chi è il turno di portare i cornetti sarà determinato il pomeriggio precedente.
  • In un dato giorno, alcune persone sono assenti. L'algoritmo deve scegliere qualcuno che sarà presente quel giorno. Supponiamo che tutte le assenze siano note con un giorno di anticipo, quindi l'acquirente di cornetti può essere determinato il pomeriggio precedente.
  • Nel complesso, la maggior parte delle persone è presente nella maggior parte dei giorni.
  • Nell'interesse dell'equità, tutti dovrebbero comprare i croissant tante volte quanto gli altri. (Fondamentalmente, supponi che ogni membro del team abbia la stessa quantità di denaro da spendere per i croissant.)
  • Sarebbe bello avere qualche elemento di casualità, o almeno la casualità percepita, al fine di alleviare la noia di un elenco. Questo non è un vincolo difficile: è più un giudizio estetico. Tuttavia, la stessa persona non dovrebbe essere scelta due volte di seguito.
  • La persona che porta i cornetti dovrebbe sapere in anticipo. Quindi, se la persona P deve portare cornetti il ​​giorno D, allora questo fatto dovrebbe essere determinato in un giorno precedente in cui la persona P è presente. Ad esempio, se il cornetto per cornetti viene sempre determinato il giorno prima, dovrebbe essere una delle persone presenti il ​​giorno prima.
  • Il numero dei membri del team è abbastanza piccolo che le risorse di archiviazione e elaborazione sono effettivamente illimitate. Ad esempio l'algoritmo può fare affidamento su una storia completa di chi ha portato i cornetti in passato. Fino a pochi minuti di calcolo su un PC veloce ogni giorno sarebbe OK.

Questo è un modello di un problema del mondo reale, quindi sei libero di sfidare o affinare le ipotesi se pensi che modellino meglio lo scenario.


Origine 1: Scopri chi acquisterà i croissant di Florian Margaine.
Origine 2: Scopri chi acquisterà i croissant di Gilles.
Questa domanda è la stessa versione di Gilles, ed è stata ripubblicata su Programmers come esperimento per vedere come le diverse comunità affrontano una sfida di programmazione.


2
Aggiunto avviso post, proteggerò se necessario ma mi piacerebbe tenerlo il più aperto possibile il più a lungo possibile. Considerando che questa domanda è in qualche modo diversa, è un esperimento. Rimarrà aperto. Per la scienza!
Ingegnere mondiale il

4
Più adatto per Code Golf?
ozz,

3
Che importa? Nessuna squadra che si rispetti avrebbe dei croissant. Ora, le ciambelle , d'altra parte, è una domanda interessante.
Ross Patterson,

3
Sembra un caso d'uso perfetto per DA Form 6 (diamine, ha funzionato per l'esercito dal 1974!). Vedere AR 220-45 per l'uso. Dovrebbe essere relativamente semplice tradurlo in un algoritmo.
Adam Balsam il

2
(per espandere su @AdamBalsam il modulo armypubs.army.mil/eforms/pdf/A6.PDF e l'utilizzo apd.army.mil/pdffiles/r220_45.pdf ... e per favore non suggerire questo al mio ex datore di lavoro, loro hanno abbastanza politiche e procedure così come sono)

Risposte:


26

Userei un algoritmo di punteggio. Ogni persona inizia con un punteggio pari a zero. Ogni volta che portano croissant, incrementa il loro punteggio di 1. Il punteggio di tutti i membri del team che non hanno portato croissant è diminuito di 1 / N. Pertanto, un punteggio pari a 0 indica che un membro del team non ha acquistato né sopra né sotto.

Senza casualità, scegli la persona con il punteggio più basso dalla lista di coloro che saranno presenti.

Per aggiungere casualità, ordina l'elenco per punteggio e scegli a caso fuori dall'elenco di tutti i membri del team con un punteggio negativo. Limitando ai punteggi negativi, si assicura che nessuno sarà troppo "fortunato" per molte settimane.

Il vantaggio di questo algoritmo è che non fa affidamento sul mantenimento dei record storici e consente facilmente l'aggiunta di nuovi membri del team in qualsiasi momento.

Potrebbe essere adattato per consentire che le assenze siano relativamente comuni diminuendo le decine dei soli presenti per godersi i cornetti.


3
Penso che il tuo ultimo paragrafo sia essenziale, altrimenti qualcuno che va in vacanza per un mese (forse la luna di miele) tornerebbe a un enorme punteggio negativo e molti acquisti.
James,

8
Potrebbe anche modificare: -1 se mangi una pasticceria che qualcun altro ha portato. (N-1) se acquisti pasticcini. In questo modo se qualcuno diventa fortunato e acquista solo per 4, quindi il giorno successivo la persona viene sfortunata e acquista per 7, quei due acquisti non vengono trattati allo stesso modo. -1 perché una pasticceria che acquisti per te è neutra.
James,

@James, nessuna paura; l'OP è negli Stati Uniti, e nessuno negli Stati Uniti si avvicina a quella vacanza. :(
Kyralessa il

@James Sì, è un bel miglioramento.
Gort the Robot il

7

Quello che farei, se dovessi scegliere questo, è prendere un cappello e mettere i nomi di tutti nel cappello una volta su piccoli pezzi di carta. Poi, ogni giorno, disegnavo a caso il nome di qualcuno dal cappello, e quella è la persona che porta i croissant il giorno successivo. Quel documento viene poi appiccicato su una tavola, sotto "PORTARE I CROISSANTI DOMANI". Il foglio che è attualmente sulla lavagna viene buttato via.

Avrei anche una scatola. Inizia vuoto. Ogni giorno, prima di disegnare i nomi, scaricavo il contenuto della scatola nel cappello, poi sfogliavo i fogli con il cappello e toglievo tutti coloro che sarebbero assenti domani. I loro nomi vanno nella scatola.

Se è il momento di disegnare un nome e il cappello è vuoto, strapperei un po 'di carta in più e aggiungerei il nome di tutti una volta, quindi sposterò i nomi di tutti coloro che saranno assenti domani nella scatola.

A causa di questi ultimi due passaggi, è possibile che lo stesso nome si trovi nel cappello più volte contemporaneamente. Se il nome che mi capita di disegnare è lo stesso del nome che si trova sul tabellone, sposterei quel foglio nella casella e poi disegnerei di nuovo.

Non dovrebbe essere troppo difficile tradurre questo sistema in un algoritmo nella tua lingua preferita.


Ordinare il cappello per tutti quelli che stanno per uscire sembra un vero dolore.
Bobson,

@Bobson: la domanda dice specificamente che la dimensione della squadra è relativamente piccola. Se avessi a che fare con un set di dati di grandi dimensioni, farei qualcosa di più sofisticato.
Mason Wheeler,

6

Algoritmo, smalgoritmo. Usa un DB.

create table team_members 
(
    id integer auto_increment,
    name varchar(255),
    purchase_count integer,
    last_purchase_date datetime,
    present integer,
    prefers_donuts integer default 0,
    primary key( id)
)

Chi compra?

select id from team_members where (present = 1) and (prefers_donuts = 0) order by purchase_count, last_purchase_date limit 1;

Dopo aver acquistato:

update team_members set purchase_count = purchase_count + 1, last_purchase_date = now() where id = ?

E quindi impostare:

insert into team_members (name, prefers_donuts) values ('GrandmasterB', 1);

... perché sono vecchia scuola.

Non dovrebbe essere troppo difficile aggiungere un po 'di casualità modificando la prima query, forse aggiungendo un random () invece di ordinare in base alla data dell'ultimo acquisto.


1
+1. Per i nuovi assunti, ti inizializzi purchase_countalla media di tutti gli altri?
Dan Pichelman,

6
Hmm, ottima domanda. Probabilmente avrebbe funzionato. Oppure puoi semplicemente fare in modo che il nuovo ragazzo porti i cornetti ogni mattina fino a quando non raggiunge. È il nuovo ragazzo dopo tutto.
GrandmasterB,

4

In realtà ho dovuto risolvere questo problema in qualche modo nel mondo reale:

remember how many times people have gotten donuts
every day:
  var candidates = everyone
  toss out people who aren't here tomorrow
  toss out people who aren't here today 
  toss out the person who got them today (unless they're the only one left)
  toss out everyone where "times they got donuts"/"times everyone has got donuts"
    is more than 1/number of people (unless that would eliminate everyone)

  pick someone at random from the candidates

Ciò che accade è che le persone che hanno acquistato ciambelle "troppo" (a causa di sfortuna, che vanno a lavorare quando gli altri sono in vacanza, ecc.) Sono escluse dal pool fino a quando non passano sufficienti acquisizioni per riportarle sotto la "giusta" percentuale di acquisti.

Questo dovrebbe essere ampliato per gestire meglio l'assunzione di nuove persone però ...

Comunque, questo progetto ha funzionato davvero bene per cambiare le variabili (chi è dentro, chi è fuori) e quando il programma deve essere (praticamente) infinito. Come bonus aggiuntivo, è facile rendere deterministico seminando il tuo RNG.


2

Non buono come alcune delle altre risposte già presentate, ma un altro modo di vedere il problema:

  1. Fai un elenco di tutti i dipendenti partecipanti
  2. Duplica l'elenco molte volte (diciamo, 1.000)
  3. Mischia l'elenco

Ogni pomeriggio, seleziona il prossimo cornetto disponibile. Ogni mattina, il cornetto croissant cancella il suo nome dalla cima della lista.

L'elaborazione giornaliera è semplice con carta e penna.

Nuovi assunti e terminazioni Gli alunni dovrebbero probabilmente essere gestiti meglio facendo un nuovo elenco. Se i cicli della CPU tornano ad essere costosi (o hai 100 milioni di dipendenti e solo un Arduino di prima generazione), sarebbe facile salare l'elenco originale con un numero appropriato di segnaposti.


Maggiori informazioni (per richiesta).

Utilizzando questo approccio con un elenco arbitrariamente lungo, si ottiene il vantaggio della trasparenza.

Non solo sai chi porterà i croissant domani, ma chi è programmato per portarli il giorno dopo e così via. Certo, più nel tempo sembri, meno preciso sarai a causa di assenze, ecc.

Gli sviluppatori subdoli che scoprono come appesantire le loro buste di carta in un cappello non avranno la stessa opportunità di evitare i loro compiti da portare con i croissant.

I non devianti lamentosi che sostengono che il trattamento è truccato possono facilmente rivedere i dati, trarre conclusioni errate e lamentarsi comunque.


1
Terminazioni ? Ghenghis Khan approva questo post.
Deer Hunter,

1
@DeerHunter Non ho mai apprezzato il modo in cui le risorse umane parlano di "terminare le persone". Mi viene in mente squadre di fuoco. Forse avrei dovuto dire "New Hires & Alumni ..." invece.
Dan Pichelman,

1

Non casuale

Mantenere un elenco ordinato. Se una persona è assente il giorno in cui dovrebbe comprare, scambialo con la prossima persona disponibile. Alla fine la persona sarà presente e quindi acquisterà i cornetti. Pertanto, i contenuti dell'elenco rimangono gli stessi, ma le persone possono essere spostate o verso il basso a seconda delle assenze.

Le nuove persone vengono inserite nell'elenco dopo la posizione corrente. Le persone che hanno chiuso o chiuso vengono rimosse dall'elenco. La posizione corrente aumenta di 1 ogni giorno, quando raggiunge la fine, tornerà all'inizio.

Ciò presuppone che ci siano abbastanza persone nell'elenco per tenere conto del tempo medio di assenso per promuovere l'equità.

Casuale

Non possiamo semplicemente selezionare persone a caso ogni giorno poiché ci sarà una propensione a breve termine, ad esempio lanciare una moneta 10 volte e potresti trovare teste 8 e code 2, quindi le teste verrebbero avvitate a breve termine. Quindi, dobbiamo creare gruppi di persone per mantenerlo equo.

Il secchio è determinato dal numero di volte in cui le persone hanno acquistato i crocevia in passato. Quindi, in questo caso, memorizzeremmo un dizionario di persone e acquisti incrociati. Il primo giorno tutti sono nel secchio zero. Man mano che le persone acquistano i croissant, verranno assegnate al bucket successivo, ovvero 1, 2, ecc. La parte casuale viene prelevata dal pool di persone disponibili nel bucket. Il primo bucket disponibile è quello con il minor numero di acquisti. Se ci sono 10 persone nel secchio, scegli un numero casuale da 1 a 10 e quello che acquista cornetti. Alle nuove persone viene assegnato il bucket corrente più basso in modo che non finiscano per acquistare round extra di crossiant (anche se saranno subito nel pool di acquisto). Se nessuno è disponibile nel bucket più basso (sono tutti assenti), si passa al bucket più alto successivo. Ad esempio, lascia che diciamo che c'è un elenco di 10 persone. Il giorno 8, 8 persone sono nel bucket 1 e 2 nel bucket 0. Le due persone nel bucket 0 sono assenti. In questo caso, verrà utilizzato il bucket 1 e una persona finirà nel bucket 2. Tuttavia, le persone saranno sempre all'interno di pochi acquisti incrociati (bucket) l'una nell'altra, perché la persona che si trova ora nel bucket 2 probabilmente non si troverà il pool di acquisto per un po '.

Potrebbero essere aggiunte delle modifiche per assicurarsi che la stessa persona non acquisti due giorni di fila e ci siano alcuni casi limite da gestire, ma ciò aggiungerebbe un elemento di casualità oltre a mantenerlo equo. Inoltre, si potrebbe voler mantenere separati gli acquisti effettivi di croissant rispetto all'attuale bucket. Quando le persone se ne vanno, vengono rimosse dal secchio marcandole in modo permanente assente o eliminandole del tutto.


1
Aggiunta implementazione casuale.
Jon Raynor,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.