このブログは技術系ブログだったことを思い出して、たまには釣り以外のことを書こうと思い立ちました。
SEだったらたまにはあるかもしれないお話。
見積金額と工数を顧客に提示した後で、職種ごとの工数を求められちゃうケース。(そんなにない?)
ちゃんと決めていればよかったのに、勢いよく出しちゃったよみたいなこと、あるんじゃないでしょうか。
見積金額xx円、工数xx人月で、PM、SE、PGとかの工数はそれぞれいくつなのみたいな。
後から『それっぽい』数字を入れるのは意外と大変。
そんな問題はソルバーで解きましょう。
フリーで使えるGLPK(GNU Linear Programming Kit)があれば十分です。
例として、10人月で700万円、PM、SE、PGの人月単価はそれぞれ100万円、80万円、60万円としましょう。
この問題のモデルファイルを作る必要があるので、次みたいな感じで作りましょう。
4行目は目的関数なのですが、とりあえず誤差最小を目指しましょう。
700万円の部分はx1~x3が100倍なので、100倍にしています。
2次式をとって最小化するのがよいのですが、GLPKは2次計画問題は解けないので、目的関数と同じ式で0以上で縛ります。
KOUSU_CONSTRAINTで職種ごとの工数の合計が10人月になるように縛っています。
100倍の数値になっているのは、職種ごとの工数を0.01刻みで求めたいので、100倍にしています。
さて、実行してみましょう。
SEは0人月でPGが7.5人月です。流石にPMとPGだけというのもあるので、
もうちょっといい感じにするために、先ほどのモデルファイルに次の行を追加して再実行しましょう。
めでたしめでたし。
素晴らしいツールのクズみたいな使い方なので、見積時にはちゃんと考えておきましょう。
SEだったらたまにはあるかもしれないお話。
見積金額と工数を顧客に提示した後で、職種ごとの工数を求められちゃうケース。(そんなにない?)
ちゃんと決めていればよかったのに、勢いよく出しちゃったよみたいなこと、あるんじゃないでしょうか。
見積金額xx円、工数xx人月で、PM、SE、PGとかの工数はそれぞれいくつなのみたいな。
後から『それっぽい』数字を入れるのは意外と大変。
そんな問題はソルバーで解きましょう。
フリーで使えるGLPK(GNU Linear Programming Kit)があれば十分です。
例として、10人月で700万円、PM、SE、PGの人月単価はそれぞれ100万円、80万円、60万円としましょう。
この問題のモデルファイルを作る必要があるので、次みたいな感じで作りましょう。
var x1 integer; # PM var x2 integer; # SE var x3 integer; # PG minimize ERROR: 700000000 - 1000000 * x1 - 800000 * x2 - 600000 * x3; s.t. KOUSU_CONSTRAINT: x1 + x2 + x3 = 1000; s.t. MONEY_CONSTRAINT: 700000000 - 1000000 * x1 - 800000 * x2 - 600000 * x3 >= 0; end;最初の3行で工数用の変数を定義します。それぞれ整数値が入るようにします。
4行目は目的関数なのですが、とりあえず誤差最小を目指しましょう。
700万円の部分はx1~x3が100倍なので、100倍にしています。
2次式をとって最小化するのがよいのですが、GLPKは2次計画問題は解けないので、目的関数と同じ式で0以上で縛ります。
KOUSU_CONSTRAINTで職種ごとの工数の合計が10人月になるように縛っています。
100倍の数値になっているのは、職種ごとの工数を0.01刻みで求めたいので、100倍にしています。
さて、実行してみましょう。
# glpsol -m <モデルファイル名> -o <出力ファイル名>出力されたファイルを見ると、次のようになっていると思います。
Problem: kousu Rows: 3 Columns: 3 (3 integer, 0 binary) Non-zeros: 9 Status: INTEGER OPTIMAL Objective: ERROR = 1000 (MINimum) No. Row name Activity Lower bound Upper bound ------ ------------ ------------- ------------- ------------- 1 ERROR 0 2 KOUSU_CONSTRAINT 1000 1000 = 3 MONEY_CONSTRAINT -7e+08 -7e+08 = No. Column name Activity Lower bound Upper bound ------ ------------ ------------- ------------- ------------- 1 x1 * 250 2 x2 * 0 3 x3 * 750 Integer feasibility conditions: KKT.PE: max.abs.err = 0.00e+00 on row 0 max.rel.err = 0.00e+00 on row 0 High quality KKT.PB: max.abs.err = 0.00e+00 on row 0 max.rel.err = 0.00e+00 on row 0 High quality End of outputx1のActivityが250となっています。これはx1(PM)が2.5人月であることを示しています。
SEは0人月でPGが7.5人月です。流石にPMとPGだけというのもあるので、
もうちょっといい感じにするために、先ほどのモデルファイルに次の行を追加して再実行しましょう。
s.t. X1_CONSTRAINT_LOWER: x1 >= 50; s.t. X2_CONSTRAINT_LOWER: x2 >= 150; s.t. X3_CONSTRAINT_LOWER: x3 >= 100; s.t. X1_CONSTRAINT_UPPER: x1 <= 200; s.t. X2_CONSTRAINT_UPPER: x2 <= 500; s.t. X3_CONSTRAINT_UPPER: x3 <= 800;出力された結果をみると、PMが1.75人月、SEが1.5人月、PGが6.75人月となっています。
Problem: kousu Rows: 9 Columns: 3 (3 integer, 0 binary) Non-zeros: 15 Status: INTEGER OPTIMAL Objective: ERROR = 1000 (MINimum) No. Row name Activity Lower bound Upper bound ------ ------------ ------------- ------------- ------------- 1 ERROR 0 2 KOUSU_CONSTRAINT 1000 1000 = 3 MONEY_CONSTRAINT -7e+08 -7e+08 = 4 X1_CONSTRAINT_LOWER 175 50 5 X2_CONSTRAINT_LOWER 150 150 6 X3_CONSTRAINT_LOWER 675 100 7 X1_CONSTRAINT_UPPER 175 200 8 X2_CONSTRAINT_UPPER 150 500 9 X3_CONSTRAINT_UPPER 675 800 No. Column name Activity Lower bound Upper bound ------ ------------ ------------- ------------- ------------- 1 x1 * 175 2 x2 * 150 3 x3 * 675 Integer feasibility conditions: KKT.PE: max.abs.err = 0.00e+00 on row 0 max.rel.err = 0.00e+00 on row 0 High quality KKT.PB: max.abs.err = 0.00e+00 on row 0 max.rel.err = 0.00e+00 on row 0 High quality End of output先ほどよりはましになりました。x1~x3の上限下限を適当に設定すれば、それっぽい数字が簡単にでます。
めでたしめでたし。
素晴らしいツールのクズみたいな使い方なので、見積時にはちゃんと考えておきましょう。