やるだけPython競プロ日誌

競プロの解説をPythonでやっていきます。できるだけ初心者に分かりやすいように『やるだけ』とかは言わないようにします。コメントについては必ず読んでいます。どんなに細かいことでもいいのでコメントくださればうれしいです。

Python における list の本質と 二次元配列 ( 多次元配列 ) のお話。

こんなページを見てくださっているような方々は分かり切っていることかもしれませんが、わたくしなりに考えてみたことです。

ネットで、私の疑問に直接回答しているサイトは見当たりませんでしたので、ここに記します。

茶番開始です()

配列の宣言

Pythonでリストを宣言するときは、

list = [1, 1, 1, 1, 1]  # あるいは
list = [1] * 5

のようにするのが一般的かと思います。
そして中身を変更する際は

list[0] = 9
print(list)  # [9, 1, 1, 1, 1]

とするものだと思います。

基本中の基本ですよね。


二次元配列

知らない方のために一応説明しておきますが、二次元配列とは、"要素として配列を使用した配列"です。

つまり、

[[1, 1, 1, 1, 1], [2, 2, 2, 2, 2], [3, 3, 3, 3, 3]]

こういったものです。今のリストは、

list = [[i] * 5 for i in range(1, 4)]

で作成できます。

range(1, 4) つまり i = 1, 2, 3の時の [i] * 5 を要素とするリスト。という意味になります。



ふむふむ。分かりやすい。


では、すべて0で初期化した二次元目の要素数5、一次元目の要素数3のリストを作ってみましょう。

list = [[0] * 5] * 3
print(list)  # [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]

できました。では、要素の変換をしてみましょう。

それぞれの要素ごとに対応したインデックスを付けたします。

list[一次元目のインデックス][二次元目のインデックス]

といった感じです。

list[1][2] では、一次元目が2番目の、二次元目が3番目の要素を指定できます。

list = [[0] * 5] * 3
list[1][2] = 1
print(list)  # [[0, 0, 1, 0, 0], [0, 0, 1, 0, 0], [0, 0, 1, 0, 0]]

あれ!? 丘people!? なんでこうなるの!?





茶番でした()


はい。よく聞くお話ですよね。


上の宣言では、[0] * 5 というリストを3つ並べているだけなので、1つの要素を変えると、ほかの要素まで変わってしまう。という話です。


もう少し細かくいうと、


[0] * 5 という1つしか無いリストをそれぞれlist[0]からlist[2]で参照しているだけなので、list[1][2]というたった1つの要素を変えたつもりでも、参照元[0] * 5 というリストそのものを書き換えてしまっているので、list[0], list[2]の要素も変わってしまう(様に見える) ということです。
(様に見える)、というのは、もともとlist[0]list[2]も固有の要素を持っているわけではないという意味です※ここ重要です。

なので

list = [[0] * 5] * 3
if id(list[0]) == id(list[1]):
    print("same id")  # same id

となります(id は固有に与えられたものなので、違うものであれば同じidであることはありません。)


ちなみに、先ほどの二次元配列を正しく初期化しようとすると、

list = [[0] * 5 for i in range(3)]
if id(list[0]) != id(list[1]):
    print("different id")  # different id

[0] * 5 というリストを3回作っているので、別のものとしてカウントされます。

となります。

これは私も理解できます。ところで

何故二次元の要素は同じだとカウントされないのだろう

ということを疑問に思いました。

先ほどの物は例として悪いので、新しいものを挙げます。

number = 0
list = [[number] * 5] * 3
print(list)  # [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
list[0][0] = 1
print(list)  # [[1, 0, 0, 0, 0], [1, 0, 0, 0, 0], [1, 0, 0, 0, 0]]
print(number)  # 0

こういった場合に関する疑問です。

上の宣言から、listの各要素はすべてが number であることがわかります。

そして代入時に

list[0][0] = 1

つまり

number = 1

よって、すべてのnumberを参照している値(list[0][0] から list[2][4])すべてが1に代わるのではないかと思ったのです。


私のどの部分が間違っているのでしょうか?


先ほど使ったid()関数を用いて考えます。

要素すべてがnumber を参照しているわけではない説

確かめてみましょう

number = 0
list = [[number] * 5] * 3
print(id(number))  # 1952997504
print(id(list[0][0]))  # 1952997504
print(id(list[0][1]))  # 1952997504
print(id(list[2][4]))  # 1952997504

全て同じですね。

この説は野獣先輩たまご説ていどの信ぴょう性しかなさそうです。


ということは

代入時に参照が解かれている説

number = 0
list = [[number] * 5] * 3
print(id(number))  # 1952997504
print(id(list[0][0]))  # 1952997504
list[0][0] = 1
print(id(number))  # 1952997504
print(id(list[0][0]))  # 1952997536
print(id(list[0][1]))  # 1952997504

らしいですね。

なぜこんなことが起こるのでしょうか?


実は、Pythonという言語の変数( リスト )の仕様に答えがあります。

答え

勘違いしがちなのですが、実際に変数やリストに値が代入されているわけではないのです。

実のところ、"値をメモリに記憶し、その参照先( アドレス )を保管している"のです。

つまり、list[0][0]が初めに参照してたのは、numberの参照先データです。

そのうえからlist[0][0] には、1 というデータを保管したアドレスを上書きされたので、numberには直接影響は出ません

しかし、list[1][0]list[2][0]list[0][0]のアドレスを参照しているので、こういった事態になるのです。


これは野獣先輩女の子説と同じくらいはっきりわかんだね…

あー、すっきりした。

というわけで、今回は私のバカな疑問でした。

それではPythonistaの皆さん、よいPython Life を!!

宣伝…

PythonでAtCoder Beginner Contest 080 ABC080

AtCoder Beginner Contest 080 ABC080

 

2017/12/03 21:00 ~ 22:40の問題でした。

 

簡単に説明すると

 

A:計算問題

B:型変換多用

C:bin換算

D:重複計算

 

となります。

A : Parking

問題文

駐車場があり、以下の二種類のプランのどちらかを選んで駐車できます。

  • プラン 1 : T 時間駐車した場合、 A×T 円が駐車料金となる。
  • プラン 2 : 駐車した時間に関わらず B 円が駐車料金となる。

N 時間駐車するとき、駐車料金は最小でいくらになるか求めてください。

制約

  • 1≦N≦20
  • 1≦A≦100
  • 1≦B≦2000
  • 入力は全て整数

入力

入力は以下の形式で標準入力から与えられる。
N A B

出力

駐車料金が最小で x 円のとき、x を出力せよ。

n, a, b = map(int, input().split())  # 入力
print(min(n*a, b))

N×AとBの小さいほうを返します。

B : Harshad Number

問題文

整数 X を十進法で表したときの各桁の数字の和を f(X) としたとき、X が f(X) で割り切れる場合、X はハーシャッド数です。
整数 N が与えられるので、ハーシャッド数かどうか判定してください。

制約

  • 1≦N≦108
  • 入力は全て整数

入力

入力は以下の形式で標準入力から与えられる。
N

出力

NN がハージャッド数ならば Yes を、そうでなければ No を出力せよ。

n = input()  # 入力
a = int(n)  # 数に変換
b = sum(map(int, list(n)))  # 各桁の合計
if a % b  == 0:
    print("Yes")
else:
    print("No")

b = sum(map(int, list(n))) とすることで、map関数でnを数に変換し、sumを実行しています。map関数に関してはこちらで。
delta114514.hatenablog.jp

C : Shopping Street

問題文

joisinoお姉ちゃんは、ある商店街に店を開こうとしています。

その商店街の店は、月曜日から金曜日の 5 つの曜日を午前と午後の 2 つの時間帯に分けて、これら 10 個の時間帯それぞれについて店を営業するか否かを決めることとなっています。ただし、1 つ以上の時間帯で店を営業しなければなりません。
商店街には既に N 個の店があり、1 から N までの番号がついています。
これらの店の営業時間の情報として Fi,j,k が与えられ、月曜日=曜日1、火曜日=曜日 2、水曜日=曜日 3、木曜日=曜日 4、金曜日 =曜日5、 午前=時間帯1、午後=時間帯 2 と対応させたとき、Fi,j,k=1 なら曜日 j の時間帯 k に店 i が営業しており、Fi,j,k=0 なら営業していないことを意味します。

店 i とjoisinoお姉ちゃんの開く店の両方が営業している時間帯の個数を ci としたとき、joisinoお姉ちゃんの店の利益は P1,c1+P2,c2+...+PN,cN となります。ただし、利益は負にもなりうることに注意してください。
1 つ以上の時間帯で店を営業しなければならないことに注意しつつ、
10 個の時間帯それぞれについて店を営業するか否かを決めるとき、joisinoお姉ちゃんの店の利益のあり得る最大値を求めてください。

制約

  • 1≦N≦100
  • 0≦Fi,j,k≦1
  • 1≦i≦N を満たす全ての整数 i に対して、Fi,j,k=1 を満たす (j,k) が必ず 1 つ以上存在する
  • −107≦Pi,j≦107
  • 入力は全て整数

入力

入力は以下の形式で標準入力から与えられる。

  • N
  • F1,1,1 F1,1,2 ... F1,5,1 F1,5,2
  • FN,1,1 FN,1,2 ... FN,5,1 FN,5,2
  • P1,0 ... P1,10
  • PN,0 PN,10

出力

joisinoお姉ちゃんの店の利益のあり得る最大値が x のとき、x を出力せよ。

n = int(input())
arr = [list(map(int, input().split())) for i in range(n)]
arm = [list(map(int, input().split())) for j in range(n)]  # 入力

ans = -9999999999  # 最小
for item in range(1, 1024):  # 2 ** 10 == 1024
    fund = 0
    for i in range(n):  # 店の数
        cou = 0  # かぶる数
        for x in range(10):  # 月-金(5) * 2
            if (item >> x) % 2 == 1 and arr[i][x] == 1:  # その時間で被るかどうか
                cou += 1
        fund += arm[i][cou]
    ans = max(ans, fund)  # 最大値更新

print(ans)

エラーの鬼ですね。

10重ループでも良いのですが汚いですし、そういうのはやりたくありません。

ところで、10重ループで各々2回回すとすると何回回すことになるのでしょうか。

1024回ですよね。 2 ** 10です。

なので、今回は10重ループの代わりにfor文を1024回回しています。

恐らく、この問題がわからない人は

(item >> x) についてわからないのだと思います。 ">>" は右ビットシフトです。
ビットですので、1ビットシフトごとに / 2されていきます。nビットシフトすると、/ (2**n)となります。

D : Recording

問題文

joisinoお姉ちゃんは、録画機を用いて N 個のテレビ番組を録画しようとしています。
テレビが受信できるチャンネルは C 個あり、1 から C までの番号がついています。
joisinoお姉ちゃんの録画したいテレビ番組のうち、i 個目のテレビ番組は、時刻 si から時刻 ti まで、チャンネル ci で放送されます。(ただし時刻 si を含み、時刻 ti を除く)
ただし、同じチャンネルで複数のテレビ番組が同時に放送されることはありません。
また、録画機は、あるチャンネルの時刻 S から時刻 T までを録画するとき、時刻 S−0.5 から時刻 T までの間、他のチャンネルの録画に使うことができません。(ただし時刻 S−0.5を含み、時刻 T を除く)
N 個のテレビ番組の全ての放送内容が含まれるように録画するとき、必要な録画機の最小個数を求めてください。

制約

  • 1≦N≦105
  • 1≦C≦30
  • 1≦si
  • 1≦ci≦C
  • ci=cj かつ i≠j ならば ti≦sj か si≧tj が成り立つ
  • 入力は全て整数

入力

入力は以下の形式で標準入力から与えられる。

N C
s1 t1 c1
:
sN tN cN

出力

必要な録画機の最小個数が x 個のとき、 x を出力せよ。

n, c = map(int, input().split())

r = [[0 for i in range(c)] for j in range(100000)]  # テレビ局の番組表
for dummy in range(n):  # 入力
    s, t, c = map(int, input().split())
    for j in range(s - 1, t):
        r[j][c - 1] = 1  # 放送中なら1

ans = 0
for i in range(100000):
    if sum(r[i]) > ans:
        ans = sum(r[i])  # 同時に放送されている番組の最大数

print(ans)

簡単ですよね。

CとDの難易度逆なんじゃないでしょうか…?

同時に放送されている最大数を求めるだけです。

分からないことがあれば気軽にコメントしてくださいね。じゃあな!

プログラミング(競プロ)に便利な Python3 用法集 ~出力編~


単純出力: print(n)

一番簡素な出力です。

n = "114514"
print(n)  # 114514

ぱっと見ではprintされるものが文字列なのか数なのか分かりませんが、

n = "114514"
print(n * 2)  # 114514114514

となり、"229028"とは表示されません。

そうしたいのであれば、int(n) としてみましょう

n = "114514"
print(int(n) * 2)  #229028

int()で囲ってやることで、nを数と認識して計算してくれます。



変数間の文字を指定: print("".join(list))

出力するものの間に表示するものを変えてくれます。

n = ["114514", "810", "893"]
print(n)  # ['114514', '810', '893']
print("アッー".join(n))  # 114514アッー810アッー893

リストを出力したいときに、[]"" は出力したくない…というときにも使えます。

n = ["114514", "810", "893"]
print("".join(n))  # 114514810893
print(" ".join(n))  # 114514 810 893


文字列に変数を挟み込む: print("{0}+{1}={2}".format("3", "4", "7"))

本来例えば、a + b = 7というのを表示しようとすると

a, b, c = 3, 4, 7
print(str(a) + " + " + str(b) + " = " + str(c))  # 3 + 4 = 7

というようにしなければなりません。ですが、format関数を使えば

a, b, c = 3, 4, 7 
print("{0} + {1} = {2}".format(a, b, c))  # 3 + 4 = 7

とできます。format()内のn番目の数が{}内の数に対応しています。リストや文字列等を代入することも可能です。

数ですので計算にも使えます


最終文字決定: print(n, end="")

pythonでは、print()の後には必ず改行が入ります。それを別のものに変えます。

n, m = 114514, 810
print(n)
print(m)
print(n, end="")
print(m)
print(n, end="アッー")
"""
出力
114514  改行が入っている
810
114514810
114514アッー
"""

ある意味、通常のprint()print(n, end="\n")であるとも言えますね。

PythonでAtCoder Beginner Contest 079 ABC079

AtCoder Beginner Contest 079 ABC079

 

2017/11/19 21:00 ~ 22:40の問題でした。

 

簡単に説明すると

 

A:orで3つをイコール

B:リュカ数を求める(キャッシュが好ましい)

C:3重ループ

D:ワーシャルフロイド

 

となります。

A : Good Integer

問題文

1118 のような、3 つ以上の同じ数字が連続して並んだ 4 桁の整数を 良い整数 とします。
4 桁の整数 N が与えられるので、N が 良い整数 かどうかを答えてください。

制約

  • 1000≦N≦9999
  • 入力は整数からなる

入力

入力は以下の形式で標準入力から与えられる。
N

出力

N が 良い整数 ならば Yes を、そうでなければ No を出力せよ。

素直に解いてみましょう

n = input()  # 入力を受け入れ
if n[0] == n[1] == n[2] or n[1] == n[2] == n[3]:  # 3つ並んでいる場合
  print("Yes")
else:
  print("No")

初心者の方にありがちなのが、"==" を "=" にしてしまうということです。

"="は代入に使うのに対し、"=="は比較演算子ですので、全くの別物です。
注意しましょう。

B : Lucas Number

問題文

今、日本は 11 月 18 日ですが、11 と 18 は隣り合うリュカ数です。
整数 N が与えられるので、N 番目のリュカ数を求めてください。
ただし、リュカ数は i 番目のリュカ数を Li とすると、
L0=2
L1=1
Li=Li−1+Li−2(i≧2)
と定義される数とします。

制約

  • 1≦N≦86
  • 答えは 1018 より小さいことが保証される
  • 入力は整数からなる

入力

入力は以下の形式で標準入力から与えられる。
N

出力

N 番目のリュカ数を出力せよ。

何も考えずにやると、

def lucas(n):
    if n == 0:
        return 2
    elif n == 1:
        return 1
    else:
        return lucas(n - 1) + lucas(n - 2)
print(lucas(int(input())))

というような再帰になるかもしれませんが、これだと明らかにTLE(時間超過)してしまいます。

なので、ここで超絶便利な方法をお教えします。

"@functools.lru_cache()"!!!\テッテレテッテテー/

使い方は簡単!

import functools

@functools.lru_cache()
def 以下略

def の一行前に書いてやることで、関数の戻り値を保存してくれるキャッシュを使えるようになります。

ええ、その速さは劇的です。劇的。

lucas(86)なんてしようものならどんな時間がかかるか想像できたものではありません

計算量でいうと O(580696784543858400) 位です。

しかし、キャッシュを使えば O(85) です。

比べ物にならないですよね。

ということで

import functools

@functools.lru_cache()
def lucas(n):
    if n == 0:
        return 2
    elif n == 1:
        return 1
    else:
        return lucas(n - 1) + lucas(n - 2)
print(lucas(int(input())))

これでよいです。
あるいは、

n = int(input())
 
lis = [2,1]
for i in range(2, n+1):
  lis.append(lis[i-1] + lis[i-2])
 
print(lis[n])

の様に、入力された番目まで作っていくというのもありかと思います。(というよりこっちのが簡単)

C : Train Ticket

問題文

駅の待合室に座っているjoisinoお姉ちゃんは、切符を眺めています。
切符には 4 つの 0 以上 9 以下の整数 A,B,C,D が整理番号としてこの順に書かれています。
A op1 B op2 C op3 D = 7 となるように、op1,op2,op3 に + か - を入れて式を作って下さい。
なお、答えが存在しない入力は与えられず、また答えが複数存在する場合はどれを出力してもよいものとします。

制約

  • 0≦A,B,C,D≦9
  • 入力は整数からなる
  • 答えが存在しない入力は与えられない

入力

入力は以下の形式で標準入力から与えられる。
ABCD

出力

作った式を、=7 の部分を含めて出力せよ。
ただし、記号は半角で出力せよ。
また、数字と記号の間に空白を入れてはならない。

CとBの難易度逆じゃないですかね?っていう問題。

3重ループでOK

a, b, c, d = list(input())
sign = "+-"
for i in range(2):  # 1つ目の記号
    for j in range(2):  # 2つ目の記号
        for k in range(2):  # 3つ目の記号
            if eval(a+sign[i]+b+sign[j]+c+sign[k]+d) == 7:
                print(str(a+sign[i]+b+sign[j]+c+sign[k]+d)+"=7")

forでそれぞれの記号が+の時と-の時を示しています。

eval() では、その引数がpythonの構文的に問題ないとき、それを実行します。eval()をしなければおかしなことになってしまいます。

あるいは、

a, b, c, d = list(map(int, list(input())))
if a + b + c + d == 7:
    print("{0}+{1}+{2}+{3}=7".format(a, b, c, d))
elif a + b + c - d == 7:
    print("{0}+{1}+{2}-{3}=7".format(a, b, c, d))
elif a + b - c + d == 7:
    print("{0}+{1}-{2}+{3}=7".format(a, b, c, d))
elif a - b + c + d == 7:
    print("{0}-{1}+{2}+{3}=7".format(a, b, c, d))
elif a - b - c + d == 7:
    print("{0}-{1}-{2}+{3}=7".format(a, b, c, d))
elif a - b + c - d == 7:
    print("{0}-{1}+{2}-{3}=7".format(a, b, c, d))
elif a + b - c - d == 7:
    print("{0}+{1}-{2}-{3}=7".format(a, b, c, d))
elif a - b - c - d == 7:
    print("{0}-{1}-{2}-{3}=7".format(a, b, c, d))

というようなごり押しも可能です。私は案外こういうのも好きです。

D : Wall

問題文

魔法少女のjoisinoお姉ちゃんは、この世にあるすべての数字を 1 に変えてやろうと思い立ちました。
1 つの数字を i から j(0≦i,j≦9) に書き変えるには魔力 ci,j が必要です。
今、目の前にある壁は縦方向に H、横方向に W のマス目になっていて、1 つ以上のマス目に 0 以上 9 以下の整数が 1 つずつ書かれています。
上から i(1≦i≦H) 番目、左から j(1≦j≦W) 番目のマスの情報として Ai,j が与えられ、

Ai,j≠−1 の場合はマスに Ai,j が書かれている
Ai,j=−1 の場合はマスに数字が書かれていない

ことを意味します。
この壁に書かれている数字を最終的に全て 1 に変えるのに必要な魔力の最小量を求めてください。

制約

  • 1≦H,W≦200
  • 1≦ci,j≦103(i≠j)
  • ci,j=0(i=j)
  • −1≦Ai,j≦9
  • 入力は整数からなる
  • 壁には一つ以上の整数が書かれている

入力

入力は以下の形式で標準入力から与えられる。

H W
c0,0 … c0,9
:
c9,0 … c9,9
A1,1 … A1,W
:
AH,1 … AH,W

出力

壁に書かれている数字を最終的に全て 1 に変えるのに必要な魔力の最小量を出力せよ。

ワーシャルフロイドと呼ばれる、二点間の最短距離(今回でいえば0~9の数から1までの最小消費魔力)を求めるような問題です。

h, w = list(map(int, input().split()))  #サイズ読み取り
power = [list(map(int, input().split())) for i in range(10)]  #魔力読み取り

for i in range(10):
    for j in range(10):
        for k in range(10):
            power[j][k] = min(power[j][i] + power[i][k], power[j][k])  # 三重ループで各数間の最小魔力を求める。

ans = 0
for i in range(h):
    wall = list(map(int, input().split()))  # 壁読み取り
    for item in wall:
        if item < 0:
            continue
        ans += power[item][1]  # それぞれの壁に使う魔力を求める
print(ans)

聞いてしまうと、何だ、そんなことか。という感じですよね。

話をするとすれば、三重ループについてでしょうか。

三重ループでは、二数間の最小魔力消費量を「その純粋な数と、別の数を挟む(3数間)の魔力消費量のうち小さいもの」としています。
これをすべての数に対して行うことで最小を導き出すことができます。

Python3 ~split関数について~

競技プログラミング(あるいはそれ以外でも)で使わない問題はないのではないと思うほど使用頻度の高いsplit()ですが、それを高度な使用をしようとしてできる人はあまり多くないのではないでしょうか。今回は、そんな愛されながらも謎に包まれたsplit()について解説します。

よくある使い方

たとえば、
AtCoder Beginner Contest030_AA - 勝率計算

問題文

野球のAtCoderリーグのシーズンが終了しました。チーム高橋は A試合中 B 回勝ち、チーム青木は C 試合中 D回勝ちました。AtCoderリーグでは勝率の高い順に高い順位が与えられます。チーム高橋とチーム青木のどちらが勝率で勝っているか答えるプログラムを作成してください。

入力

入力は以下の形式で標準入力から与えられる。

A B C D
1行目には、4 つの整数 A,B,C,D(1≦A,B,C,D≦100)が与えられる。
B ≦ A かつ D ≦ C を満たすことが保証される。

出力

チーム高橋の勝率がより高いときは TAKAHASHI、チーム青木の勝率がより高いときは AOKI、両チームの勝率が等しいときは DRAW と 1 行に出力せよ。出力の末尾にも改行をいれること。

などでは、入力にsplit()をつかうべきですよね。A, B, C, Dそれぞれがn桁だと明示されていれば入力そのものを文字列だと受け取り、インデックスの指定で計算してやることもできますが、今回は1から3桁まで開きがあるので(できないとは言いませんが)やるべきではありません。

ここで役に立つのがsplit()です。

引数(split(*)の*の部分)を指定せずに使ってやると、スペース区切りでリストに入れてくれます。

n = input().split()  #114 514 810 931
print(n)  # ['114', '514', '810', '931']

区切り文字を指定して区切る

引数の中に文字(列)を入れてやると、それを区切りとしてリストを作ってくれます。

n = input().split("アッー")  # 114アッー514アッー810アッー931
print(n)  # ['114', '514', '810', '931']
arrgh = "アッー"
n = input().split(arrgh)  # 114アッー514アッー810アッー931
print(n)  # ['114', '514', '810', '931']

区切り回数を指定する

第二引数に数を指定してやると回数指定ができます。

はじめの一回だけ区切りたい...あるいは、最後の一回だけ区切りたい、と言った時に使えます。

n = input().split("アッー", 1)  # 114アッー514アッー810アッー931
print(n)  # ['114', '514アッー810アッー931']

あるいは、split()rsplit()に変えてやることで、反対から区切ってくれます。

n = input().rsplit("アッー", 1)  # 114アッー514アッー810アッー931
print(n)  # ['114アッー514アッー810', '931']

すでにある文字列・リストを区切ってリストにする

すでに存在している文字列に後置で実行してやることによって、リストを作成することもできます。

n = "114 514 810 931"
n = n.split()
print(n)  # ['114', '514', '810', '931']

リストにsplit()は使えません。ので、一度文字列に直してやります。

n = ['114 514 810 931', '893 889464']
n = ' '.join(n).split()
print(n)  # ['114', '514', '810', '931', '893', '889464']
"""※str(n)では
['114 514 810 931', '893 889464']
というように中括弧やアポストフィーも出力されるので
' '.join(n)とし、nの要素間に' '(スペース)を挟んで出力する
114 514 810 931 893 889464
とする。
"""

とできます。

簡単な部分のみを解説しましたが、非常に便利な関数なので、ぜひ使ってみてください。

よいPythonLifeを!

Python3 ~map関数について~

今回は map()関数 についてお話します。

簡単に言うと、リストやタプルなどのシーケンスに対してmap(a, b) はbに対してaしたものを返す。ということです。

たとえば

def double(n):
    return (int(n) * 2)
lis = ["114514", "810", "893"]
print(list(map(double, lis)))  # [229028, 1620, 1786]

というように、全ての要素を引数として読み込みそれぞれ"114514", "810", "893"に対して ×2をして返します。

分岐・ループ

lis = ["1024", "23", "16", "256", "1", "32"]
def beautify(n):
    m = format(int(n), "b")
    if m == "1" + "0" * (len(m) - 1) and n != "1":
        return n
    else:
        return '1024'  # 2の累乗数ならそのままreturnし、そうでないなら最も美しい数をreturnする。
print(list(map(beautify, lis)))  # ['1024', '1024', '16', '256', '1024', '32']

というように、分岐やループが入っているようなものも可能です。

lambda式

あるいはlambda式も使えますので

lis = ["114514", "810", "893"]
print(list(map(lambda n:int(n)*2, lis)))  # [229028, 1620, 1786]

というようにも扱えます。

ですが、map関数を使おうとしてできることは、大方リスト内包表記も使えます。そちらのほうがほとんどの場合高速なので、リスト内包表記を使うほうが良いと個人的に思います。

lis = ["114514", "810", "893"]
print([int(n) * 2 for n in lis])  # [229028, 1620, 1786]

ちなみに、このmap関数が返すのはmap型です。これ自体を直接見ることはできないので、毎回list()としてリストに変換しています。

プログラミング(競プロ)に便利な Python3 用法集 ~入力編~


単純入力: n = input()

一番簡素な入力だと思います

n = input()  # 114514
print(n)  # 114514

大切なことですがこのとき、nの中に入っているのは数としての“114514”ではなく、文字列(str)としての“114514”です。ですので、

n = input()  # 114514
print(n * 2)  # 114514114514

となり、"229028"と表示されるわけではありません。


単純入力~数~: n = int(input())

恐らく最も使用頻度の高いものではないでしょうか。

n = int(input())  # 114514
print(n * 2)  #229028

しっかりと数だと認識してくれます。

input()を後でintに直すよりもよほどスマートですね。必須技能です。


リスト入力: n= input().split()

入力をスペース区切りでリストにしてくれます。

n = input()  # 114514 810 893
print(n)  # 114514 810 893

こちらも先ほどの例にもれずすべて文字(str)カウントです。

n = input().split()  # 114514 810 893
print(sum(n))   # 114514810893


リスト入力~数~: n = list(map(int, input().split()))

こちらは入力をスペース区切りで数としてリストにしてくれます。

n = list(map(int, input().split()))  # 114514 810 893
print(n)  # [114514, 810, 893]

数ですので計算にも使えます

map関数については
Python3 ~map関数について~ - やるだけPython競プロ日誌
を参照ください。

n = list(map(int, input().split()))  # 114514 810 893
print(sum(n))  # 116217


分割入力: n, m = input().split()

重要なのは、input().split() というのは特別な関数でもなんでもなく、ただリストを返すだけだという点です。

ですのでこれは、input()split()という名前のリストがあり、その一つ一つのインデックスにn, mが対応しているという見方もできます。

n, m = input().split()  # 114514 810
print(n + m)  # 114514810

当然

n, m = list(map(int, input().split()))  # map(int, input().split())でも同じ

とすれば

n, m = list(map(int, input().split()))  # 114514 810
print(n + m)  # 115324

となります。


一部を一つに、残りをそれ以外に: n, *m = input().split()

変数名に "*"を先付けすることによって、あふれ出る要素をそこに入れることができます。

n, *m = input().split()  # 114514 810 893 364
print(n, m)   # 114514 ['810', '893', '364']

*n, m = input().split()  # 114514 810 893 364
print(n, m)  # ['114514', '810', '893'] 364

n, *m, l = input().split()
print(n, m, l)  # 114514 ['810', '893'] 364


リスト入力~改行~: for i in range(times): n.append(input())

複数行の入力をすべて一つのリストに入れ込みます。

times = int(input())  # 3
n = []
for i in range(times):
    n.append(input())
    """
    114514
    810
    893
    """
print(n)  # ['114514', '810', '893']
n.append(int(input()))

とすれば数扱いになりますので

times = int(input())  # 3
n = []
for i in range(times):
    n.append(int(input()))
    """
    114514
    810
    893
    """
print(sum(n))  # 116217

となります。


リスト入力~リスト内包表記~: n = [input() for i in range(times)]

こちらのほうが上のappendを使った方法よりも2倍ほど高速です。さらにコードも短くなりますし、可読性も高まります。

times = int(input())  #3
n = [input() for i in range(times)]
"""
114514
810
893
"""
print(n)  # ['114514', '810', '893']

しつこいですが、

n = [int(input()) for i in range(times)]

として

times = int(input())  #3
n = [int(input()) for i in range(times)]
"""
114514
810
893
"""
print(sum(n))  # 116217

とできます。


複数行に複数のデータ: for i in range(times): n.append(input().split())

times 回分だけ、nに input().split() を append します。なので、n は二次元配列になります。

times = int(input())  # 3
n = []
for i in range(times):
    n.append(input().split())
    """
    114514 810
    931 893
    364 364
    """
print(n)  #[['114514', '810'], ['931', '893'], ['364', '364']]


あるいは例のごとく

n.append(list(map(int, input().split()))) 

とすることで

times = int(input())  # 3
n = []
for i in range(times):
    n.append(list(map(int, input().split())))
    """
    114514 810
    931 893
    364 364
    """
print(n, sum(n[0]))  # [[114514, 810], [931, 893], [364, 364]] 115324

ともできます。


複数行に複数のデータ~リスト内包表記~: n = [input().split() for i in range(times)]

できることは上のものと同じですが、こちらのほうが高速です。

times = int(input())  # 3
n = [input().split() for i in range(times)]
"""
114514 810
931 893
364 364
"""
print(n)  # [['114514', '810'], ['931', '893'], ['364', '364']]

intで読み込みます。

times = int(input())  # 3
n = [list(map(int, input().split())) for i in range(times)]
"""
114514 810
931 893
364 364
"""
print(n, sum(n[0]))  # [[114514, 810], [931, 893], [364, 364]] 115324


いつデータ入力が終わるかわからない複数行にわたる入力 : except EOFError: break

EOFError は『End Of File Error』のことで、ファイルの読み込める物はもうない(入力がない)のに読み込みを続けようとしたときに出るエラーです。なので、読み込みが終わったときに辞める、ということになります。

n = []
while True:
    try:
        n.append(input().split())
    except EOFError:
        break
    """
    114514
    810
    893
    """
print(n)  # [['114514'], ['810'], ['893']]