radiko録音スクリプトのdocker化

radikoを録音するシェルスクリプトmatchy2/rec_radiko.shをdockerで動かすようにした。他の方のスクリプトを使うだけだが…。

できたもの

docker_rec_radiko

あとはCRONでdocker runすればいい(docker runはradiko_docker_run.shの中でやっている)。
録音時にdocker runするか、それともコンテナは起動しっぱなしにしてその中でCRONで録音するか迷った。(つまりCRONでコンテナを起動するか、コンテナの中でCRONするか。)だが録音しないときはこのコンテナは何もしないので、前者でいいということにした。

作った理由

環境構築が面倒だから。

関東を離れてもTBSラジオなどが聞きたいので、ConoHaで東京リージョンのVPSを借り、そこでこのシェルスクリプトを動かすようにした。スクリプトではffmpegなど必要となるツールがあるのでそれらをインストールしないといけない。VPSではいろいろなOSの中からCentOSを(サーバならCentOSだろという固定観念から)選んだが、CentOSだとこのインストール作業がやや面倒なのでdockerにした。

radikoのプレミアム会員になれば全国どこの局でも聞けるというのは知ってる。借りたサーバでは他にも何かするつもり。

もろもろ

  • 時刻設定が面倒だった
    はじめubuntu:16.04のdockerイメージを使っていたが環境変数 TZAsia/Tokyo にしてもJSTにならないようだった。他にも設定の方法はあるのだろうが、ubuntuはやめてdebianのイメージを使うことにした。debianだとこれでいける。
  • dockerでのマウント方法
    新しい指定方法ができていた(docker 17.06からなので新しくもない…知らなかっただけ)。
    ホストのディレクトリ[ファイル]をコンテナのディレクトリ[ファイル]にマウントさせたいとき、以下が同じ意味になる。

    # -v で指定する方法
    $ docker run -v <PATH_IN_HOST>:<PATH_IN_CONTAINER> <IMAGE_NAME>
    # --mount で指定する方法
    $ docker run --mount type=bind,source=<PATH_IN_HOST>,target=<PATH_IN_CONTAINER> <IMAGE_NAME>
    

    -v では、コロンの前と後どっちがホストのパスだっけと迷うことがよくあった。 --mount だといちいちsourceなどと書くのでわかりやすい。
    またvolumeのマウントでも、 -v だとホストディレクトリ[ファイル]のマウントと区別しにくかったが、 --mount ではtypeが変わるのでわかりやすい。

    # -v で指定する方法
    $ docker run -v <VOLUME_NAME>:<PATH_IN_CONTAINER> <IMAGE_NAME>
    # --mount で指定する方法
    $ docker run --mount type=volume,source=<VOLUME_NAME>,target=<PATH_IN_CONTAINER> <IMAGE_NAME>
    

参考

モンティ・ホール問題で勘違いしていた話

論理パラドクスという本を読んでいたら、モンティ・ホール問題について勘違いしていたことに気付かされたのでメモ。
結論としては、普通のモンティ・ホール問題についてはこの勘違いでも(たまたま)正しいが、一般的な状況でも成り立つと思っているとまずいという話。正直、長い割にはしょうもない結論になった感が拭えない。

以前作った「意識高い」タグがとてつもなくバカっぽいが、数学関係なのでつけとく。「数理系」タグにしとけばよかった…。

勘違いの内容

モンティ・ホール問題で選択を変えることは「最初に選んだドア以外の2つのドアを選ぶ」ことと同じ意味だ。

確率を計算すると、最初の状態では当たりを選ぶ確率は1/3。
司会者が外れのドアを開けたあとは、ドアを変えた方が当たる確率は2/3になる。
たしかに最初に選んだドア以外の、2枚のドアを選んだときに当たる確率と同じになっている。

変形モンティ・ホール問題

なんとなく物語形式にしてみる。

モンティ・ホール問題で出てくるゲームをやることになった挑戦者。前日に猛勉強してコツ(a.k.a. 勘違い)をつかんだ。

「結局これは3つのドアのうち、1つを選ぶか、他の2つを選ぶかということだ。
司会者は必ず、挑戦者が選んだ1つのドア以外の2つのドアから、外れのドアを開ける。
ということは、選択を変えることは、他の2つのドアを選ぶのと同じだ。
1つより2つの方が当たる確率が高いのは当然だ。実際、確率は変えるときの方が変えないときの倍になっている。
簡単な話だ。選択を変えた方が勝ちだ!」

翌日、ゲームの説明を受ける挑戦者。
基本的にはモンティ・ホール問題なのだが、1つだけ違いがあった。

3つのドアA、B、Cのどれに賞品を入れるか、くじで決めるのだが、この確率が1/3ずつではなかった。
Aには\cfrac{9900}{10000}、Bには\cfrac{  99}{10000}、Cには\cfrac{   1}{10000}の確率で賞品を入れる。以降は同じで、司会者はどのドアに賞品が入ったか知っている。

こんなの絶対Aに決まってると思った挑戦者はあまりの興奮のため、誤ってBを選んでしまった。
だが選択を変えるチャンスが1回あるのはルールで決まっているのだ。そのときAに変えればいい…
しかし司会者が開けたのはAだった! Aは外れだったのだ。

挑戦者は思った。

「Aが外れだったのは驚きだな…奇跡的な確率じゃないか?
でもこれでBが当たりというのは確実だろう。BにはCの100倍近い確率で賞品が入るんだから。
…待てよ? 選択を変えるということは、残り2つのドアを選ぶということだったはずだ。
司会者がAを開けるまで、B以外が当たる確率は\cfrac{9900}{10000} + \cfrac{1}{10000}だった。これはBが当たる確率より100倍以上大きい。そしてそれがそっくり、Cが当たる確率になるはず…なのか?
だとすればBを選び続けるのはあまりに無謀すぎる。しかしCが当たりとはとても…」

当たる確率が高いのは選択を変える方なのだろうか、変えない方なのだろうか?

変形モンティ・ホール問題の定義

念のため、ちゃんと書いておく。この状況を変形モンティ・ホール問題と呼ぶことにする。

3つのドアA、B、Cのどれか1つに賞品が入っている(当たり)。残り2つのドアは外れ。
当たりのドアがどれか、挑戦者が当てるゲームをする。
このとき以下の条件がある。
1. 賞品をどのドアに入れるかはくじで決める。A、B、Cに賞品が入る確率をそれぞれP_A, P_B, P_C(0 \leq P_A, P_B, P_C \leq 1, P_A + P_B + P_C = 1)とする
2. 司会者はどのドアに賞品が入っているか知っているが、挑戦者は知らない
3. 挑戦者がドアを1つ選んだ後、司会者は残りの2つのドアのうち、外れのドアを(複数あれば無作為に選んで)必ず開け、選択肢から外す
4. 司会者が外れのドアを開けた後、挑戦者は必ず選択を変えるチャンスを与えられる

以上の事項を司会者、挑戦者は共通で認識している。両者の知識に違いがあるのは、どのドアが当たりか知っているかどうかだけ。

このとき、選択を変えて当たる確率はいくらか?
そしてそれが「最初に選んだドア以外の2つのドアを選ぶ」ときに当たる確率と等しいのはどんなときか?

解く

挑戦者は常に最初にBを選び、司会者は常にAを開けるとする。

確率を計算する

ドアAに賞品が入っている事象をA、ドアBに賞品が入っている事象をB、ドアCに賞品が入っている事象をCとする。司会者がドアAを開ける事象をXとする。
選択を変えて当たる確率はベイズの定理から

  \begin{aligned}  P(C|X) &= \cfrac{P(X|C)P(C)}{P(X)} \\         &= \cfrac{P(X|C)P(C)}{P(X|A)P(A) + P(X|B)P(B) + P(X|C)P(C)} \\         &= \cfrac{1 \cdot P_C}{0 \cdot P_A + \frac{1}{2} \cdot P_B + 1 \cdot P_C} \\         &= \cfrac{2P_C}{P_B + 2P_C}  \end{aligned}

なお、特にP_B = P_Cのときは常に2/3になる。

シミュレーション

Python3.6.3で動作確認。

# Copyright (c) 2017 sankaku
# Released under the MIT license
# http://opensource.org/licenses/mit-license.php

# Simulator of the Monty Hall problem.
# The names of the three doors are 'A', 'B' and 'C'.
# The challenger always chooses 'B'.
# The host always opens 'A'.

from random import random
from functools import reduce

# Probabilities of the prize being behind the door
P_A = 1/3
P_B = 1/3
P_C = 1 - P_A - P_B
# P_A = 9900/10000
# P_B =   99/10000
# P_C =    1/10000

# number of trials
TRIAL = 100000

# Return the door the prize is hidden behind
def get_answer():
    rand = random()
    if rand < P_A:
        return 'A'
    elif rand < (P_A + P_B):
        return 'B'
    else:
        return 'C'

def get_host_opened(answer):
    if answer == 'A':
        return 'C'
    elif answer == 'B':
        return 'A' if random() < 0.5 else 'C'
    else:
        return 'A'

def simulate_no_change():
    answer = 'A'
    while True:
        answer = get_answer()
        # Challenger chooses 'B', then ...
        if get_host_opened(answer) == 'A':
            break
    # Challenger does not change the choice
    return 'B' == answer

def simulate_change():
    answer = 'A'
    while True:
        answer = get_answer()
        # Challenger chooses 'B', then ...
        if get_host_opened(answer) == 'A':
            break
    # Challenger changes the choice to the other door
    return 'B' != answer


print('If the challenger does NOT change...')
num_hit_no_change = reduce(lambda x, y: x + y, [simulate_no_change() for _ in range(0, TRIAL)], 0)
print('Hit: {0}/{1} = {2}%\n'.format(num_hit_no_change, TRIAL, 100 * num_hit_no_change/TRIAL))

print('If the challenger DOES change...')
num_hit_change = reduce(lambda x, y: x + y, [simulate_change() for _ in range(0, TRIAL)], 0)
print('Hit: {0}/{1} = {2}%'.format(num_hit_change, TRIAL, 100 * num_hit_change/TRIAL))

検証

あまり自信がないので、普通のモンティ・ホール問題のときに、確率とシミュレーションが一致していることを確かめる。

このときP_A = P_B = P_C = \cfrac{1}{3}で、
変えて当たる確率はP(C|X) = \cfrac{2P_C}{P_B + 2P_C} = \cfrac{2}{3}となる。

シミュレーションでは以下。

If the challenger does NOT change...
Hit: 33263/100000 = 33.263%

If the challenger DOES change...
Hit: 66367/100000 = 66.367%

変えて当たる確率は66.367%という結果。だいたい合ってる。

本題

冒頭の物語での答え

P_A = \cfrac{9900}{10000}, P_B = \cfrac{99}{10000}, P_C = \cfrac{1}{10000}の場合を考える。
変えて当たる確率はP(C|X) = \cfrac{2P_C}{P_B + 2P_C} = \cfrac{2}{101}になる。
シミュレーションでも同様の結果。

If the challenger does NOT change...
Hit: 98048/100000 = 98.048%

If the challenger DOES change...
Hit: 2054/100000 = 2.054%

よって、変えない方が当たる。

…実はこれは計算しなくてもすぐわかることだった。
いまはP_Cが非常に小さいときを考えているが、極端な場合として0のときにはCは確実に外れだから、選択を変えない方が当たる。

「最初に選んだドア以外の2つのドアを選ぶ」のと等価な条件

これはP(C|X) = P(A) + P(C)ということだ。左辺は既に求めたので代入するとP_A = P_CまたはP_B = 0となる。これが答え。
2番目の場合は当然でつまらないので無視。
1番目の場合はAとCに賞品が入る確率が等しいということ。つまりAもCも同等。だから司会者がそのどちらを開けても、何も情報が増減しない。
ということは最初に選んだドアが当たる確率は前と同じ。残った1枚のドアが当たる確率は、選んでいない2枚のドアが当たる確率の合計値になるはずだ。
そう考えると納得はいく。

普通のモンティ・ホール問題のときはP_A = P_Cが成り立っていたので、たまたま冒頭のような勘違いをしても正解だった。
変形モンティ・ホール問題ではこれが成り立つとは限らないので、冒頭の物語の挑戦者のように(自分のように)考えるとおかしな結果になってしまうという話だった。

補足

よくモンティ・ホール問題のわかりやすい解説で、ドアを大量に増やすというものがある。
たとえばドアを10000枚にして、挑戦者は1番目のドアを選ぶ。司会者は3172番目以外のすべてのドアを開けて、
1番目のドアのまま変えないか、3172番目のドアに変えるかというチャンスを与える。
こう考えると、選択を変える方が当たる確率が高いのは明らかだという説明だ。最初に挑戦者が選んだドアがそのまま当たる確率は非常に低く、残ったドアに当たりがあると考えてまず間違いない。

今回の変形モンティ・ホール問題はこれに似ているように見える。
9998枚のドアをグループA、1枚のドアをグループB、残りの1枚のドアをグループCと名付けて、
どのグループに賞品が入っているかという問題にすればP_A = \cfrac{9998}{10000}, P_B = P_C = \cfrac{1}{10000}での変形モンティ・ホール問題になる。
このとき、選択を変えた方が当たる確率は2/3にしかならない…?

なぜこうなるのかというと、ドアを増やす解説で考えている状況はここでの変形モンティ・ホール問題とはまったく別物だからだ。
ドアを増やす場合、グループはあらかじめ決まっておらず、司会者がドアを開けて初めて定義できる。変形モンティ・ホール問題をグループ版にした場合は、あらかじめどのドアがどのグループかが決まっている。だからそのまま当てはめることはできない。

Ubuntu/ArchLinuxで画面キャプチャ(jpg/png/mp4/gif)

久々に画面キャプチャしようとしたらやり方がわからなかったのでメモ。
たくさんツールはあると思うので、一例でしかない。

環境

以下のOSでの方法。

  • Ubuntu 16.04
  • ArchLinux

なおUbuntu(というかDebian)では現在、パッケージ管理のコマンドとして apt-get ではなく apt を使う方がいいらしい。
でも apt-get でやってしまったのでそのまま書く。

静止画(jpg/png)

GIMPを使う。
これでjpg形式やpng形式にできる。
スクショを撮るためにGIMPを入れるのはけっこう間違ってるが、たまたま入ってたのでこれを使う。
なおUbuntuでは普通にPrintScreenを押せば撮れる。

インストール

  • Ubuntu
    $ sudo apt-get install gimp
    
  • ArchLinux
    $ sudo pacman -S gimp
    

使い方

  1. GIMP起動
  2. 「ファイル」 -> 「画像の生成」 -> 「スクリーンショット」
    キャプチャ範囲などが選択できる。これでキャプチャするとすぐGIMPに取り込まれて編集可能になる。
  3. 「ファイル」 -> 「エクスポート」
    普通に「保存」だとxcf形式とかになってしまう。jpgなどにするにはエクスポートを選択。

動画(mp4/gif)

SimpleScreenRecorderを使う。
これでmp4形式などにできる。

SimpleScreenRecorderではなぜかgif形式での録画に失敗したので、いったんmp4で録画し、FFmpegで変換してgifにした。

インストール

公式のダウンロード方法に書いてある。

  • Ubuntu
    SimpleScreenRecorderは17.04以降では公式リポジトリに入っているようだが、それ以前では以下のようにPPA追加してからインストール。

    $ sudo add-apt-repository ppa:maarten-baert/simplescreenrecorder
    $ sudo apt-get update
    $ sudo apt-get install simplescreenrecorder
    # FFmpegを入れる場合は以下もやる
    $ sudo apt-get install ffmpeg
    
  • ArchLinux
    $ sudo pacman -S simplescreenrecorder
    # FFmpegを入れる場合は以下もやる
    $ sudo pacman -S ffmpeg
    

使い方

SimpleScreenRecorder

SimpleScreenRecorderはツールチップがわりと親切なので、それを参考に設定する。

  1. SimpleScreenRecorder起動
  2. キャプチャ範囲などの設定をする
    「任意の矩形を録画」を選択すれば、自由に矩形範囲を指定してキャプチャするか、ウィンドウをキャプチャするか選べる。
    ウィンドウをキャプチャするときは、ウィンドウ選択時にウィンドウの枠をクリックすると枠を含めたウィンドウ全体がキャプチャされる。内部をクリックすると枠はキャプチャされない。(ということがツールチップに書いてある。)
    解像度が高いときなどは動画サイズが大きくなってしまうので「映像をリサイズ」で小さくした方がいい。
  3. 保存場所・コンテナ・コーデックなどの設定をする
    コンテナはMP4などを選ぶ。
    「その他」を選ぶとgifやflvなどたくさん選択肢があるが、gifではなぜかうまく録画ができなかった。
    他はデフォルトにした。(コーデックはH.264、プリセットはsuperfast。)
  4. 録画
    「録画を開始」を押すかホットキー(デフォルトはCtrl + r)で開始。終了は「録画を中断」か同じホットキー。
    「録画を保存」するのを忘れない。

FFmpeg

SimpleScreenRecorderでgif形式にできなかったので、FFmpegでmp4をgifに変換する。

$ ffmpeg -i input.mp4 output.gif

サイズを小さくしたいときはフレームレートを変えるのがいいよう。
これはrオプションで数値指定。小さいほどフレームレートも小さい。
ffmpeg -i input.mp4 -r 5 output.gif のように使う。これでサイズを小さくできるが、当然カクつく。

参考