メモやらログやら

考えたこととかメモする

LRU Cache

class ListNode(object):
    def __init__(self, key, val):
        self.key = key
        self.val = val
        self.prev = None
        self.next = None


class LRUCache:

    def __init__(self, capacity: int):
        self.head = ListNode(-1, -1) # 新しいほう
        self.tail = ListNode(-2,-2) # 古いほう
        self.cache = dict() # nodeがval
        self.head.next = self.tail
        self.tail.prev = self.head
        self.capacity = capacity
        self.length = 0
        
        
    # 先頭にnodeを追加
    def add(self, node):
        
        node.prev = self.head
        node.next = self.head.next
        self.head.next = node
        node.next.prev = node
        
    # 削除
    def remove(self, node):
        node.prev.next, node.next.prev = node.next, node.prev

    # 一回削除してから先頭に足す。増えないのでcapacityを超えることはない
    def get(self, key: int) -> int:
        if key not in self.cache:
            return -1
        
        node = self.cache[key]
        self.remove(node)
        self.add(node)
        return node.val
        

    def put(self, key: int, value: int) -> None:
        # 既にキャッシュ内に存在するならgetとほぼ同じ
        if key in self.cache:
            node = self.cache[key]
            node.val = value
            self.remove(node)
            self.add(node)
        # ないならcapacityを超える場合はtailに最も近いものを削除してから追加
        else:
            if len(self.cache) == self.capacity:
                tail_node = self.tail.prev
                del self.cache[tail_node.key]
                self.remove(tail_node)
            
            node = ListNode(key, value)
            self.cache[key] = node
            self.add(node)

collections.OrderedDictを使うと非常にスッキリする

class LRUCache:

    def __init__(self, capacity: int):
        self.capacity = capacity
        self.dic = collections.OrderedDict()
        
    
    def get(self, key):
        if key not in self.dic:
            return -1
        
        val = self.dic[key]
        self.dic.move_to_end(key) # 後ろのものほど新しい
        return val
    

    def put(self, key, value):
        self.dic[key] = value
        self.dic.move_to_end(key)
        if len(self.dic) > self.capacity:
            # last=Trueなら末尾(最も新しく利用したもの)を、Falseなら先頭のkey,valueを削除
            self.dic.popitem(last=False)


参考
medium.com

leetcode.com

Go言語によるWebアプリケーション開発 5章でつまったところ

作ったアプリが動かず、Twitter API から 406 が返ってきていたので、リクエスト不正なのでパラメータ作成周りのコードをずっと調べてたのだけど、問題はDBの方だった。

MongoDBに作ったテーブル名が、pollsのつもりだったのだけど、polssになっていて、レコードを引っ張れず、そこの結果レコードからパラメータを作れないみたいなことになっていた。

 

iter := db.DB("ballots").C("polls").Find(nil).Iter()

の形でDBから引くのだけど、テーブルがないからと言ってエラー落ちしたりしないため、レコードを引けたかどうか、バリデーションをちゃんとやるべきだった。

 

Go言語によるWebアプリケーション開発 1~3章をやった

目的

Go書きたい&ソケット通信でなんか作ってみたかった(現職では他社システムから叩かれるAPIをつくっていて、ソケット通信に関する知識がないため)。

 

Goでサーバ立ててチャットアプリ作って、Googleとかを利用した認証機能を作って、アイコン画像アップ機能追加して、リファクタリングして、みたいな流れ。

ソケット通信を利用したアプリの勘所が全くなかったので、とても興味深く面白かった。

普通のWebページとかと異なり、接続が瞬間的なものではなくいつ切れるかわからないため、負荷分散やスケーラブルなシステム設計がかなり難しそうだとおもった。youtubeとかのような動画配信はどんな感じで対応してるのか気になる。

「データ指向アプリケーションデザイン」のメモ

2章

宣言的コード:SQLクエリのような、「結果をこのような形でください」というような依頼。裏側で処理をどのように行っているかは気にしなくてよく、CPU利用などのその時に適した最適化なども期待できる。一方で、既存の命令セット(という表現はよいのか?)の外のことをするのが難しいので、柔軟性の面では命令的コードに劣るか。

命令的コード:通常のプログラミングのように、「この順でこのように処理をしてね」のような依頼により、所望の結果を得る。処理を自由に組み合わせられるので、柔軟性は高いが、その分CPU最適化などが難しい。また、宣言的コードのように、結果だけください、というわけにはいかず、結果に至るまでの処理を自身で構成する必要がある。

 

 どちらも一長一短あり、データモデルによりどちらが適してるかも変わりそう。また、どちらか一方のみしか使わない、というようなものでもなく、ミドルウェアによってはどちらも(というかこれらの中間的なもの)をつかえるか。

 

ドキュメント型DB:データが単体で完結していて、データ間に関係が薄いものに向く。

グラフデータベース:あらゆるもの同士に関係が存在するようなデータに向く。

RDB:データ間に関係性が強いもの

 

まずはデータから考えて、それらにどのような特性・関係性があるか見ていくことで、適した処理方法やミドルウェアが見えてくる。これらに絶対的な正解はなく(明らかに適さないものもあるだろうが)、たいてい何らかのトレードオフから重視したいものを選ぶ形か。

 

3章

06/21~06/27

唐突にやったこと記録を付け始める

 

今週やったこと

・カジュアル面談を受けた

・「応用技術者合格教本2020」を7割ぐらいまで読んだ

・「コンピュータシステムの理論と実装(nand2tetris)」をやり始めた

・「達人に学ぶDB設計徹底指南書」を半分ぐらい読んだ

 

今後やる/やりたいこと

・nand2tetris引き続きやる

・システム設計の本をぽちったので読む

・英語を聴く習慣を作りたい

・引き続き転職活動

・コードも書きたい

AtCoder Beginner Contest 129 E - Sum Equals Xor

問題ページ

atcoder.jp

 

愚直解
L以下の全a,bに対して、
a + b <= L
a xor b = a + b
になるか見ていって、条件にあうものを数え上げる。
計算量は O(N^2) で、最大でa,bそれぞれ 10^5 通りぐらいあるので、間に合わない

愚直解の良くない点として、例えば
L = 111100001111111、a = 11111~~, b = 00000~~
のような場合に、左から5文字目を調べた段階で、a xor b > L が決まり条件を満たさないことが確定するのに、6文字目以降も全パターン調べてしまう。
なので、左から徐々に幅を増やして(小さい問題から大きい問題へ)、条件を満たすものだけ拾っていく形に変更する。メモ化を利用した処理(DPなど)

また、xor は桁ごとに処理するので、桁DPが使えそう
xor には桁上がりがないので、 a, b で同じbitに1が立っている場合は、 a+b = a xor b が満たせない。

DP[i+1][j] : Lの左からi番目まで見たときに、条件を満たすa,bの組。j=1のとき、L未満である。

遷移
dp[i][1] -> dp[i+1][1] : a+b = a xor b となる組合わせならOK
dp[i][0] -> dp[i+1][1] : L[i] = 1 のときに、a xor b = 0 かつ a+b = a xor b になるようにとればよい
dp[i][1] -> dp[i+1][0] : L[i] = a xor b のi桁目、a xor b = 0 かつ a+b = a xor b になるようにとればよい

 

解答

atcoder.jp