Hatena::ブログ(Diary)

xmallocのプログラミングノート

2010年04月03日

俺にとってプログラミングは仕事だ、それ以外の何でもない

またお前か。

 岡嶋氏の人材獲得作戦で用いられた問題はこちらに公開されており、ソーシャルブックマークなどでさまざまな意見が寄せられていますが、中には、「これは知識問題だ」「業務には必要ない」「これでプログラマーの実力が測れるのか?」などの意見もありました。なお、これらの意見について、岡嶋氏は以下のように述べています。

この問題ができたから優秀な人材とは限らないけれど、できない人は“ほぼ確実に”優秀ではない、のではないかと。

http://www.itmedia.co.jp/enterprise/articles/1004/03/news002.html

で、また問題か。

麻雀の手牌が入力として与えられたとき、「待ち」を出力するプログラムを書いてください。

  • 字牌なし・萬子のみの想定、つまり、いわゆる「チンイツ」限定で結構です(プログラミングの本質的にはこの限定でまったく問題ないため)
  • 1〜9の数字13個からなる文字列を受け取り、できている順子・刻子・アタマを()、待ちの部分を[]でくくって出力してください
  • 面前かつ槓子は存在しない前提でOKです
  • ()[]の出力順は自由ですが、順序だけが違うものは同一視してください(例:111222を刻子2つで構成するとき、(111)(222)が(222)(111)に入れ替わるだけのものは同一解答とします)
  • 多面待ちのときも含めすべての待ちを出力してください
  • 待ちがないときは何も出力しないでください
http://www.itmedia.co.jp/enterprise/articles/1004/03/news002_2.html

答えは後に載せるので、先に言いたいことを言っとく。


仕事の内容によっては、こうした論理的思考力などの質的な部分より、量的な部分、つまりマンパワー自体が要求されるケースもあることは否定しませんが、そうした仕事は長い目で見ると自動化される対象であり、少なくとも「面白い仕事」とは言い難いといえるのではないかと思います。この議論については割愛しますが、興味のある方は、例えばスラッシュドット・ジャパンの「プログラマーの力量を見極める質問」をご覧いただければと思います。

http://www.itmedia.co.jp/enterprise/articles/1004/03/news002.html

まず、プログラミング能力が高いことは素晴らしいと思う。ただそういうすごいプログラマーは、Google とかそういう人を飼っとけるだけの環境にある企業に行けばいいと思うし、そういうすごいプログラマーだけで社員を揃えるという考え方もありだとは思う。でもそんな仕事ばかりじゃない。その辺はプログラマーの力量を見極める質問に似たような意見書いてる人がいたので引用させてもらう。

Re:個人的には無縁な感じ

metta (20740) : 2010年03月04日 14時00分 (#1727419) 日記


オーバースペックくれくれ厨の会社が多そうですよね。


googleのように、全く新しいコンセプトのサービスを開発する会社なら

知能指数の高い研究者のような人を取る必要があるかもしれませんが


帳票設計のようなものは、どぶ板サービス業ですから

頭の良い人にはつとまりません。

http://slashdot.jp/developers/article.pl?sid=10/03/04/0330233

うちの話で言うと、優秀な人ばっかで面子揃えちゃうと俺の営業能力とか人脈では仕事が取れん、というかあんまりそういう仕事を取る気もあんまり無い。俺からしたら、仕事として「面白い」よりも「儲かる」方が重要。(一般的なプログラマーなら)誰でもできる内容で、需要があるような仕事がいい。受注が増えたら人増やして受注量を増やせるようなのが理想的。

なんでまあ、優秀な人しかできないような仕事は基本的に要らない。だいたいだ、どの程度難しい話なのかまったく理解してないまま予算だけ決めて仕事振ろうとするクライアントも結構多かったりして、例えば「やほーみたいなサイトが欲しい、30万で」とか、それに類似したムチャな話は一度は聞いたことあると思うけど、仮にそういう商談に超優秀を連れていって、その人が「はいできます」なんて答えちゃったら商売上がったりだ。そのクライアントからのムチャぶりをずっと受けざるを得なくなる。


例えば工場のラインのように、設計する人と、実際の作業をする人が明確に分かれてて、設計する人に優秀な人がいたら超いいよね、という話は分かる。設計する人が優秀なら作業の手間が省けるだろうし、生産性も上がるだろう。

ただソフトウェア開発してる会社だと、人月工数計上してるところがほとんどだと思うので、単位時間あたりの生産性が上がったところで請け負う側の利益にならないから、生産性をあげる利点がほとんどない。前に一度、生産性を上げて単位時間あたりの単価を上げつつ見積り金額を下げるってことを試してみたことがあるんだけど、デスマサビ残前提のブラックと相見積りになると絶対勝てないのが分かったのでやめた。こちらの営業手法に問題があったのかもしれないけど、こっちが下げると他も下げるということになってるみたいで、細かい話は省略するけどまめに営業したりする方が売上的には全然いい。

そういう請負体質が悪いんだと言う話もあるし、その話自体は分かるけど、請負側にどうしろ?という話ですよ。

極論すれば、誰も見たことも無いすごい商品よりも、食べ物の方が売りやすい。だからって飯屋になるつもりは無いけど、何かよく分からんものに金払う人は少数派。特に相手が企業だと、向こうもそれを手に入れることによって何らかの利益が出ることを期待してるから、何か分からんものを買う訳ない。ということで新規性のあるものを作っても広告とかにそれなりに金を使わないと売れない。うちにそんだけの金は無い。なら新規性のあまり無い、それなりに需要もあって競合もあるけど、とりあえず売れそうなものに手を出さないといけない。あんま新規性が無いものなら誰でも作れる。だからすごい優秀な人とか、来てくれるんならうれしいですけど、それなりに払うものは用意できないですよ、ということです。

まあしょうもない話ですが、現実なんてそんなもんです。

書きたかったのは優秀じゃなくても全然構わない、ってことなんだけど、話だいぶずれたような気はするwwまあ、優秀な人は優秀な人がいれる場所にいけばいいってことですよ。日本にどんだけそういうとこあるのか知らんけど。


 問題についてはネガティブな意見も多くありましたが、岡嶋氏をより失望させたのは、この問題を解けた方が驚くほど少なかったという点です。この点について、岡嶋氏は以下のように述べています。

とくに理解できないのはLv0,1(編注:ギブアップまたは入力を読んでメモリ上の構造に落とすところまでできた方)の人たちが開発現場でどんな仕事をしてるのかだな。ぶっちゃけ、3時間かけてこれということはコードを書く能力は0でしょ? なのに経歴書にはいろいろなプロジェクトにプログラマとして関わった経験があるようだし、こんな人でも何とか使わないといけない状況に置かれた彼らの上司がかわいそうでならない(原文ママ)。

http://www.itmedia.co.jp/enterprise/articles/1004/03/news002.html

とりあえず、ウェブのドキュメントを全角スペースで字下げするのは気持ち悪い。CSS2の:first-letterで同じ効果出せなかったっけ?とくに理解できないのは、こんな気持ち悪い文章書くライターを飼ってるITMediaの上司が可哀想、って感じwwww

ま、俺が理解できないのは、Lv0,1の人たちをうまく御すこともできないのに上司ずらしてるバカがいることだ。


んで答え

# -*- coding: utf-8 -*-

import sys

TANKI = 0
ATAMA = 1
JYUNTU = 2
RYOUMEN = 3
KOKUTSU = 4

def answer(mach, te, i):
    ans = []
    ma = []
    for j in xrange(len(te)):
        if j in (i):
            ma.append('[%s]' % ''.join([str(x) for x in te[j]['pi']]))
        else:
            ans.append('(%s)' % ''.join([str(x) for x in te[j]['pi']]))
    mach.append(''.join(ans) + ''.join(ma))

def scan(mach, te):
    atama = []
    ryoumen = []

    for i in xrange(len(te)):
        t = te[i]

        # 単機のときは他は順子と刻子
        if t['type'] == TANKI:
            ok = True
            for j in xrange(len(te)):
                if j == i:
                    continue
                t = te[j]
                if t['type'] not in (JYUNTU, KOKUTSU):
                    ok = False
                    break
            if ok:
                answer(mach, te, [i])
            return

        elif t['type'] == ATAMA:
            atama.append(i)
        elif t['type'] == RYOUMEN:
            ryoumen.append(i)

    # 両面待ち
    if len(atama) == 1 and len(ryoumen) == 1:
        answer(mach, te, [ryoumen[0]])

    if len(atama) == 2 and len(ryoumen) == 0:
        answer(mach, te, atama)

def find(pi, num):
    for i in xrange(len(pi)):
        if pi[i] == num:
            return i
    return None

def pong(pi, mach, te=[]):
    if len(pi) == 0:
        scan(mach, te)
        return

    i = pi.pop(0)

    # 単機は一個しかないはず
    ntanki = 0
    for x in te:
        if x['type'] == TANKI:
            ntanki += 1
            break
    if ntanki == 0:
        te.append({'type':TANKI, 'pi':[i, ]})
        pong(pi, mach, te)
        te.pop()

    if len(pi) > 0:
        if pi[0] == i:
            j = pi.pop(0)
            # 頭
            te.append({'type':ATAMA, 'pi':[i, j]})
            pong(pi, mach, te)
            te.pop()
            # 順子
            if len(pi) > 0 and pi[0] == i:
                k = pi.pop(0)
                te.append({'type':JYUNTU, 'pi':[i, j, k]})
                pong(pi, mach, te)
                te.pop()
                pi.insert(0, k)
            pi.insert(0, j)

        # 両面
        ind1 = find(pi, i + 1)
        if ind1 is not None:
            j = pi.pop(ind1)
            te.append({'type':RYOUMEN, 'pi':[i, j]})
            pong(pi, mach, te)
            te.pop()

            ind2 = find(pi, j + 1)
            if ind2 is not None:
                k = pi.pop(ind2)
                te.append({'type':KOKUTSU, 'pi':[i, j, k]})
                pong(pi, mach, te)
                te.pop()
                pi.insert(ind2, k)
            pi.insert(ind1, j)

    pi.insert(0, i)

def jang(line):
    pi = [int(ch) for ch in line.strip()]
    pi.sort()
    mach = []
    pong(pi, mach)

    for x in mach:
        print x

if __name__ == '__main__':
    def do_test(s):
        print '%s' % s
        jang(s)
        print

    do_test('1112224588899')
    do_test('1112223335559')
    do_test('1223344888999')
    do_test('1112345678999')
    do_test('1122455677889')

んで実行結果。

1112224588899

(111)(222)(888)(99)[45]


1112223335559

(111)(222)(333)(555)[9]

(123)(123)(123)(555)[9]


1223344888999

(234)(234)(888)(999)[1]

(123)(44)(888)(999)[23]

(123)(234)(888)(999)[4]


1112345678999

(11)(345)(678)(999)[12]

(11)(123)(678)(999)[45]

(11)(123)(456)(999)[78]

(123)(456)(789)[11][99]

(111)(345)(678)(999)[2]

(111)(456)(789)(99)[23]

(111)(234)(678)(999)[5]

(111)(234)(789)(99)[56]

(111)(234)(567)(999)[8]

(111)(234)(567)(99)[89]

(11)(345)(678)(999)[12]

(123)(11)(678)(999)[45]

(123)(11)(456)(999)[78]

(123)(456)(789)[11][99]


1122455677889

問題始めたのが14:23、終わったのが15:56、1時間半ちょい。ブログにコード貼るにあたって空行消したりしたけど、その時間は入れてない。


問題の感想。

ぶっちゃけ麻雀よく分かんねーんだよね。100%知らない訳じゃないけど、付き合いで何度かしただけなんで、未だに問題の意味いまいち分かってない。ほんと、もっと前提知識の要らん問題にしてくれよ。とりあえず「面前かつ槓子」って何?存在しない前提らしいからスルーしたけど、これが仕事じゃスルーできねーって。


追記

カンチャン待ちというのもあるとコメントで指摘されたのでパッチ付けときます。

--- ans.py.orig	2010-04-04 18:26:00.000000000 +0900
+++ ans.py	2010-04-04 18:30:42.000000000 +0900
@@ -7,6 +7,7 @@ ATAMA = 1
 JYUNTU = 2
 RYOUMEN = 3
 KOKUTSU = 4
+KANCHAN = 5
 
 def answer(mach, te, i):
     ans = []
@@ -21,6 +22,7 @@ def answer(mach, te, i):
 def scan(mach, te):
     atama = []
     ryoumen = []
+    kanchan = []
 
     for i in xrange(len(te)):
         t = te[i]
@@ -43,13 +45,20 @@ def scan(mach, te):
             atama.append(i)
         elif t['type'] == RYOUMEN:
             ryoumen.append(i)
+        elif t['type'] == KANCHAN:
+            kanchan.append(i)
 
     # 両面待ち
-    if len(atama) == 1 and len(ryoumen) == 1:
-        answer(mach, te, [ryoumen[0]])
-
-    if len(atama) == 2 and len(ryoumen) == 0:
-        answer(mach, te, atama)
+    if len(kanchan) == 0:
+        if len(atama) == 1 and len(ryoumen) == 1:
+            answer(mach, te, [ryoumen[0]])
+
+        if len(atama) == 2 and len(ryoumen) == 0:
+            answer(mach, te, atama)
+
+    # カンチャン待ち
+    elif len(kanchan) == 1 and len(atama) == 1 and len(ryoumen) == 0:
+        answer(mach, te, kanchan)
 
 def find(pi, num):
     for i in xrange(len(pi)):
@@ -108,6 +117,15 @@ def pong(pi, mach, te=[]):
                 pi.insert(ind2, k)
             pi.insert(ind1, j)
 
+        # カンチャン待ち
+        ind1 = find(pi, i + 2)
+        if ind1 is not None:
+            j = pi.pop(ind1)
+            te.append({'type':KANCHAN, 'pi':[i, j]})
+            pong(pi, mach, te)
+            te.pop()
+            pi.insert(ind1, j)
+
     pi.insert(0, i)
 
 def jang(line):
@@ -130,3 +148,4 @@ if __name__ == '__main__':
     do_test('1223344888999')
     do_test('1112345678999')
     do_test('1122455677889')
+    do_test('1231231234479')

パッチ分の処理結果。

1231231234479

(11)(123)(234)(234)[79]

(111)(222)(333)(44)[79]

(123)(11)(234)(234)[79]

(123)(123)(123)(44)[79]

名無し名無し 2010/04/04 16:08 カンチャン待ち(「四■六」みたいに順子の真ん中が待ち)は?

xmallocxmalloc 2010/04/04 18:44 そういうのもあるんすか。
出力例になかったので抜けてますね。
とりあえずパッチ足しときました。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/xmalloc/20100403/1270284960