python; checkio日記

checkioを中心にpythonプログラミングの記録

Electric Station問題3 Find Sequence

正方形のMatrixの中で4つ以上連続するものがあればTrue、なければFalseを返す関数を作ります。
行内か右下がりの方向で探して、次にMatrixを90度回転させてもう一回探すという方向性でプログラムを書いてみました。

def checkio1(test):
    for i in range(len(test)):    
        for j in range(len(test)):
            if j <= len(test)-4:
                if test[i][j] == test[i][j+1] == test[i][j+2] == test[i][j+3]:
                    return True
            if i <= len(test)-4 and j <= len(test)-4:
                if test[i][j] == test[i+1][j+1] == test[i+2][j+2] == test[i+3][j+3]:
                    return True
    else:
        return False

def checkio(test):
    return checkio1(test) or checkio1(list(map(list, zip(*test)))[::-1])

結構すっきりいった気がするが、最も人気の高かったものは、takapt0226さんのコードでrecursionの使い方がとても上手*1。こういう風に使えるようになりたい。

def checkio(matrix):
    N = len(matrix)
    def seq_len(x, y, dx, dy, num):
        if 0 <= x < N and 0 <= y < N and matrix[y][x] == num:
            return 1 + seq_len(x + dx, y + dy, dx, dy, num)
        else:
            return 0
    DIR = [(0,1), (1,-1), (1,0), (1,1)]
    for y in range(N):
        for x in range(N-3):
            for dx, dy in DIR:
                if seq_len(x, y, dx, dy, matrix[y][x]) >= 4:
                    return True
    return False

*1:好みで微変更してます

Electric Station問題2 Hamming Distance

2進法での距離を求める問題。Hamming Distanceというらしい。 106 (219< 106 < 220) までを考慮したので良いということだったので、

def hamming_d(numb):    
    ans = []
    for i in reversed(range(0, 21)):
        if numb / 2**i >=1:
            ans.append(1)
            numb = numb - 2**i
        else:
            ans.append(0)
    return ans

def checkio(n,m):
    return sum([abs(x-y) for (x, y) in zip(hamming_d(n), hamming_d(m))])

今回は簡単だったと思ったが、いろいろ勉強になるやり方あり。 まず1つめ
returnで関数自体のループをするというやり方が個人的に新鮮
%があまりで//が商。同時に2で割っていく。

def checkio(a, b, distance=0):
    distance += a % 2 != b % 2
    a, b = a // 2, b // 2
    if a == b == 0:
        return distance
    return checkio(a, b, distance)

2つめ
bin(117) だと'0b1110101'というような値を返すbin関数を使用する。
そこまでは良いが、更に'^'
これはBitwise Exclusive Orといって排他的論理和を返す。

checkio = lambda n, m: bin(n ^ m).count("1")

驚愕!!
色々あるOperatorはこのページが参考になる。

Home問題5 チェス

守られているポーンの数を数えるという課題。
説明しにくいので、適当に とりあえず、特定のポーンを守れる場所に別のポーンがあるかどうかを判別
ある場合だけsafe ポーンとして数えるというアルゴリズム

def safe_pawns(pawns):
    safe = 0
    for i1 in pawns:
        ok = [1 for i2 in pawns if i2 ==chr(ord(i1[0])-1) + chr(ord(i1[1])-1) \
                                or i2 ==chr(ord(i1[0])+1) + chr(ord(i1[1])-1)]
        if len(ok)>0:
            safe += 1
    return safe

assert safe_pawns({"b4", "d4", "f4", "c3", "e3", "g5", "d2"}) == 6
assert safe_pawns({"b4", "c4", "d4", "e4", "f4", "g4", "e5"}) == 1

一番人気は、if文をもっと効率的に使っていました。シンプルに考えれば良かったですね。

    safe = 0
    for i1 in pawns:
        if chr(ord(i1[0])-1) + chr(ord(i1[1])-1) in pawns \
        or chr(ord(i1[0])+1) + chr(ord(i1[1])-1) in pawns:
            safe += 1
    return safe

Home 問題 Long Repeat

'aaabbbccssafa' -> 3
アルファベットがもっとも長く連続する長さを返す関数を作る。
直線的にやってみます。

def long_repeat(line):
    if line=='':
        return(0)
    else:       
        count=1
        count_chr=[1]
        for i in range(1,len(line)):
            if line[i-1]==line[i]:
                count +=1
            else:
                count = 1
            count_chr.append(count)
        return max(count_chr)

assertのチェックには問題ないが、最初の2行を入れないとステージはクリアできないです。書いといてほしい。。。。 連続する4ステージの一つ目らしく、brute forceより効率的にしよう!みたいなコメントが入っているので、たぶんもっと工夫したコードをしなければならないのでしょうが、とりあえず今回はこれで。

Home 問題4 シェイクスピア

テキストの中にwords(辞書)のうち何個含まれているか。

count_words("How aresjfhdskfhskd you?", {"how", "are", "you", "hello"}) == 3

re.searchを使って、有った場合だけ数える。とても直線的

import re
def count_words(text, words):
    x = 0
    for i in words:
        match = re.search(i, text, flags=re.IGNORECASE)
        if match:
            x += 1
    return x

もうちょっと短くしてみた。マッチする奴だけリストにして数える

def count_words(text, words):
    ok = [i for i in words if re.search(i, text, flags=re.IGNORECASE)]
    return len(ok)

このroopとif文は()の中でも使えるみたい。

return sum(1 for i in words if re.search(i, text, flags=re.IGNORECASE))

で、一番投票の多い答えは

sum(i in text.lower() for i in words)

前半がTrueの数を数えるやり方ですね。

Home 問題5: ●×ゲームの判定

今回は●Xゲームの勝敗決め データの与えられ方は
data = ([
"O.O",
"XX.",
"XOX"])

で勝敗を決定する。 行列を使ってみた。

def checkio(data):    
    mapping = {'X': '1', 'O':'-1', '.':'0'}
    text = [int(i.translate(str.maketrans(mapping))) for i in ''.join(data)]
    testmat = np.array(text).reshape(3,3)
    testrow = testmat.dot(np.ones(3))
    testcol = np.ones(3).dot(testmat)
    testdiag = np.trace(testmat)
    testinvdiag = np.trace(np.flip(testmat, axis=0))
    test = np.concatenate((testrow, testcol, [testdiag], [testinvdiag]), axis = 0)
    if np.sum(test==3) >= 1:
        return 'X'
    elif np.sum(test==-3)>= 1:
        return 'O'
    else:
        return 'D'

上記のtestで、8個のラインをそれぞれ評価する。この流れは皆かわらないが、文字列のままでやっている人がほとんどで、
return 'X' if 'XXX' in test else...
とやっている人が多かったです。
更に、zip関数を上手に使うと

    rows = data
    cols = list(map(''.join, zip(*rows)))
    diag = [(r[i], r[2-i]) for (i, r) in enumerate(rows)]
    diag = list(map(''.join, zip(*diag)))
    test = rows + cols + diag

でtestまでシンプルにいけるということを学びました。この調子でcheckioをしながら、基本的な関数の使い方を少しづつ学びます。

Home 問題2

問題: 数字の入ったリストから、重複がないもののみを消す。
[1,2,3,4,5,6,4,2,5] => [2,4,5,4,2,5] #1,3,6が消去

from collections import Counter
import numpy as np
def checkio(data):
    count = Counter(data)
    [x for (x, y) in count.items() if y == 1]
    erase = [x for (x, y) in count.items() if y == 1]
    return [i for i in data if sum(np.array(erase)==i)==0]

まず、一つしかない数字をリストにして、それをarrayにしてから、ループでそのリストに含まれないものをり出すという方法。
これは冗長で、下記のようにすっきりかけた

def checkio(data):
    count = Counter(data)
    return [i for i in data if count[i] >1]

更に、list.count(x)という関数を使うと、リストの中身にxが何個あるかを計測してくれるようなので、

def checkio(data):
    return [i for i in data if data.count(i)>1]

ともっと短くかけるのでした。