PSM

プログラミング 初心者の メモ書き です

Python3で任意長のリストを取ろうと思ったらいつのまにかリスト内包表記を勉強していた

Python3で任意長のリストを宣言しておくのってどうしたらいいんだと思って調べていたら,いつの間にかリスト内包表記を勉強していました.

これを使うと,
1.とりあえずfor文で回した結果を片っ端からappendなどでリストに突っ込む
      ↓↓↓
2.要素1つ1つ判定しながらリストを加工(削除,要素の変更など)する
というような操作が1行で書けます.
正確に言うと,先に判定をして加工も済んだ要素で,リストをつくるという感じのようです.

任意長の配列

[0, 0, 0, 0, 0, 0, 0]

例えば 上のような長さ7のリストが欲しい時は,

[0]*7

と書けばよいですが,リスト内包表記という”[]の中にfor文を書く”やり方でも作ることができます.

 [0 for i in range(7)]

リスト内包表記

基本形

 [i for i in range(7)]

出力は

[0, 1, 2, 3, 4, 5, 6]

一番左のiがリストの要素となっています.
この場合では,右側のfor文でiが0から1つずつ増えていくと定義されていることになります.
よって例えば以下のように一番左のiを(i+1)に変えると,

 [(i+1) for i in range(7)]

出力は

[1, 2, 3, 4, 5, 6, 7]

となります.

if(後置)

要素をifで判定してリストに加えるかどうか決めることもできます.
この場合,for文のあとにif文を書きます.

 [i for i in range(7) if i<5]

5より小さいiのみが要素として追加され,

[0, 1, 2, 3, 4]

となります.

if else

if elseを使いたい時は最初にもってくる必要があります.

 [i if i<5 else 4 for i in range(7)]

iが5より小さい時はそのままiが要素となり,iが5以上のときは4が要素となります.

[0, 1, 2, 3, 4, 4, 4]

集計

リストなどで集計関数がそのまま使えます.例えば,

sum( [(i+1) for i in range(7)])

について,全要素の和となるので

28

が得られます.28は完全数ですね.

2重for

forを2回重ねることもできます.

[[i,j] for i in range(2) for j in range(5)]

この場合はiが2通り,jが5通り動くので,長さ10のリストができます.

[[0, 0],
 [0, 1],
 [0, 2],
 [0, 3],
 [0, 4],
 [1, 0],
 [1, 1],
 [1, 2],
 [1, 3],
 [1, 4]]

Python3で二分探索

wikipediaに載っているc++スクリプトpythonで書き直しただけですが,”忘れた時にとりあえずコピペしたら動く”がモットーなので自分用にメモ.

[wiki:二分探索]

実装は以下.

#二分探索
def binarySearch(ary, key, imin, imax):
    if ( imax < imin ):
        return "KEY_NOT_FOUND"
    else:
        imid = imin + (imax - imin)//2
        if (ary[imid] > key):
            return binarySearch(ary, key, imin, imid - 1)
        elif (ary[imid] < key):
            return binarySearch(ary, key, imid + 1, imax)
        else:
            return imid

試してみたらちゃんと動きました.

a = [1,2,2,4,5,6,7,10,20,40]
binarySearch(a, 40, 0, len(a)-1)

再帰ってプログラミング初心者が苦しむ難関の1つですよね……

Pythonで2進法方のリストを作る

Pythonで二進法の各桁を各要素とするようなリストをつくって,降順に出力することを考えました.
その際,文字列の各要素のリスト化や文字列のゼロパディングなど,いくつか覚えておきたい操作があったのでメモしておきます.

操作

数値型のリストのソート

a = [0,3,4,2,1]

# 昇順
sorted(a)
#降順
sorted(a, reverse = True)

文字列の各文字を要素に持つリストをつくる

s  = "abcde"

#リスト化
list(a)

これを応用して,何桁かの数字を各桁を要素に持つリストにバラしたい場合は

a = 12345

# リスト化
map(int, list(str(a)))

二進数に変換

a = 31
format(a, 'b')

出力はstr型になるのに注意が必要です.

文字列の0埋め

"20"
"123"
"02"

#4桁にそろえる
"0020"
"0123"
"0002"

にしたいという話です.

a = "20"
a.zfill(4)

サンプルスクリプト

l  = 4
f = 2**l-1
for i in range(2**l):
    ff = list(map(int,list(format(f,'b').zfill(l))))
    print(ff)
    f-=1
[1, 1, 1, 1]
[1, 1, 1, 0]
[1, 1, 0, 1]
[1, 1, 0, 0]
[1, 0, 1, 1]
[1, 0, 1, 0]
[1, 0, 0, 1]
[1, 0, 0, 0]
[0, 1, 1, 1]
[0, 1, 1, 0]
[0, 1, 0, 1]
[0, 1, 0, 0]
[0, 0, 1, 1]
[0, 0, 1, 0]
[0, 0, 0, 1]
[0, 0, 0, 0]

こんな感じにでてきます.
なんか無駄が多い気がしますが……


あとがき

競技プログラミング初心者(2日)です.
あくまで練習のために始めたので,競プロに取り組む上ではどうも邪道らしいんですが,Pythonでやっていきたいと思います.

今回は上で作ったリストをフラグとして使ったのですが,結局,TLEが出てしまって方向転換を余儀なくされました.

python3での標準入出力

pythonでの標準入出力を求められる機会って稀によくありますよね.
pythonって2だったり3だったり紛らわしいし,調べてみてもコピペで動いてくれるスクリプトが載っている記事を見つけられなかったので,自分の検索能力のなさを嘆きつつ苦しんだ結果をメモ書き程度に残しておきます.
(環境はpython3.6.0を想定しています)

入力

1行に1つの入力を受け取る

28

とか

test

とかを受け取りたい時です.
たぶんどの言語でもそうなんでしょうけど,pythonでも入力関数を一回呼び出すごとに1行分の入力を読み込んでくれるようです.
というわけで,

a = input()

とすればaに入力を代入できます.

ただしこれだとintだろうとfloatだろうとstrだろうと何でもstr型で読み込んでしまうので,型変換までついでにやっちゃうなら

a = int(input())

のように書けます.

1行に複数の入力を受け取る

a b c d e

1 2 3 4 5

とかを受け取りたい時です.

文字列で受け取るなら

a = input().split()

とすると,半角スペースのところで区切ってリストにして受け取ってくれます.

型を指定して読み込むときには,変数が1つのときの場合のようには書けず,map関数を使う必要があります.
intで受け取りたいなら

a = list(map(int, input().split()))

となります.
注意点としては,python3ではmap()を使って読み込むとmap objectというものになってしまうので,list()で変換する必要があります.

また,受け取りたい入力が数個程度で,その数がわかっているならこんな風にもできました
たとえば

3 4

に対して

a,b = list(map(int, input().split()))

とすればaに3が,bに4がint型で代入されます.


複数行に複数の入力を受け取る

3
1 2 3
4 5 6
7 8 9

みたいなのを受け取りたい時です.

input()を一回呼び出すごとに1行分の入力を受け取ってくれるので,for文などで行数分呼び出すか,whileなどを使えば良いようです.
というわけで,

n = int(input())
a = []
for i in range(n):
    a.append(list(map(int, input().split())))

最初に空のリストを用意して,appendでどんどん突っ込んでいくという方針です.
numpyを使うならここでついでにnp.arrayにしておいても良いかもしれません.


出力

python3では標準出力printが関数となっていました(python2系との違い).

print("output")

のように書きます.
勘違いしていたのですが,ここではint型も勝手に型変換して出力してくれるようなので,例えば

print(1234)

とint型を直接書いても,勝手に変換して"1234"と出力してくれます.
複数の引数をとることもできて,

n = 2
print("春が",n, "階から降ってきた")

とすれば

春が 2 階から降ってきた

プログラミング初心者がUnityチュートリアルで苦しんだポイント

1 はじめに

プログラミング初心者がUnityのチュートリアルに挑戦してつまづいた部分をメモしていきます.
(逐次更新する予定です)

「初心者ゆうてもピンキリやがな」という話ですが,私がどのくらい初心者かというと,for文やif文くらいは自信を持ってわかると言えるレベルです.
クラス?宣言?オブジェクト指向
なんとなく説明できないこともないこともないこともないよ!ってなものです.

さて.
現在のUnityのバージョン(5.6.1)に対してチュートリアルで使われているバージョンが若干古く,UIなどの仕様変更が多々あっていちいちつまづきました.
プログラミングを勉強し始めてから,
あっこれ死にゲーってやつだ
と割と早く悟ることができたのですが,盲点なのは自分の記憶力が恐ろしく悪かったことです.
いつまでも序盤の罠でしに続けているわけにはいかないので,主に自分の為にそれらをメモしていこうと思います.

実際に作ったものや感想についてはまた別の記事で.


2 つまづいた部分

2.1 Assetについて

Asset Tabを開き,導入したいassetを検索してインポートします.
今回はspace shooterで検索するとチュートリアル用の一式を手に入れることができます.
Asset storeのタブは,ショートカット⌘9か,Window > Asset Storeで開くことができます.


2.2 Unity Web PlayerとWebGL

チュートリアルの動画ではWeb Playerが紹介されていますが,現在のUnityでは非推奨としており,どうもそのうち使えなくなるようです.
そこで代わりにWebGLを使用することにしました.
作業環境や対象とする環境は,⌘+Shift+Bでビルトの設定画面を開き,そこで設定することができます.


2.3 Gameウィンドウの設定

ビルトの設定画面で左下のplayer settingsをクリックすると,Inspectorタブで設定ができるようになります.
ここではResolution and Presantationのところでサイズを変更し,そのあとGameタブの左上のFree Aspectとなっているところをクリックして+を選択し,新しく600×900に設定し直しました.


2.4 スクリプトエディターについて

Unityをインストールすると勝手についてくるMonoDevelopを使えばとりあえずスクリプトは書けます.
サジェスト機能など,使い心地などがちょっとアレな場合はVisual Studioなどを使うこともできます.
www.visualstudio.com


2.5 RIgidbodyの取得

Unity公式のチュートリアル動画では

rigidbody.velocity = movement

のように,rigidbodyのvelocityメソッドに直接movementの情報を与えられると書いていますが,これはUnity4のための書き方であって,現在のUnity5ではエラーとなってしまいます.
Unity5では,まずvoid FixedUpdate()の前にvoid Start()を宣言し,その中でGetComponent()を使ってrigidbodyを取得してやる必要があるようです.

つまりスクリプト

void Start()
{
	rb = GetComponent<Rigidbody> ();
}

void FixedUpdate()
{
	float moveHorizontal = Input.GetAxis ("Horizontal");
	float moveVertical = Input.GetAxis ("Vertical");

	Vector3 movement = new Vector3 (moveHorizontal, 0.0f, moveVertical);
	rb.velocity = movement;
}

と書き換えれば大丈夫です.


2.6 Mathf

チュートリアルの中で,公式にリファレンスがあるので逐一チェックすると良いよと言われました.
試しにMathfで検索してみたら公式の(しかも日本語の!)リファレンスがヒットしました.
docs.unity3d.com
これによるとMathfはUnityEngineの中に入っている,標準的な数学関数を扱うためのモジュールのようです.


2.7 public float speedなど

public class PlayerController : MonoBehaviour
{
	public float speed;
	public float xMin, xMax, zMin, zMax;
}

のようにクラスの先頭で[public+型+変数名]で宣言した変数は,UnityのInspectorで後から与えることができます.
こんな感じに新しい入力窓が勝手にできます.
f:id:muromura:20170612194751p:plain


また詳しいことはわかりませんが,class宣言時に[System.Serializable]を使うと,以下のように折り畳んだ形で変数を与えられるようです.
f:id:muromura:20170612195538p:plain





3 まとめ

そもそもCとC++C#の違いすらわかっていない人間なので,コードの意味もなんとなくしか追えず,ひたすら辛い時間が続いています.
しかしその辛さを乗り越えて余りあるのが,ゲームを作る楽しさです.

とりあえずチュートリアルの範疇では面倒なキャラクターの3Dモデリングやテクスチャー作成をする必要がないのも,素晴らしい点です.
システムとしてはそんな複雑なことをやっていなくても,とりあえずモデリングとテクスチャーがしっかりしていたら見れるものになりますもんね.

それでは.




4 この記事を書くにあたって参考にさせていただいた記事

tech-camp.in
idol-ch.net
docs.unity3d.com
brian.hatenablog.jp
curious-boys.hatenablog.com

大数の法則

1 はじめに

中心極限定理について直感ではいまいち納得できなかったのでちょっとまとめようとおもったのですが,その前にまず大数の法則についてまとめてみます.

wikipedia先生に中心極限定理について聞いてみます.

大数の法則によると、ある母集団から無作為抽出された標本平均はサンプルのサイズを大きくすると真の平均に近づく。これに対し中心極限定理は標本平均と真の平均との誤差を論ずるものである。多くの場合、母集団の分布がどんな分布であっても、その誤差はサンプルのサイズを大きくしたとき近似的に正規分布に従う。

wikipedia:中心極限定理
さて,中心極限定理について聞いたのに大数の法則というのが出てきました.

2 大数の法則とは?

大数の法則が言っているのはあたりまえのことでした.
世の中のたいていの事象は無限に存在するので,その全てを調べ尽くすことは不可能に等しい行為です.
例えば卵にはたまに黄味が2つ入っているのがあります.
しかし黄味が2つ入っている確率を求めたいとして,そのためにこの世の全ての卵を調べようなんてのは無理な相談です.(こうしている間にも鶏は卵を産み続けます)
そこでほとんどの人が思いつく方法が,だいたい1,000個とか頑張って10,000個とかの卵を調べて,その中の黄味が2つある卵を数え,それでとりあえず確率を出して満足しておこうというものでしょう.
これがまさに大数の法則の考え方となります.
自分が調べることのできる量(=1000個)なんて全体の量(=世界中の卵の数)からみたら本当に微々たるものにすぎませんが,それでも結構信頼の高い確率を得ることができるということです.
これを指して,大数の法則は「経験的確率と理論的確率が一致する」ことを示すものだと言ったりもします.
また,ここで信頼性という面で大事なのは自分が”何個”調べたかであって,”何%”調べたかではないというところに注意が必要です.

大数の法則が現実に使われている例としては,選挙速報やTVの視聴率調査があります.
これらはほとんど現実の値から外れることはありませんが,現実にすべての投票者.すべてのTVのある家庭に対して調査を行っているわけではありません.
一部の家庭や有権者を調べるだけで,全体の動向もだいたいわかってしまうものなんですねえ.

Pythonで麻雀の戦績を集計してみた

1 初めに

前回の投稿に引き続き,記録をとっていた最近の戦績を集計してみました.
基本的にはpythonのpandasしか使ってないのですが,集計してみたり,for文回してみたり,DataFrame 結合してみたり,ブロードキャストについて思い出したりと結構よい勉強になりました.
それではいよいよ実際に行った作業に移ります.


2 やったこと

こんな感じのデータを読み込みます.
f:id:muromura:20170510223606p:plain
列が各対局者,行が半荘ごとの成績(得点)となっています.
対局に参加していない人の欄は空白になっており,pythonではNaNとして読み込まれます.

3行目から6行目はエクセルで順位つけようとして途中で面倒になってやめた名残です.
不要行削除の例示に都合良いのでそのままにしておきます.



1行を削除するときには,配列のスライスと同じように指定すれば良いです.

#不要部分削除
hoge[:5]

列を削除するときにはdrop()を使います.

#不要部分削除
data = data.drop("削除したい列名", axis=1)

対局数をカウントするのには,NaNの数を利用することにしました.
各列ごとにis.null()でNaNの数を調べるとTrue/Falseで結果が帰ってきます.
pythonにおいてはTrue/Falseは数値計算すると1/0に等しくなるので,結果を1から引いてやることで各列で対局に参加した部分(=得点が入力されている部分)を1,それ以外を0とすることができました.
あとはそれを合計するだけです.

#対局数
(1-data.isnull()).sum()

順位の集計にはちょっと手間取りました.
rank()を使えば良いのですが,このとき行ごとに順位を出すには,引数でaxis=1を指定します.
ちなみにこのときNaNはNaNのまま無視してくれました.便利!

rank()を使うと数が少ない順に1から数字が振られるので,本来の順位とは逆になってしまいます.
それをひっくり返すためには5から順位を引けば良いことになります.
これはpythonのブロードキャストを使って簡単に書けました.

#順位づけ
rank = data.rank(axis=1)
rank= 5 - rank

次に,1位から4位までそれぞれの順位を何回とったか数えます.
たぶん他にもっと良い方法があると思いますが,思いつかなかったので,各列ごとに抜き出してvalue_counts()で集計し,後でそれを全部くっつけるという方法をとることにしました,
最初に空のDataFrameを用意し,colmuns()でとってきた列名でfor文を回して集計結果をくっつけています.
DataFrameの結合にはconcat()を使い,列方向にくっつけたいときにはaxis=1を指定します.

#各順位を何回とったかの集計
#列名を取得
players = rank.columns
#空のデータフレームを用意
ranks = pd.DataFrame(index=[1,2,3,4], columns=[])
#for文で回してranksに突っ込んでいく.
for player in players:
    r = rank[player].value_counts()
    ranks = pd.concat([ranks, r], axis=1)

3 サンプルコード

というわけで長くなってしまいましたが,サンプルコードは以下.

import numpy as np
import pandas as pd

#データ読み込み
raw_data = pd.read_csv("sample.csv", encoding="sjis")

#不要な部分を切り捨てる
data = pd.DataFrame(raw_data[5:])
data = data.drop("day", axis=1)
#型をチェック
#data.dtypes

#順位づけ
rank = data.rank(axis=1)
rank= 5 - rank

#対局数
(1-data.isnull()).sum()

#平均順位算
rank.mean()

#合計計算
data.sum()

#平均得点計算
data.mean()

#順位合計
players = rank.columns
players
ranks = pd.DataFrame(index=[1,2,3,4], columns=[])
for player in players:
    r = rank[player].value_counts()
    ranks = pd.concat([ranks, r], axis=1)

#集計
result = pd.concat([(1-data.isnull()).sum(),
                    ranks.T,
                    rank.mean(),
                    data.sum(), 
                    data.mean()],
                   axis=1, join='inner').T

result.index=['対局数',"1位","2位","3位","4位",'平均順位','合計得点','平均得点']
result = result.round(2)

結果はこんな感じになります.
f:id:muromura:20170510224213p:plain


また時間のあるときに,次は各日ごとの集計をしたり,player同士の相性を計算したりなどやっていきたいと思います.
適当ですが,得点の相関係数とかでも相性の指標になりうるかもしれませんね.