Skip to content

Conversation

@TORUS0818
Copy link
Owner

Copy link

@hayashi-ay hayashi-ay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

良いと思います。行列の解法は見てないです

return num_ways_helper(n, k)
```
思考ログ:
- cacheを配列で取った版

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nとkのtupleとかをキーにしてdictで管理するのが良いのかなと思います。

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

と思いましたが、そもそもkの値は固定なのでnについてだけで良いですね。

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ありがとうございます。
あ、これかなり無駄なことしてますね。。ボケてました。

@Yoshiki-Iwasa
Copy link

Yoshiki-Iwasa commented Aug 29, 2024

良さそうです

強いて言えば、num_ways_so_farの変数名は工夫の余地ありかなと
num_ways_so_farは一度値が決まったら更新されないので、so_farいるかな?という違和感です

@TORUS0818
Copy link
Owner Author

ありがとうございます。
確かに更新されなかったですね。

ポストを一個ずつ塗っていって、ここまではN通り、次、みたいなイメージだったのでso_farを付けたんだと思います。

- https://ja.wikipedia.org/wiki/%E3%83%95%E3%82%A3%E3%83%9C%E3%83%8A%E3%83%83%E3%83%81%E6%95%B0
- 行列表現の結果をもとに漸化式を作る
- q = n//2として、q-1, q, q+1番目のフィボナッチ数を用いてn番目のフィボナッチ数を表すことができる(lognに落とせる)
- 漸化式に2nと2n-1を代入して確かめれば、そうなってるなあとなるが、、結構テクい式変形に感じた
Copy link

@sasanquaneuf sasanquaneuf Aug 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

この式を見ると若干テクい感じに見えますが、計算量のオーダー的には以下のような事と同じです。
(再帰を上から展開していくか、桁の下の方からボトムアップに計算していくかみたいな違いです。)

import numpy as np

def fib(n):
    result = np.array([[1 , 0], [0, 1]])
    fib_array = np.array([[1 , 1], [1, 0]])
    val = 1
    while val <= n:
        val *= 2  # ビットシフト
        if n % val:  # log(val, 2)桁目のbitが立っているとき
            n -= n % val
            result = result.dot(fib_array)
        fib_array = fib_array.dot(fib_array)
    return result[0][1]

※この実装はnumpyの数値行列なのでオーバーフローがあり、実用上の価値はあまりないかもしれません。あくまでも概念的な説明でした。

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

実装例もありがとうございます。

行列のn乗で計算できることから、律儀に行列をn回掛けるのでなく、(n=2^kとしてざっくり)k=logn個の道中のk乗行列を計算しておけばいいよね、ということだと理解しました。

ちょっと頂いたコードの流れがよく追えてないので少し考えてみます。。(n -= n % valあたり)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

これ変なコードですみません、ビットシフトの知識を前提にしない方がわかりやすいかもと思って、余計意味不明になってしまいました。。。
-=しているのは、チェックした部分までに立っているビットをすべてクリアする意図でした。
要は、fib_array ** nを、2進展開の要領でn = 2^{k_1} + 2^{k_2} + ... + 2^{k_m} みたいに分解して、積を取っています。
例えばn=10のとき、n=2^3+2^1ですが、(fib_array ** 8) * (fib_array ** 2)とするとき、このカッコの中身たちは自乗で効率的に計算ができるということでした。
このn乗を効率的に計算する方法は、フィボナッチ数列に限らず任意の行列で使えます。
(ただ、巨大数の積や大きな行列の演算は高コストなので、任意の行列などでやると、一般にはlog(n)よりも大きなオーダーになりがちだと思います)

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-=しているのは、チェックした部分までに立っているビットをすべてクリアする意図でした。
ありがとうございます。理解しました。

大きな行列の演算は高コスト
スパースな行列の場合はうまく工夫できたりと色々奥が深いですよね。

色々と詳細にありがとうございました。
またコメントよろしくお願いします!

- https://discord.com/channels/1084280443945353267/1251052599294296114/1272143005851058229
> あー、あと、この問題、行列の n 乗なので、そういう方法でも計算できるはずです。
- 漸化式から行列表記にして、n乗を計算する
- 普通に累乗するとコストが大きそうなので、対角化して累乗計算する
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

大きさ次第ですが、2次の正方行列の n 乗は、偶奇で場合分けして半分にしていけば、対角化しなくてもいいですね。
対角化、固有値計算、逆行列計算、次数が上がるとそれなりに重いです。

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

よくよく考えるとそうですね。。
ケーリーハミルトンの定理で頑張っていた頃を思い出しました。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants