mocchaso note

サーバーエンジニアが何か色々呟いているところ。

Python100本ノック 92本目~96本目(91本目~95本目)

Python100本ノック、92本目から96本目についてです。
終わりが見えてきたぞおおお

qiita.com


92本目:1を何回書いたか


問題:高橋君は1以上N以下のすべての整数を10進表記で1回ずつ紙に書きました。
この作業で、高橋君は1という数字を何個書いたでしょうか。

特に問題無く解けました。
あまり時間をかけず、しかも関数内を3行で済ませることができました!

93本目:リモコン


問題:高橋君は、エアコンの設定温度を変更しようとしています。
現在の設定温度はA度ですが、これをB度に設定したいと思っています。
エアコンのリモコンは1回ボタンを押すことで、
1度設定温度を下げる、もしくは上げる
5度設定温度を下げる、もしくは上げる
10度設定温度を下げる、もしくは上げる
の、6種類の操作のいずれか1つを実行することが出来ます。
高橋君が設定温度をA度からB度に変更するために押すボタンの最小回数を求めなさい。

時間はかかりましたが特に問題無く解けました。
自分は、以下の方針を立てました。

  • 現在の設定温度に6種類の操作をそれぞれ当てはめる

  • 6種類の操作の内、B度に最も近づくことができる操作を選ぶ
    = |B - 操作した後の温度|が最小となる操作

コーディングの内容としては、

def change_temperature(A, B):
    if not type(A) is int or not type(B) is int:
        # 引数の型がintでないとピッタリBになることは無いのでreturn
        return -1
    operate_list = [-10, -5, -1, +1, +5, +10]
    now_temperature = A
    push_count = 0
    while now_temperature != B:
        ## operate_listの中で、一番Bに近づくことができる操作を求める
        ## 現在の設定温度に対して操作を行った後、Bとの温度差の最小値を求める。それを実際に次の操作として選択する
        
        # now_temperatureに操作を行った後のBとの温度差
        diffs_between_B = list(map(lambda x: abs(B - (now_temperature + x)) , operate_list))
        # 温度差の情報のうちの最小値
        min_diffs = min(diffs_between_B)
        # 最小値がdiffs_between_Bの何番目に格納されているか。これはoperate_listの並びと対応している
        min_diffs_idx = diffs_between_B.index(min_diffs)
        # 求めた最小値のインデックスをoperate_listに指定し、実際に操作を行う
        optimal_operation = operate_list[min_diffs_idx]
        now_temperature += optimal_operation
        # カウント
        push_count += 1

となっています。
while文から抜けたらB度に達したことになるので、return push_countしています。
方針をコードに落とし込むのに時間がかかりましたが、達成感がすごかったです笑

~おまけ~
引数の数値がint型ではなかったら調査すらしないという処理を、関数の最初に加えました。
その際、type()isinstance()について調べました。
参考サイト:https://note.nkmk.me/python-type-isinstance/


94本目:高橋くんと魔法の箱


問題:高橋くんは魔法の箱を持っています。
この箱に整数を入れるとそれに対応した整数が出てきます。
出てくる整数は入れた整数だけによって決まり、同じ整数を入れると毎回同じ結果が得られます。
高橋くんは任意の整数xについて、xを入れた時と2xを入れた時に出てくる整数が同じであることに気づきました。
高橋くんが入れた整数がN個与えられるので、最大で何種類の整数が出てくるか答えてください。

一応解けましたが、時間かかりました。
問題の内容を理解してから、実装の方針を立てるのに苦労しました。
立てた方針は以下の通りです。

  • 任意の整数x, yについて、出てくる整数が同じかを確かめる関数check(x, y)を作る
    最初は、メインの関数に書いていましたが、見づらかったので分裂させました。
    yは問題文で言うと2xに該当します。

  • メインとなる関数内で、高橋君が入れた整数のリストに対してfor文を回す

    • 先頭の要素を見た時点でcountを+1する
      リストに要素が1つでもあれば、出てくる整数は必ず1種類以上になるためです。
    • i番目(i>0)では、nums_list[i] / nums_list[i-1] == 2が成り立つかどうか調べる
      成り立てば、i-1番目の時の整数と同じ整数が出ると言えるので、何もしません。 成り立たなければ、カウントを増やします。

これらの方針に則って、以下のコードを書きました。

def check(x, y): # x, yを入れた時に同じ整数が出るかどうかを確かめる
    if x > y:
        # x < yとなるように入れ替える
        t = y
        y = x
        x = t
    return y / x == 2 

def magic_box(nums_list):
    count = 0
    for i in range(len(nums_list)):
        if i == 0:
            count += 1
        else:
            if not check(nums_list[i-1], nums_list[i]):
                count += 1
    return count


私のコードは少し長くなってしまいましたが、Qiita記事のコードは非常に短かったです...!


95本目:123引き算ゲーム


問題:最初に、数字nが与えられます。
1, 2, 3の中から好きな数字を選び、与えられた数字に対し、引き算を行う、という処理を行うことができます。
この処理は100回まで行うことが可能であり、最終的に数字を0にすることが目標のゲームです。

しかし、計算途中でなってはいけないNG数字が3つ(リスト型で)与えられており、この数字に一時的にでもなってしまった瞬間、このゲームは失敗となります。NG数字がnと同じ場合も失敗となります。
あなたは、このゲームが、目標達成可能なゲームとなっているかを調べたいです。
目標達成可能な場合はYES、そうでない場合はNOと出力してください。


特に問題無く解けました。
と思いきや、n=300, NGワード=[57,121,244]の時のみうまく行きませんでした。
100回目の試行の後にwhile文を抜けた後、"NO"を出力し忘れていました..。
解き方は、Qiita記事のものとだいたい同じでした。

96本目:割り切れる日付


問題:高橋君は割り切れる日付が好きです。
割り切れる日付とは、年÷月÷日の計算結果が整数になる日付のことです。
例えば今日の日付は2012年5月2日ですが、2012÷5÷2=201.2となり整数ではないので、
今日の日付は割り切れる日付ではありません。

高橋君は割り切れる日付が好きでたまらないので、次の割り切れる日付を心待ちにして、
毎日今日が割り切れる日付かどうかをチェックしてしまいます。
彼に少しでも多くの仕事をしてもらうために、入力として与えられた日付以降で
最初に来る割り切れる日付を求めなさい。
ただし、入力として与えられた日付が割り切れる日付だった場合は、
与えられた日付が答えになります。

例:check_date("2012/05/02") ==> "2013/01/01"


以前にも‘datetime‘を使う問題がありましたが、まだ使い方を覚えていませんでした。
参考サイト:Pythonのdatetimeで日付や時間と文字列を変換(strftime, strptime) | note.nkmk.me

上記サイトを見ながらでしたが、問題無く解けました。
Qiita記事の解法を見たところ、以下の点が異なっていました。

  • 日付が割り切れるかどうかのチェックについて
    自分:日付の初期値は、while文の外で別にチェックしている
    記事:日付の初期値も、while文の中でまとめてチェックしている
    後者の方がが短く書けますね....。

  • 割り切れるかどうかの条件式について
    自分:(year / month / day) % (year // month // day) == 0
    記事:Y % M == 0 and (Y / M) % D == 0
    後者の方が綺麗ですね(^_^;)

~おまけ~
求めた日付を出力する際、formatで書式を指定してみました。
こんな感じ。-> "{}/{:02}/{:02}".format(year, month, day)
参考サイト:Python, formatで書式変換(0埋め、指数表記、16進数など) | note.nkmk.me




いよいよここまで来たか、といった感じです。
夏休みに入ってわりと時間が作れるため、ペースが上がっています(卒論と同時進行w)。

魔法の箱の問題を解いて、
map()とかリスト内包表記とか使いまくってコードを短くできたのは良いのですが、
基本に沿った書き方もできないと、処理の流れを理解しづらいのでは、と思いました。
速く動くコードの裏での処理内容もしっかり分かっていれば、
可読性とパフォーマンスのバランスをとりやすくなる気がしました。

...なんてそれっぽいことを語ってしまいましたが、Qiita記事も残るは1つ!
詰まっても、時間を掛けて考えたり調べたりして、乗り越えることができました。
最後のQiita記事も、粘り強く取り組みたいと思います!