カメヲラボ

主にプログラミングとお勉強全般について書いてます

きれいなPythonプログラミング

久しぶりに本出ます

なんとPython本です。Pythonに魂を売りました。ウソです。2月15日発売だそうです。

なんでPython本?

昨年なんとなく見て、面白かったので日本語版があると良いかなーと思って書いた次第です。Pythonに魂を売ったわけではないのですが、この4~5年くらい、仕事上でも研究上でもPythonでコードを書く機会が非常に増えました。機械学習が流行ったのと、(職業としての)プログラマー以外の人がプログラムを書くことが増えたからかなと個人的には感じています。

みんなプログラム書けるよね

最近感じるのは、特に若い人たちは皆普通にプログラミングできるということ。理工系の学部でない学生さんたちで、プログラミングのプの字も知らないような人でも、必要になってから2~3か月もあれば普通にコードを書いているし、いろんなツールが使えるようになっています。この10年くらいで初心者向けの学習環境が格段に良くなっていると思います。良いプログラミング本もたくさんありますからね。そんなわけで、今さら初心者用の本を書き下ろす気もないし、翻訳するのもそれほど気が進みません(めっちゃ良いのがあればやりたいですが)。

中~上級者の本はもっとあった方がいい

本書は、プログラミング初心者が「もうワンランク上」を目指すための内容です。たとえば、

  • 本に書いていないエラーが出ることが多くなった
  • 新しいライブラリを使うのにやたら手間取る
  • 自分の書いたコードの量がかなり多くなった
  • 複数人で開発する必要が出てきた

のような状況を経験するようになってくると、問題の解決方法や仕事の管理方法の質はとても重要になってきます。アルゴリズムとかデータ構造のような問題というよりは、実務のクオリティを上げる必要が出てきたときに、一度目を通しておくと良いのではと思います。

翻訳版

原著との違い

本題とあまり関係がないような細かい修正は加えてたものの、内容的には原著とほぼ同じです。原著が英語ということで参考文献がすべて英語のテキストや動画ですので、少しでも使い勝手が良いように日本語訳された資料があるものについてはその情報を記載しました。動画に関しては私が実際に動画をチェックして、日本語字幕の質が特に良く、観ていてストレスが少ないものにコメントを付けました。その他、ちょっと日本人にはわかりにくそうかなという部分には注釈をいれてあります。

訳文

翻訳というのは、私は結構最近まで、原文をできるだけ忠実に訳した方が良いと信じていました。しかしどうも世間的にはそうではなくて単純にわかりやすい方が良いっぽいので、原著の情報量はキープしつつ文章自体は結構書き換えた部分が多いです。もし本書を手に取っていただけた方がいらっしゃったら、何らかのフィードバックをいただけると大変ありがたいです。

あと近況も

半年前くらいに「大学院生になりましたー」と書きましたが、昨年の秋から休学しています。理由はメインの仕事+きれいなPython本で手一杯だったのと、研究が一段落したことです。これからジャーナルに最低2本採録される必要があって、論文作成~採録までの時間がちょっと見当つかなかったということもあって、休学中にある程度仕上げてから復学せねばと考えています。仕事しながらだとホントしんどいです。いや、しんどいことはわかっていたのだけど、思っていた以上に(精神的に)しんどい。社会人でドクター目指す人は入学する前から緻密に計画しといた方がよいですよ、まじで。

大学院生になりました

大学を卒業してから20年以上経ちますが、ようやくちゃんと勉強したいという気持ちが生まれました(遅) というわけで、事後報告になりますが2020年から再び学生になりました。学位論文が無事に完成するまで、もしかしたらものすごく時間がかかるかもしれないので、ひとまずお先に、私を受け入れてくださった関西大学大学院 総合情報研究科の諸先生方に心より感謝申し上げます。それから、お仕事お断りしちゃった方々本当に申し訳ないです。一通り終わったら何でもしますんで🙇‍♂️

博士後期課程

受験資格

私は大学院に進学後すぐに退学してしまったので、本来ならば前期課程から入学すべきなのですが、後期課程の受験資格審査という制度のおかげで受験資格を得ることができました。昔ちょこっと書いたショートペーパーとか、執筆系の仕事の評価が大きかった気がします。開発系の実績もそれなりに評価していただいたとは思いますが。

入学試験

入学試験の前の段階から結構な回数の面談を重ねてある程度準備はできていましたが、なんせ20数年ぶりの試験なのでかなり緊張しました。試験が始まって40~50分位で、周りの若い子たちの手が止まるんですよ。すごいスピードで解き終わってるんですよね。私はというと、いきなり解く問題を間違えて焦り、辞書をパラパラし過ぎて時間が無くなりで、冷や汗ダラダラの中90分時間ギリギリで完成しました。筆記試験が終わった安堵感から面接ではかなり注意散漫な感じだったと思いますが、なんとかクリアできました。

40過ぎて大学院の仕組みを知る

正直な話、生まれて40年くらいの間、大学院のことをよくわからないまま生きてました。研究会?国際会議?論文誌?ナニソレ的な。昔書いた論文も、当時はよくわからずに投稿し、よくわからんけど採録された感じですね。おまけに修士課程をスキップしたこともあって、去年一年間はそういうのを一通り理解するだけで終わったような気がします。早くも休学リーチがかかっている気が…。

何の研究すんの?

最適化とかアルゴリズムの研究にはめっちゃ興味ありますが、実際にやっているのはSNS(ほぼTwitter)に関してで、学位論文もTwitterに関するもので書く予定です。そもそもなんで関大なのかというと、偶然知り合って研究のお手伝いをすることがあった院生の方がご卒業ということで、彼の研究を引き継いでやっていくと内容もそれなりに把握できているし、ある程度方向性が決まっているので良いかなーという理由です。

自分自身、Twitterを使うのを長らく控えていて、それは忙しいということだけでなく使っててなんかしんどいというのが大きな理由です。便利だし面白いんだけど、この疲労感は辛いなと思っています。だから、もうちょっと楽に使えるようになりたいという気持ちもあって、Twitterに関する研究でやっていくと決めました。

今とこれから

とにかくやり切りたい

いろいろと自分を追い込んで疲れてきたので、息抜きにブログ書いたりTwitterで遊んだりしていこうと思います。心身の健康を保ちつつ、なんとか学位論文を完成させたい。将来どう役に立つとか二の次です。学位は足の裏に付いた飯粒みたいなものなんて言われたりしますが、今足の裏がベッタベタです。

辛いこと

大学院に入学すると決めてから、マラソンマッチやるのを断っているんですよ!通知が来るたびにムズムズしてます。やるべきことをすべて終わらせて、思う存分マラソンしたいです。

まあそんなわけで

皆さん今後ともよろしくお願いします。「そんなことより論文書け」とか言わないでね😱

256【解説】

本稿はCodeIQで2016年7月12日~2018年4月25日に出題された 【実力判定:Cランク】256 という問題の解答例と簡単な解説です。

  • 本稿におけるコードは単なる解答の1例に過ぎません。「もっと良い解法があるよ!」とか、「こう書いた方がわかりやすいよ!」とか、「他の言語で書いてみたよ!」という場合は、自由にコメントに書いていただいても、個人のブログ等で公開していただいても結構です。

問題本文はこちら

256【解説】

入力サイズが小さいので、素直に二重ループでよいでしょう。数値のリストから同じ要素を2回選ばないように注意してください。

# python3
# O(n^2)

n = int(input())
v = [int(s) for s in input().split()]

for i in range(n-1):
    for j in range(i+1, n):
        if v[i] + v[j] == 256:
            print("yes")
            exit() # 見つかったらおしまい

# 見つからなければココに到達する
print("no")

「2人組作って!」【解説】

本稿はCodeIQで2014年9月4日~2014年9月16日に出題された プログラミング言語★総選挙 予備選挙「2人組作って!」 という問題の解答例と簡単な解説です。

  • 本稿におけるコードは単なる解答の1例に過ぎません。「もっと良い解法があるよ!」とか、「こう書いた方がわかりやすいよ!」とか、「他の言語で書いてみたよ!」という場合は、自由にコメントに書いていただいても、個人のブログ等で公開していただいても結構です。

問題本文はこちら

「2人組作って!」【解説】

◆解説

欠席者が最大1という制約でペアを作る問題でした。欠席者が0か1であれば、次のようなルールで判別することができます。

f:id:Ozy:20180507150100p:plain

生徒の位置によって、ABのグループに分けると、Aのグループの人数とBのグループの人数が等しくなった場合にペアを作ることができます。

f:id:Ozy:20180507150139p:plain f:id:Ozy:20180507150158p:plain

の場合はAもBも4ずつになり、うまくペアを作ることができますが、

f:id:Ozy:20180507150211p:plain

の場合はAが5でBが3となり、数が等しくなくなりますので、ペアを作ることができません。 この性質を利用すれば、単純に出席者をグループ毎に数えるだけで済みます。

# python3
import sys

# index[x, y]について、x+yが偶数ならa、奇数ならbを増やす
a = b = 0

for i, s in enumerate(sys.stdin):
    for j, c in enumerate(list(s)): # 1文字ずつ処理
        if c == 'O': # 出席者
            if i+j & 1 == 0: # indexの偶奇を調べる
                a += 1
            else:
                b += 1

print("yes" if a == b else "no")

最大の攻撃を繰り出せ!【解説】

  • 本稿はCodeIQで出題された 第1回プロコン:【鬼】最大値をたたき出す攻撃を繰り出して鬼を倒そう という問題の解答例と簡単な解説です。

  • 本稿におけるコードは単なる解答の1例に過ぎません。「もっと良い解法があるよ!」とか、「こう書いた方がわかりやすいよ!」とか、「他の言語で書いてみたよ!」という場合は、自由にコメントに書いていただいても、個人のブログ等で公開していただいても結構です。

問題本文はこちら

最大の攻撃を繰り出せ!【解説】

◆解説

a3 + b3 = c3 + d3…(1)を満たすa, b, c, dについてすべての場合を総当たりで解こうとすると、入力の最大値が1000ですので大雑把に計算量を見積もると、約1000の4重ループで10004近い計算を行うことになります。このままでは制限時間を超えてしまいますので、ループ回数をもう少し抑える工夫をしてみます。 x3 + y3 = k (ただしx < y)とすると、この等式を満たすx, yが2通り以上あれば、(1)を満たすa, b, c, dが存在すると言えます。そこでkをキーとしたハッシュマップを用いて、取り得るkの値をすべて記録していきます。ハッシュマップをhとすると、xとyの組み合わせに重複が無いように列挙すれば、あるx, yについて、h[k]が存在しなければh[k]を登録し、もしすでに存在していれば、それはx, yの組み合わせが少なくとも2種類は存在するという意味になります。この時のkの値の中から最大のものを見つけることで解が得られます。

◆解答例

#!ruby

n = STDIN.gets.to_i
h = Hash.new
m = 0
(1..n-1).each{|x|
  (x+1..n).each{|y|
    k = x*x*x + y*y*y
    if h[k]
      m = k if k > m
    else
      h[k] = true
    end
  }
}
puts m

「MENMA」を探そう【解説】

  • 本稿はCodeIQで出題された 第1回プロコン:【猿】偏食の猿の好き嫌いを見極めよう という問題の解答例と簡単な解説です。

  • 本稿におけるコードは単なる解答の1例に過ぎません。「もっと良い解法があるよ!」とか、「こう書いた方がわかりやすいよ!」とか、「他の言語で書いてみたよ!」という場合は、自由にコメントに書いていただいても、個人のブログ等で公開していただいても結構です。

問題本文はこちら

「MENMA」を探そう【解説】

◆解説

これは検索文字列"MENMA"の文字数を深さの最大とした深さ優先探索を素直に実装するだけの問題です。テストケースは大きくありませんので、特別な工夫を行う必要はありません。落ち着いてコーディングしましょう。

◆解答例

#!ruby

$w = "MENMA"
$v = STDIN.readlines.map{|s|s.strip.split''}

def dfs(d, cx, cy, ox, oy)
  
  return $w[d] == $v[cx][cy] if d+1==$w.size
  
  return false if $w[d] != $v[cx][cy]
  
  [ [-1,0], [1,0], [0,-1], [0,1] ].each{|u|
    nx, ny = [cx,cy].zip(u).map{|t|t.inject(&:+)}
    next if [nx,ny] == [ox,oy]
    next if nx<0 || ny<0 || nx>=$v.size || ny>=$v.size
    
    return true if dfs(d+1, nx, ny, cx, cy)
  }
  
  return false
end

n = $v.size
n.times{|i|
  n.times{|j|
    if dfs(0, i, j, i, j)
      puts"yes"
      exit
    end
  }
}

puts"no"

少ない重りで重さを量ろう【解説】

  • 本稿はCodeIQで出題された 第1回プロコン:【雉】くそ生意気な雉を黙らせよう という問題の解答例と簡単な解説です。

  • 本稿におけるコードは単なる解答の1例に過ぎません。「もっと良い解法があるよ!」とか、「こう書いた方がわかりやすいよ!」とか、「他の言語で書いてみたよ!」という場合は、自由にコメントに書いていただいても、個人のブログ等で公開していただいても結構です。

問題本文はこちら

少ない重りで重さを量ろう【解説】

◆解説

1つひとつの分銅には、「左に置く」「右に置く」「置かない」という3つの選択があります。つまりn個の分銅を用いると、3n通りの置き方があります。ただし、1つの分銅を左に置く場合と右に置く場合で量ることのできる重量は変わりませんから、左右の違いは1つのペアと考えなければなりません。
たとえば、AとBの2つの分銅を使って、左にA・右にBを乗せる場合と、左にB・右にAを乗せる場合とでは、量ることのできる重量は同じです。左にAB・右はナシの場合と左はナシ・右にABの場合も同じ重量です。唯一ペアにならない(ペアが存在しない)のは、おもりを1つも乗せない場合です(左に乗せないや右に乗せないとは言いませんね)。 以上から、n個の分銅を用いて表すことのできる0以上の連続する整数値は、最大で(3n – 1) / 2と表すことができます。

◆解答例

#!ruby
n = STDIN.gets.to_i
m = 1

while (3**m-1)/2 < n
  m += 1
end

puts m

◆補足

本問の解答には必要ありませんが、n個の分銅の質量は、3n-1グラムにします。たとえば、分銅を2つ用意する場合、1グラム(A)と3グラム(B)にすると、

  • 乗せない…0グラム
  • Aだけ乗せる…1グラム
  • 一方にA、もう一方にBを乗せると…3-1=2グラム
  • Bだけを乗せる…3グラム
  • 一方にAB両方を乗せる…3+1=4グラム

のように4グラムまで表すことができ、(32 - 1) / 2の値と一致します。分銅を3つ用意する場合、1グラム(A)、3グラム(B)、9グラム(C)にすると、先程の0~4グラムに加えて、

  • 一方にC、もう一方にAB…5グラム
  • 一方にC、もう一方にB…6グラム
  • 一方にAC、もう一方にB…7グラム
  • 一方にC、もう一方にA…8グラム
  • 一方にだけC…9グラム
  • 一方にだけAC…10グラム
  • 一方にBC、もう一方にA…11グラム
  • 一方にだけBC…12グラム
  • 一方にだけABC…13グラム

のように、13グラムまで表すことができ、(33 - 1) / 2の値と一致します。