0
0

More than 1 year has passed since last update.

「新・明解Pythonで学ぶアルゴリズムとデータ構造」で勉強日記#1~4まとめ(第1章部分)

Posted at

第1章分のまとめです。自分で振り返れるように作りました。

【出典】「新・明解Pythonで学ぶアルゴリズムとデータ構造」

「新・明解Pythonで学ぶアルゴリズムとデータ構造」で勉強日記#1
さて、今日から、上記の本にて学んだことをまとめておきます。
本来はド頭からやるべきですが、なんとなく繰り返し処理からでいいかなーと思ったので、ここからスタートします。もし振り返ってめっちゃ重要なこと書いてある―!って思ったら追加編集しようと思います。

繰り返しについての部分からになります。(P14の2-1)

以下はすべて同様の処理を別表記にしているみたいですね。

1からnまでの総和を求める。

最初のお題でありがちなやつですね。(総和をどこで使うのかよくわかってない( ;∀;))
ここではwhile文とfor文それぞれの表記をしてくれています。

list1-7
#1からnまでの総和を求める。while文

print('1からnまでの総和を求めます')
n = int(input('nの値:'))

sum = 0
i = 1

while i <= n:
    sum += i
    i += 1

print(f'1から{n}までの総和は{sum}です。')
list1-8
#for 文

print('1からnまでの総和を求めます')
n = int(input('nの値:'))

sum = 0
for i in range(1, n + 1):
    sum += i

print(f'1から{n}までの総和は{sum}です。')

for文にすると簡潔に書けますね…。正直while文の存在意義はどこにあるのか初学者の私には見当もついてないです。
実はラスボス戦で効いてくるポジションなのかな?
わかる方がいればコメントください。

そして、おまけでガウスの法則版を載せます。

EX16
#ガウスの法則
print('1からnまでの総和を求めます')
n = int(input('nの値:'))

sum = n * (n + 1)//2

print(f'1から{n}までの総和は{sum}です。')

今回学びになった点は、数学の偉大さですね。

なんちゃって理系なので、ガウスの法則とか覚えてなかった…。

「新・明解Pythonで学ぶアルゴリズムとデータ構造」で勉強日記#2

2値のソートと2値の変換

list1-9
#aからbまでの総和を求める

print('aからbまでの総和を求めます')
a = int(input('整数a:'))
b = int(input('整数b:'))

if a > b:
    a, b = b, a #2値の交換

sum = 0
for i in range(a, b + 1):
    sum += i

print(f'{a}から{b}までの総和は{sum}です。')

こういうのって最初に聞いたことあったっけ?てくらい単純かつなるほどな書き方。aとbを入れ替えるのに複雑な式とか考えがちだけど、こういうのでいいんだっていうプチ感動があります。

繰り返しの過程における条件判定(その1)

list1-10
#aからbまでの総和を求める

print('aからbまでの総和を求めます')
a = int(input('整数a:'))
b = int(input('整数b:'))

if a > b:
    a, b = b, a #2値の交換

sum = 0
for i in range(a, b + 1):
    if i < b:
        print(f'{i} + ',end = '')
    else:
        print(f'{i} = ',end = '')
    sum += i

print(sum) 

上記は処理が長くなるので好ましくないらしい…。
というのもif文の処理がb回?あるので、10000回とかになったときに大変なんだとか?
ということで、下記↓

list1-11
#aからbまでの総和を求める

print('aからbまでの総和を求めます')
a = int(input('整数a:'))
b = int(input('整数b:'))

if a > b:
    a, b = b, a #2値の交換

sum = 0
for i in range(a, b ):
    print(f'{i} + ',end = '')
    sum += i
#aからb-1までの値の後ろに+をつけて出力

print(f'{b} = ',end = '')
sum += b
#b値の後ろに=をつけて出力

print(sum) 

どれくらい処理が変わるのかは実感はありませんが、やることは減ったような気がするのは確かになと。

繰り返しの過程における条件判定(その2)

list1-12
#記号と文字を交互に表示(その1)

print('記号文字+を-交互に表示します')
n = int(input('全部で何個:'))

for i in range(n):
    if i % 2:
        print('-', end='') #奇数
    else:
        print('+', end='') #偶数
#n=5を入れたとすると0,1,2,3,4をiにいれるので+-+-+という結果が得られる?
print()

奇数なら-偶数なら+を表記しますよという例。
if文のあと=とかいらないのか~って思いました。多分プログラミングやっている方からすれば普通なんでしょうけど、私には驚きという…。
ちなみにこれは余りがあればtrueってことでいいんですかね?ソウイウコト全然ワカラナイ…。
あと、オプション引数というのを今調べて知りました。
便利だなーと。
そしてこれまた、判定回数が増えるのでよくないとか…。
改良版が↓

list1-13
#記号と文字を交互に表示(その2)

print('記号文字+を-交互に表示します')
n = int(input('全部で何個:'))

#'_'は変数の値をループ本体で使用しない
for _ in range(n // 2):
    print('+-', end='') 

if n % 2:
    print('+', end='')
#for文とif文が合わせてn割る2の商(for文)と余り(if文)に対応している。
print()

次はrangeの()内に数式入れるのかっていう驚き。なんとも初学者感まるだしで…。
あと_の使い方がさらっと説明されていたけど、よくわかりません!(タスケテ)

その2のif文についてもその1では奇数判定してたくせに、その2では偶数じゃんと思ったんですが、そもそもやっていることが違ったんですね汗

その1では、単純なfor文にrange関数を使っているので、inputに入れた数値から-1された数字が渡されるんですね。
その2では、forとifに使っている数式を合わせると商と余りの関係になるんですね。
なんだか算数からやり直したくなる気分。

こういった発想がプログラミングには大事なのかと記事を起こしながら思いました。

「新・明解Pythonで学ぶアルゴリズムとデータ構造」で勉強日記#3

繰り返しの過程における条件判定(その3)

for文の続きですね。前回までは改行をしないプログラムでしたが、ここでは改行を指定します。
次の文は*をn個w回改行します

list1-14
#n個の記号文字*をw個ごとに改行しながら表示(その1)

print('記号文字*を表示します。')
n = int(input('全部で何個:'))
w = int(input('何個ごとに改行:'))

for i in range(n):
    print('*', end='')
    if i % w == w-1:
        print()      #改行

if n % w:
    print()         #改行

そして例のごとくその1はif文が処理回数を増やすので効率的でないということですね。
変更したのが↓で

list1-15
#n個の記号文字*をw個ごとに改行しながら表示(その2)

print('記号文字*を表示します。')
n = int(input('全部で何個:'))
w = int(input('何個ごとに改行:'))

for _ in range(n // w):
    print('*' * w)

rest = n % w
if rest:
    print('*' * rest)

また_出てきましたね。変数を置くまでもない時に使えばいいのでしょうか?
そしてまた商と余りを使った書き方ですね。
商の回数for文を回して(改行)w個並べ、余りの文は最後の行に余りの数だけ*を並べるというようなイメージでしょうか?

正の値の読み込み

ここでは、負の数を消す処理をしていますね。
初回のlist1-8では負の数を代入すると処理できなかったので修正版として↓

list1-16
#1からnまでの総和を求める(nに正の整数値を読み込む)

print('1からnまでの総和を求めます。')

while True:
    n = int(input('nの値:'))
    if n > 0:
        break

sum = 0
i = 1

for i in range(1, n + 1):
    sum += i
    i += 1
print(f'1から{n}までの総和は{sum}です。')

'pythonでは後判定繰返しのための繰返し文が提供されないため、前判定繰り返し文とbreak文を組み合わせる必要がある'

コラム1-11:for文終了時のカウンタ用変数の値
コラムでちょこちょこ注意点を書いてくれていてありがたいです。

while i <= n: 繰返し終了時のiの値はn + 1
for i in range(開始値, n + 1):繰り返し終了時のiの値はn

辺と面積が整数値である長方形

list1-17
#縦横が整数で面積がareaの長方形の辺の長さを列挙

area = int(input('面積は:'))

for i in range(1, area + 1):
    if i * i > area: break
    if area % i : continue
    print(f'{i}x{area // i}')

'continue文が実行されると、ループ本体内の後続部の実行がスキップされて、プログラムの流れが判定に戻る。'

コンテニューと聞くと続きそうなイメージが先行されちゃうんですが、後続部が飛ばされて処理すんですねー。混乱しそう。

list1-18
#10 ~99の乱数をn個生成(13が生成されたら中断)

import random

n = int(input('乱数は何個:'))

for _ in range(n):
    r = random.randint(10, 99)
    print(r, end=' ')
    if r == 13:
        print('\n事情により中断します。')
        break
else:
    print('\n乱数生成終了')

'else節はbreakが実行されなかったときのみ実行される。'
今までifの中で使うものと認識してた・・・。

「新・明解Pythonで学ぶアルゴリズムとデータ構造」で勉強日記#4

繰返しのスキップと複数のrangeの走査

走査の意味を調べてみましたが、よくわかりませんでした。ので、参考程度に載せておきます。
そう‐さ【走査】
[名](スル)テレビカメラなどで画像を多くの点や線状に分解し、それぞれの輝度・色相・色度などを順次に電気信号に変換すること。また逆に、受像機で電気信号を画像に組み立てること。
【出典】デジタル大辞泉
だそうです。
繰り返しのスキップというとなんだかどう使うんだろうかという気はしますが、いわゆるcontinueを使った文みたいですね。ここでは8をスキップして表示する処理をしています。

list1-19
#1から12までを8だけスキップして繰り返す(その1)

for i in range(1, 13):
    if i ==8:
        continue
    print(i, end=' ')
print()

ここで、もし8という決まった数字がわかっている場合はlist1-20のように書き換えも可能です。

しかし、入力する数字が定まっていない場合はifとcontinueを使う必要があるみたいですね。

list1-20

#1から12までを8だけスキップして繰り返す(その2)

for i in list(range(1, 8)) + list(range(9, 13)):
    print(i, end=' ')
print()

リストを生成してそれぞれを結合して表示させる処理ですね。
list関数にrange関数を入れてるのを見て、こういうのさらっと書けるようになりたいななんて思いました。関数の使い方はExcelとかをイメージして使っていけばできるようになりますかね…?
最初のうちは整理する意味で一つずつ変数を定義して入れ子のように組んでもいいのかもしれないですね。(どちらが整理しやすいかはわからない)

コラム1-12 値比較演算子の連続適用とド・モルガンの法則

数学Aでやる集合についての話ですね。ここで高校数学が活きてくるというのはなかなか熱いと思います。
さて、List1C-2に示すのは、「2桁の整数値」を読み込むプログラムです。

List1C-2
# 2桁の正の整数値(10~99)を読み込む

print('2桁の整数値を入力してください。')

while True:
    no = int(input('値は:'))
    if no >= 10 and no <= 99: #変更する文
        break
print(f'読み込んだのは{no}です。')

このプログラムでは、andを使っていますね。and や or はそれなりにPC触ってると常識ではあるけど、どのタイミングで使うのが有効なのかいまいちわかっていない…。
でもド・モルガンの法則といわれるとベン図書けばいいのかなあって感じですかね。

ド・モルガンの法則を適用
a and b = not(not a or not b)
a or b = not(not a and not b)
二重否定ってやつですね。

List1C-2ド・モルガンの法則を適用
# 2桁の正の整数値(10~99)を読み込む

print('2桁の整数値を入力してください。')

while True:
    no = int(input('値は:'))
    if not( no < 10 or no > 99): #変更した文
        break
print(f'読み込んだのは{no}です。')

なんかnoが頻出するので見づらい気はしますが、not関数を用いることで割とすっきりしたイメージですね。
使い分けは自由ですが、記号なんかはタイピングするのなかなか面倒な気もするので私はnot関数使っている方が好きです。

構造化プログラミング(整構造プログラミング)
単一の入口点と単一の出口点とをもつ構成要素だけを用いて、階層的に配置してプログラミングを構成する手法のことで、順次、選択、繰返しの3種類の制御の流れを利用する。

多重ループ

いよいよプログラミング感出てきましたね。名前だけでもプログラム感出てます。
list1-21では、2重ループ?をしています。for文の中にfor文を入れます。
ちなみに多重ループは2重ループや3重ループなどがあるのでそれらの総称を指します。(たぶん)

list1-21
#九九の表を表示

print('-' * 27)
for i in range(1, 10): #行ループ
    for j in range(1,10): #列ループ
        print(f'{i * j:3}', end='')
    print()
print('-' * 27)

for文は何度やってもイメージがしづらい気がします。そこがまだ初心者の感覚なんだろうなと…。

list1-21ではつぎの順で処理されています
※()内は(始値,増値,終値)の順です。
①行ループ(i:1, 1, 9)
②列ループ(j:1, 1, 9)
③i * jを3桁で表示
④列ループ
⑤改行
⑥行ループ
つまりjの列をiの回数行表示させるということですね。
iが1の時、jは1から9までをとり、処理が終わったらiに2を入れて、jを1から9まで入れ込んで・・・の繰り返しをする形ですね。
カレンダーとか作れそうな気がします。(カレンダーモジュールあるけど・・・)

コラム1-13 random.randint

乱数を生成するrandom.randint(a, b)はa以上b以下の乱数を生成してa以上b以下の整数値の中から無作為に1個の整数値抽出してその値を返却する。
さらっと書かれていたのでよくわからないですが、注意書き程度に書いておきます。

直角三角形の表示

二重ループを応用すると図形の表示が可能になります。
ここではアスタリスクを羅列して描画します。

list1-22
#左下側が直角の二等辺三角形を表示

print('左下直角の二等辺三角形')
n = int(input('短辺の長さ:'))

for i in range(n):
    for j in range(i + 1):
        print('*', end = '')
    print()

ちょっと整理すると、n=5としたときrange(5)なので、0,1,2,3,4をiに入れ込んでいく…。
i, j =(0, 1),(1, 2),(2, 3),(3, 4),(4, 5)ということですね?
(range関数がn-1の数字をとることは分かるんですが、組み合わさっていくと?が浮かんでくるようなこないような…。)

そして応用として以下

list1-23
#右下側が直角の二等辺三角形を表示

print('右下直角の二等辺三角形')
n = int(input('短辺の長さ:'))

for i in range(n):
#変数名を_にすることで、ループ本体の中でカウンタ用変数の値を使わないことを伝える。
    for _ in range(n - i - 1): #1
        print(' ', end = '')
    for _ in range(i + 1): #2
        print('*', end = '')
    print()

list1-22は左揃えでしたが、ここでは右揃えをしています。
ここではforが3つ出てきますね。インデントに注目すると、同じところで揃えられているので、#1,#2の順番で並列に処理されます。(これも一応2重ループという扱いでいいのかな?)
仮にforの中にforを入れて、さらにforを入れた場合、*がprintされ終わってから空白がprintされるという流れになるので三角形ではなくなりますよね。(#1に#2を内包させた場合。)
インデントの大事さと、for文の仕組みがうっすらわかるような文だなと思いました。

ちなみに、1-22のjも_に変更可能です。

コラム1-14 変数の補足

「pythonでは、データ、関数、クラス、モジュール、パッケージなどあらゆるものがオブジェクト」だそうで、
「オブジェクトは型を持つとともに記憶域を占有している」らしいです。
「pythonの変数は値を保持しないため、変数とは値を格納する箱のようなものであるという例えが当てはまらない」とのことで、ちょこちょこ解説で使われるあれは、不適切だったんだなと…。こういう情報は結構大事ですよね。
「変数は、オブジェクトを参照する、すなわち、オブジェクトに結びつけられた名前に過ぎない」(タグ付けくらいの感覚らしい)
「すべてのオブジェクトは、記憶域を占有してい型を持つだけでなく、他のオブジェクトと識別できるようにするための識別番号すなわち同一性もっている」(つまりIDみたいな割り振りがされてるってことかい?興味深い話デスネ)

ID変数のイメージ
 n = 17
id(n)

id(17)

#17というint型オブジェクトにnという名前を与えるイメージ。

それぞれ、同じIDが出力されます。つまり17という数字にもIDというものが割り振られていて、n = 17と書くと17のIDに対してnを紐づけするといったイメージでしょうか?

list1C-3
#関数内外で定義された変数とオブジェクト
#オブジェクトと変数の識別番号を表示

n = 1 #広域変数(関数内外で利用できる)

def put_id():
    x = 1 #局所変数(関数内のみで利用できる)
    print(f'id(x) = {id(x)}')

print(f'id(1) = {id(1)}')
print(f'id(n) = {id(n)}')
put_id()

記憶域期間という概念がpythonにはない。とのことで…処理が終わった後も使うことができるらしいです。逆に言えば同じ名前を使うときは消す処理とか再定義みたいな処理がいるわけですね。

list1C-4
#1から100まで繰返して表示

for i in range(1, 101):
    print(f'i = {i:3} id(i) = {id(i)}')

上記を実行すると1から100までのIDを表示します。
具体的に何に使えばいいのかはわかっていませんが、参照するときに、同じIDを使っているんだぞーという確認ができるという話でいいですかね?

さて、以上で「第1章 基本的なアルゴリズム」が終了しました。

次は「第2章 データ構造と配列」について学びます。

ありがとうございました。

0
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0