当社は、社員をランダムなグループ(各4人)に分け、ビデオハングアウトをセットアップする実験を行ってきました。コーヒーマシンの周り、ランチでの順場待ち、またはプリンターを待っている間に時々起こるセレンディピタス(偶然)の出会いに代わるものとしてこれを行っています。また、単純に社員にお互いのことを知ってもらいたいのです。
そのために、私はコードを書くことになりました。コアになるのは、_n個の要素を複数のグループに分け、少なくともサイズgにして各グループの人数を最小限に抑えることでした。_例えば、オフィスに15名の従業員がいる場合、5、5、5の3つのグループに分けられ、16名の場合は4、4、4、4、17名の場合は、4、4、4、5になります。
私は最初に次のようなコードを書きました(Python):
groups = [g] * (n//g)
for e in range(0, n % g):
groups[e % len(groups)] += 1
1行目は n//g
(//
は整数の除算)サイズ g
の行列を算出します(たとえば、 g == 4
、 n == 17
は、 groups == [4, 4, 4, 4]
になります)。 「for
ループ」は、「余り」でグループのサイズ gで割り切れなかったものを扱います。たとえば、g == 4
、n == 17
の場合は [4、4、4、 4]
のどこかに「余り1」を加える必要があり、最終的にグループは [5, 4, 4, 4]になります。
「 e % len(groups)
」が必要なのは、等しい数でグループに分けた時に、余りの方が、算出されたgroupsエントリーよりも大きい場合があるからです。たとえば、 g == 4
、 n == 11
の場合は、groups は最初は [4, 4]
になりますが、余りが「 3
」になってしまい、それをたった2つの groups
エントリーに分けなければなりません。
それで、上記のコードが機能し、以下に示すのが様々なサイズの n
(そして g == 4
)のアウトプットです。
4 [4]
5 [5]
6 [6]
7 [7]
8 [4, 4]
9 [5, 4]
10 [5, 5]
11 [6, 5]
12 [4, 4, 4]
13 [5, 4, 4]
14 [5, 5, 4]
15 [5, 5, 5]
16 [4, 4, 4, 4]
17 [5, 4, 4, 4]
しかし、このコードには苛立ちを感じました。なぜなら、私は各グループにいくつの要素(elements)があるべきかを理解するための簡単な式が必要だと感じたからです。この問題についてしばらく頭を悩ませた後、よく役立つ解決方法をここでも使ってみることにしました。問題をシンプルで単純なものにする、もしくは、少なくともソリューションをシンプルで単純なものにすることです。そこで、以下のようなコードを書きました。
groups = [0] * (n//g)
for i in range(n):
groups[i % len(groups)] += 1
これは本当に簡単な実装です。私はこれがn 回ループするため好きではないのですが、視覚化するのを助けてくれます。たとえば、 g == 4
と n == 17
を考えてみます。このループは、グループの各エントリーを次のように埋めていきます(四角はグループの中のエントリーを表し、四角の中にある数字は、ループによって増加するi値を表します)。
そして、グループは最終的に [5, 4, 4, 4]
になります。これで視覚化されたのは、 groups[i]
が何回増加したかは、i番目の要素で何回forループが、「ループ」しなければならなかったかによるということです。これは、ループを使用しなくても簡単に計算することができます。
というわけで、コードは以下のようなシンプルなものになりました。
groups = [1+max(0,n-(i+1))//(n//g) for i in range(n//g)]
私にとっては、この方がもっと満足するものでした。 n//g
はグループのサイズで、これがループがグループの各エントリーをアップデートする回数を決めます。各エントリーは1 + max(0, n-(i+1))//(n//g)
.のようにセットされます。これを次のように考えることができます:
1. 1は、グループのエントリーに最初に置く要素です。
2. max(0, n-(i+1))
は、iに達するまでグループの各要素に1つずつ置いていって、残った数です。これを、 n//g
で割ると、要素が均等に分けられるまで(上記の簡潔なループを参照)、何回ループする必要があるかがわかります。
2番目に書かれていることが不明瞭な場合は上記の図を考え、 groups[0]
( n == 17
と g == 4
)で具体的に考えてみてください。まず、 1
を groups[0]
に配置し、16を均等に分けることにします。単純に分けると、4回ループするので、16÷4で算出された要素をgroups[0]に加える必要があるため、5になります。
次に、 groups[1]
に移動し、1を置きます。15の要素が残っていて、これを均等に分けます。15÷4(整数除算では3)になるので、 groups[1]
は4になります。という風に続けていきます。
そして、そのソリューションは満足のいくものでした。端的に グループをワンショットで算出できました。もちろん、考えすぎかもしれませんし、他の人はこれよりも他のソリューションの方が明確で管理しやすいと考えるかもしれません。