web-dev-qa-db-ja.com

欠席の可能性を考慮して、クロワッサンを購入する順番を調べましょう

チームは毎朝誰かがクロワッサンをみんなのために持ってくるべきだと決めました。毎回同じ人である必要はないので、次の順番を決定するシステムが必要です。この質問の目的は、クロワッサンを明日持ってくる順番を決定するためのアルゴリズムを決定することです。

制約、仮定、目的:

  • クロワッサンを持参する順番は、前日の午後に決定されます。
  • 特定の日に、何人かの人々は欠席しています。アルゴリズムは、その日に出席する人を選ぶ必要があります。すべての欠席が前日に判明しているため、クロワッサンの購入者は前日の午後に決定できます。
  • 全体として、ほとんどの人がほとんどの日に立ち会います。
  • 公平を期すために、誰もがクロワッサンを他の人と同じ回数購入する必要があります。 (基本的に、すべてのチームメンバーがクロワッサンに費やす金額が同じであると想定します。)
  • 名簿の退屈さを緩和するために、ランダム性、または少なくとも知覚されるランダム性のいくつかの要素があるといいでしょう。これは厳密な制約ではありません。より審美的な判断です。ただし、同じ人物を2回続けて選ぶことはできません。
  • クロワッサンを持参する人は、事前に知っておく必要があります。したがって、P氏がD日目にクロワッサンを持ち込む場合、この事実は、P氏がいる前日に決定する必要があります。たとえば、クロワッサンの持参人が常に前日に決定される場合、それは前日に存在する人物の1人である必要があります。
  • チームメンバーの数は十分に少ないため、ストレージおよびコンピューティングリソースは事実上無制限です。たとえば、アルゴリズムは、過去に誰がクロワッサンをもたらしたかの完全な履歴に依存できます。高速のPCで毎日数分までの計算で問題ありません。

これは実際の問題のモデルであるため、シナリオをより適切にモデル化していると思われる場合は、仮定に挑戦または調整することができます。


起源1: クロワッサンを買う人を見つける フロリアン・マーゲインによる。
オリジン2: クロワッサンを購入する人を見つける ジル氏。
この質問はGillesと同じバージョンであり、さまざまなコミュニティがプログラミングの課題にどのように対処しているかを確認する実験として、プログラマに再投稿されています。

13
user53019

スコアリングアルゴリズムを使用します。各人はゼロのスコアから始まります。クロワッサンを持参するたびに、スコアを1ずつ増やします。クロワッサンを持参しなかったすべてのチームメンバーのスコアは、1/Nずつ減少します。したがって、スコアが0の場合、チームメンバーは買いすぎでも買いすぎでもありません。

ランダム性がない場合、出席する人のリストからスコアが最も低い人を選択します。

ランダム性を追加するには、リストをスコアで並べ替え、スコアが負のすべてのチームメンバーのリストからランダムに選択します。負のスコアに制限することにより、何週間も「幸運」になることがないようにします。

このアルゴリズムの利点は、履歴記録の保持に依存せず、いつでも新しいチームメンバーを簡単に追加できることです。

クロワッサンを楽しむために存在する人だけのスコアを減らすことにより、比較的一般的な欠席を可能にするように適合させることができます。

26
Gort the Robot

私がこれを選ばなければならないなら、帽子を手に入れ、小さな紙にみんなの名前を一度帽子に入れます。それから毎日、帽子から誰かの名前をランダムに描きます。それが翌日クロワッサンを持ってくる人です。その紙は、「BRINGING CROISSANTS TOMORROW」の下のボードに貼り付けられます。現在ボード上にある紙は捨てられます。

箱も欲しいです。空から始まります。毎日、名前を描く前に、箱の中身を帽子に捨てて、帽子の中の書類を調べて、明日欠席する人を全員削除しました。それらの名前はボックスに入れられます。

名前を描くときが来て、帽子が空の場合は、もう少し紙をはがして全員の名前を一度追加してから、明日欠席する全員の名前をボックスに移動します。

これらの最後の2つのステップのために、同じ名前が一度に複数回ハットに入る可能性があります。たまたま描いた名前がボードに書いてある名前と同じなら、その紙を箱に移してもう一度描きます。

このシステムを選択した言語のアルゴリズムに変換することはそれほど難しくないはずです。

7
Mason Wheeler

アルゴリズム、誤解。 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)
)

誰が買うの?

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

彼らが購入した後:

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

次に設定します:

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

...私は古い学校だからです。

最初のクエリを微調整することによって少しランダム性を追加することはそれほど難しくありません。おそらく、last_purchaseの日付で並べ替えるのではなく、random()を追加します。

6
GrandmasterB

私は実際にこの問題を現実の世界でいくらか解決しなければなりませんでした:

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

何が起こるかというと、「あまりにも多く」ドーナツを購入した人(不運のため、他の人が休暇中に仕事に行くなど)は、十分な買収が行われて「正しい」パーセントに戻されるまでプールから除外されます。購入。

これは、新しい人々を雇うことをよりうまく処理するために拡張する必要があります...

とにかく、この設計は、変数を変更する(誰が参加しているか、誰が参加していないか)およびスケジュールを(実質的に)無限にする必要がある場合に非常にうまく機能しました。追加のボーナスとして、RNGをシードすることで決定論を簡単に作成できます。

4
Telastyn

すでに提示された他のいくつかの回答ほど良くはありませんが、問題を見る別の方法:

  1. 参加しているすべての従業員のリストを作成する
  2. リストを何度も複製する(たとえば、1,000)
  3. リストをシャッフルする

午後ごとに、次の利用可能なクロワッサンブリンガーを選択します。毎朝、クロワッサンブリンガーはリストのトップから名前を消します。

毎日の処理はペンと紙で簡単です。

新入社員 & 終了 卒業生はおそらく新しいリストを作ることによって最もよく扱われるでしょう。 CPUサイクルが再び高額になる場合(または、1億人の従業員と第1世代のArduinoしかいない場合)、元のリストに適切な数のプレースホルダーを追加するのは簡単です。


詳細(リクエストごと)。

このアプローチを任意の長いリストで使用すると、透明性のメリットが得られます。

誰が明日クロワッサンを持ってくるか知っているだけでなく、誰が翌日にクロワッサンを持ってくる予定か知っています。もちろん、時間が経つにつれて、不在などにより、精度が低下します。

紙片を帽子に入れて重みを付ける方法を理解している卑劣な開発者は、クロワッサンを運ぶ義務を回避する機会があまりありません。

処理済みであると主張する非開発者がうなり声を上げると、データを簡単に確認し、間違った結論を導き出し、とにかく駄々をこねることができます。

2
Dan Pichelman

非ランダム

順序付きリストを維持します。購入予定の日に不在の場合は、次に空いている人と入れ替えます。結局、その人は立ち会って、クロワッサンを買うでしょう。したがって、リストの内容は同じままですが、不在によっては人が移動したり上下したりする場合があります。

新しい人は現在の位置の後にリストに挿入されます。終了または終了した人はリストから削除されます。現在位置は毎日1ずつ増加し、最後に達すると最初に戻ります。

これは、公平性を促進するための平均不在時間を説明するのに十分な人数がリストに含まれていることを前提としています。

ランダム

短期的なバイアスがあるため、毎日ランダムな人々を選択することはできません。たとえば、コインを10回めくると、ヘッド8とテイル2が出てきて、短期的に頭がねじ込まれる可能性があります。そのため、公平に保つために人々のバケットを作成する必要があります。

バケットは、ユーザーが過去にcrossiantを購入した回数によって決まります。したがって、この場合は、人と国境を越えた購入の辞書を保存します。 1日目、全員がバケット0にいます。人がクロワッサンを購入すると、次のバケツ、つまり1、2などに割り当てられます。ランダムな部分は、バケツにいる利用可能な人のプールから選んでいます。最初に利用可能なバケットは、購入数が最も少ないものです。バケツに10人いる場合は、1〜10の乱数とクロワッサンを購入する人を選びます。新しい人々は現在の最低のバケツを割り当てられているので、彼らは余分な数の交差点を購入することになりません(彼らはすぐに購入プールにいますが)。最も低いバケットで誰も利用できない場合(それらはすべて存在しない)、次に高いバケットに移動します。たとえば、10人のリストがあるとします。 8日目に、バケット1には8人、バケット0には2人が入っています。バケット0の2人は欠席しています。この場合、バケット1が使用され、最終的に1人がバケット2になります。しかし、バケット2にいる人はおそらくいないため、人々は常に相互に数クロスバイン(バケット)内にいます。しばらくの間購入プール。

微調整を追加して、同じ人物が2日間続けて購入しないようにしたり、処理するEdgeケースをいくつか用意したりすることができますが、これにより、ランダム性の要素が追加され、公平性が保たれます。また、実際のクロワッサンの購入と現在のバケツを分けておくこともできます。人が去ると、永続的に不在としてマークするか、完全に削除することにより、バケットから削除されます。

1
Jon Raynor