日記

問題: 積み木を10個積み上げるのにかかる時間は

一つ積み上げる毎にかかる時間を 1 とすると当然全体では 10 になります。

しかし一定の確率で積み木が崩れるとするとどうなるでしょうか。

数式を考えればバチーンと答えが出せるのでしょうが、そちらは不慣れなのでプログラムで試行してみたいと思います。

プログラムの概要

処理の大雑把な流れは次の通りです。

  1. 積み木を積んでみる
  2. 積んだ積み木より下の積み木の安定度を掛け算する(例えば安定度0.7の積み木が5つ積まれていたら、一番上のものを除いて 0.7×0.7×0.7×0.7 = 0.2401 を計算する)
  3. 2で計算した数値(安定度)が0以上1未満の乱数(ぐらつき)より大きい場合は何もしない。それ以外の場合は一番上の積み木を取り除く
  4. 積み木の高さが10になるまで以上の処理を繰り返す

最終的に積んだ回数を掛かった時間として採用します。

ただ、乱数を使用しているため、1回の試行ではばらつきが大きくなりすぎますので、1000回試行して平均を出すようにしました。

当初は最後に積んだ積み木よりも下で崩れる場合があるような実装だったのですが、それだと積む回数が爆発しかなり計算に時間がかかるため、最後に積んだもの(一番上のもの)だけ崩れるような実装にしました。

これは実際の積み木には合ってない実装だと思いますが、計算時間以外にもちょっとした理由があってこのようにしました。(これについては後述します)

ちなみに積み木の形状としては直方体を想定しており、個別の積み木自体に固有の安定度は存在しない、要は積む人の腕次第という感じのものを想定しています。

プログラムは JavaScript で書きましたが ES6 の機能をバリバリ使ってますので動作しない環境があるかもしれません。

まず安定度1で計算

これはまぁ絶対に崩れないということですから、かかる時間は高さと同じになります。

答え: ?

0.9~0.5まで下げてみる

答え: ?

答え: ?

答え: ?

答え: ?

答え: ?

乱数を使用してますのでばらつきは多いのですが、安定度0.9の場合で16前後、0.8で33前後、0.7で80前後、0.6で245前後、0.5で1000前後になるようです。

まとめると次の通り。

安定度だいたいの時間安定度1の場合との時間の比率
1101
0.9161.6
0.8333.3
0.7808.0
0.624524.5
0.51000100.0

安定度0.9の場合に着目すると、安定度が10%下がるだけで時間は160%にもなってしまうということが見て取れます。

0.5の場合になると、50%下がるだけで時間は10000%(100倍)にもなってしまうということになりますね。

なぜこんなことをやったか

一番上の積み木しか崩れないという現実にはあまり無い仮定をしてまで何故こんな実験をしたのか、不思議に思われるかもしれません。

実は今回の積み木シミュレーターは、現実のシステム開発に例える意図で作成しました。

現実のシステム開発 = 積み木だ!という主張をしたいわけでは無いのですが、システム開発も積み重ねが重要ですので似た側面もあると思います。

ただシステム開発は物理的な積み木と違って、ちょっとしたミスで全部が崩れる、といったことは(無くはないと思いますが)基本的にあまり無いと思います。

例えばちょっとしたミスでソースコードの重要な部分を消してしまったとしても、たいていバージョン管理システム等で復元することが可能でしょう。(ちょっとしたミスで本番のデータ全消しして復元できなくなった話は稀に聞きますが…w)

でも新たに機能を追加しようとしたときに、既存の処理との兼ね合いで簡単には追加できないという場合はよくあります。(既存の部分の実装がマズかったりするとよくあります)

これが一番上の積み木しか崩れないという仮定をした理由です。

つまり?

先の表を再掲します。

安定度だいたいの時間安定度1の場合との時間の比率
1101
0.9161.6
0.8333.3
0.7808.0
0.624524.5
0.51000100.0

この安定度ですが、これを「何らかの機能を作成するために必要な能力に対し、実装者の実際の能力がどれくらいあるか」という数字として見ると、結構実際のシステム開発でもよくある「なかなか思うように開発が進まない現象」をある程度説明出来るのではないかと思いました。

ここでは「何らかの機能を作成するために必要な能力に対し、実装者の実際の能力がどれくらいあるか」は長すぎるため単に「実力」と言い換えますが、ある機能の実装について実力1の人は、今回の例で言えば、途中で大きなやり直しをすることなく普通に完成させれる人と言えます。

こういう人の進捗は一定しているので、だいたい計画通りに終わります。

次に実力が0.9の人を考えます。

実力自体は1の人と大きな差がありませんが、ここで重要なのは、この0.9とかいう数字は「これから作成しようとしている機能を実装するのに必要な能力」を基準としているということです。

そのため少しでも1を下回ると、実装途中でやり直しが発生する可能性があります。

やり直しが発生すると当然書く行数のトータルは増えますので、さらにそこでやり直しが必要になるようなミスをする余地が増えます。

そんなこんなで実力的にはそこまで差がないのに、完成させれるかどうかで見ると大きな差が出る、ということがありえます。

さらに下がって実力が0.5とかになると、とんでもないことになります。

これ、現実の開発の現場でも思い当たる点が多々あります。

開発の現場では

実際の開発の現場では実力1以上の人ばかりを集めるのは難しいです。

普通に実力が0.5の方も居たりしますし、実力が0.5なのに1.5くらいあるよとハッタリをかます人もいます。

0.5もあれば良い方、みたいな現場もあります。

しかもこの実力という数字は、今回の定義では当然人によっても変わりますが、何をさせるか(どんな機能を実装させるか)でも変わりますので、他で1.2の実力の人が他では0.8しかない、といったことが普通にありえます。

でもマネジメントをやる方の中には「まぁそんな細かい話は抜きにして、単純に全員稼働させるのが一番早い」と考える方がいるかもしれません。

というか実際に見たことがあります。

気持ちは非常によく分かるのですが、今まで色々な開発に参加した経験から、何となく「単純に全員稼働させるのが一番早い」というわけでは無さそうだということを肌で感じていました。

むしろ全員稼働させるから遅くなると感じることもありました。

それをうまく説明する方法を今回思いついたのでこの記事を書いているというわけです。

全員稼働の罠

スケジュールはギリギリ、猫の手でも借りたい、とんなとき「全員で乗り切るぞ!」と決定したとします。

しかし先の表に当てはめると、実力0.5の人は1の人の100倍の時間がかかるため、現実的には実装不可能とみなすことになると思います。

しかしそれでもそういう人を活用するにはどうするか?、と考えて「ある機能の前半を0.5の人に任せて後半を1の人に仕上げてもらう」というやり方を思いついたとします。

実際にそういったやり方で進行を行うマネジメントの方がいらっしゃいます。

ではそれを今回の積み木シミュレーターで実際に確認してみましょう。

まず、前半を0.5の人にやってもらいます。

答え: ?

これは30前後になるようです。

30前後というと0.8の人が完成させるスピードより多少早いということになります。

もちろんこれで完成するのは半分なので、単純計算でも2倍にして考える必要があります。

じゃ2倍の60で考えると、なんと0.7の人が完成させるスピードより早くなってしまいます。

もちろん後半の方が難易度が高いのでそんな単純計算は成り立たないのですが、どうもこのあたりをしっかり区別出来てる方はこのようなやり方をやってる方の中ではあまり見たことがありません。

ま、それはともかく、0.5の人が作った前半に対して実力1の人に後半を実装してもらいましょう。

「0.5の人で30くらい時間がかかったけど、残り半分だから1の人に任せれば残り5の時間で完璧なものが仕上がってくるはず」なんてことを無意識に考えてしまう方もいらっしゃるんじゃないでしょうか。

答え: ?

しかしこれはどうも190前後になるようです。

この数字は前半込みになりますので、後半の実力1の人でかかる時間は160ぐらいになるということになります。

160…めっちゃ遅いですね。

そんな妙なことさせずに普通に実装させていたら今頃同じような機能が16個は実装できていたはずです。

何が問題か

現実ではゴールラインが曖昧で、例えば品質を下げるなどして時間を誤魔化してその場をしのいだり等、取れる手立てがいくつかありますので、ここまであからさまな差がつくことは少ないと思いますが、「見た目は半分くらい出来てるけどプログラムの構造がめちゃくちゃ」なんてものを引き継ぐと、完成させるのにいつもより時間がかかりますし、精神的な負担も大きいのは間違いありません。

一旦捨てて作り直せれば良いんですけどね、すでにまぁまぁのコストが掛かってるとなかなか捨てるという判断が出来ないマネジメントの方も多いと思います。

しかしその「既存の酷い部分」を生かしたまま、それに合わせて上積みしていくというのは、想像以上に難しいものです。

解決策

この全員稼働の問題を解決するのは実は簡単です。

表を再再掲しますが、

安定度だいたいの時間安定度1の場合との時間の比率
1101
0.9161.6
0.8333.3
0.7808.0
0.624524.5
0.51000100.0

この例で言うと、基本的には0.8までの現実的な時間内に完成させれる人にひとりで実装してもらうのが良いと思います。

しつこいですが重要なのは0.8などの数字は、あくまでもこれから実装しようとしている機能に対する数字ですので、人の得意不得意をよく見極める必要があります。

ただまぁそこまでやらなくても一つの簡単な原則を守るだけで速度低下は抑えられます。

さっきの前半0.5、後半1の例を前半0.8、後半1でやってみましょう。

答え: ?

答え: ?

すると前半は8前後で、全体で23前後になるようです。

全部0.8の人にやらせると33でそこから10も早くなるわけですから、1の人が暇してたら検討しても良いかもしれませんね。

まぁ猫の手も借りたいときに1の人が暇してることは基本無いと思いますが。

ここで重要なのは後半にかかる時間です。

1の人なら本来5で終わる部分に 23 – 8 = 15 も掛かっています。

0.8の人も実力はそれなりにあるのですが、やはり想定が甘い部分が少しあって、そういうところに合わせて後半を作ったり、そういうところを修正しながら後半を作ったりが必要になるはずですので、いつもより時間がかかるのは不思議ではありません。

こういう引き継ぎを、特別な理由があれば別ですが、進行のスピードを求めてやるべきではない、ということになると思います。

じゃあさっきの逆はどうでしょうか。

前半を1の人にやってもらって後半を0.8の人にやってもらいます。

答え: ?

これは13前後になるみたいですが、かなりいい感じですね。

せっかくなので、前半を1の人にやってもらって後半を0.5の人にやってもらうパターンも試してみます。

答え: ?

これは36前後になるみたいです。これも良いですね。

1の人に先にやってもらうと、1の人の時間は5だけで済みますし、後半の人の時間もひとりで全部作るよりは大幅に少なくて済みます。

これが答えです。

結局当たり前の話

この効率的なやり方は、計算はしたことはなくても当たり前のように実施している組織も多く存在すると思います。

例えば実力が高い人に土台部分を担当してもらって、実力がそうでもない人には枝葉の部分を担当してもらうというやり方ですね。

プログラミングの難しさ、または簡単さを肌感覚で分かってる方には今回の記事は具体的な数値はともかく当たり前のことしか書かれていないと感じると思います。

ですが世の中にはこの逆のやり方をやる組織があったりするんですね。

まぁ確かに、開発の初期は既存の部分との兼ね合いなどを考える必要が無いので実力がちょっと足りてない人でもそれなりの進捗を出せたりします。

ただしこの進捗は「実力が足りている人が最後まで見通して逆算したもの」に沿って作られたものでは無いので、見かけ上の進捗と言えます。

見かけ上5割出来てるけど品質的にも問題のないものをちゃんと完成させるには作り直した方が早い、なんてことになりますとその5割の進捗は実際は進捗0ということになります。

進捗0に出来る(作り直しが許可されている)のならまだ良い方です。

作り直せない場合はまさに今回の積み木シミュレーターの例のように、実力がある人の時間を非常に非効率な方法で使ってしまうということになり、前半部分の進捗を真面目に計算したら実際はマイナスであったということもあり得ます。

組織ごとに色んな事情があると思いますので、非効率な方法が絶対ダメだとは言えないのですが、少なくとも開発スピードを早めようとしてごちゃごちゃやった挙げ句、今回説明したようなことが要因となって逆に開発スピードが落ちるようなことになったら、そら「何やっとんねん」という話になります。

ということで、シミュレーターはともかく今更な話を長々と書いてしまいました。

ま、出来ることなら時間はもちろん精神的な点でも効率的に進めたいものですね。

コメントを残す

メールアドレスが公開されることはありません。



※画像をクリックして別の画像を表示

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください