Skip to content

121. best time to buy and sell stock#4

Open
arahi10 wants to merge 5 commits intomainfrom
121._Best_Time_to_Buy_and_Sell_Stock
Open

121. best time to buy and sell stock#4
arahi10 wants to merge 5 commits intomainfrom
121._Best_Time_to_Buy_and_Sell_Stock

Conversation

@arahi10
Copy link
Copy Markdown
Owner

@arahi10 arahi10 commented Apr 11, 2026

問題

https://leetcode.com/problems/best-time-to-buy-and-sell-stock/description/

成果物所在

121._Best_Time_to_Buy_and_Sell_Stock/memo.mdがステップ1~3までをまとめたmdファイルです.

次に取り組む問題のリンク

https://leetcode.com/problems/permutations/description/

```python
class Solution:
def maxProfit(self, prices: List[int]) -> int:
acc_min = float("inf")
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

こちらのコメントをご参照ください。
hemispherium/LeetCode_Arai60#10 (comment)

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

気を抜くとやってしまいます,気を付けます.

diffs = [prices[i + 1] - prices[i] for i in range(len(prices) - 1)]
return self.__max_profit(diffs, 0, len(diffs))

def __max_profit(self, diffs: List[int], begin: int, end: int) -> int:
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

アンダースコア 2 つから始まるメソッドは mangling の対象となり、内部用であるという意図を強く感じさせるものの、完全に private というわけではありません。通常はアンダースコア 1 つで十分だと思います。

参考までにスタイルガイドへのリンクを共有いたします。

https://peps.python.org/pep-0008/#method-names-and-instance-variables

To avoid name clashes with subclasses, use two leading underscores to invoke Python’s name mangling rules.

Python mangles these names with the class name: if class Foo has an attribute named __a, it cannot be accessed by Foo.__a. (An insistent user could still gain access by calling Foo._Foo__a.) Generally, double leading underscores should be used only to avoid name conflicts with attributes in classes designed to be subclassed.

https://google.github.io/styleguide/pyguide.html#3162-naming-conventions

Prepending a double underscore (__ aka “dunder”) to an instance variable or method effectively makes the variable or method private to its class (using name mangling); we discourage its use as it impacts readability and testability, and isn’t really private. Prefer a single underscore.

なお、このスタイルガイドは“唯一の正解”というわけではなく、数あるガイドラインの一つに過ぎません。チームによって重視される書き方や慣習も異なります。そのため、ご自身の中に基準を持ちつつも、最終的にはチームの一般的な書き方に合わせることをお勧めします。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

コメントと参考の提示ありがとうございます.
技術的な部分は理解しました.そういわれるとダンダーを使うことのメリットがそれらを上回るとは言えないというところで納得したので,今後はアンダースコア1つを通常の選択として運用したいと思います.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

細かい点で恐縮ですが、ダンダーはメソッド名の前後にアンダースコアが2つ付いているメソッドを指すようです。

https://docs.python.org/ja/3.14/library/dataclasses.html#module-contents

訳注:dunderはdouble underscoreの略で、メソッド名の前後にアンダースコアが2つ付いているメソッド

Copy link
Copy Markdown
Owner Author

@arahi10 arahi10 Apr 15, 2026

Choose a reason for hiding this comment

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

補足ありがとうございます。自分も気になっていたので検索していたところでした。

<summary> 分割統治による $O(N \log N)$ 時間アルゴリズム </summary>

- [アルゴリズムイントロダクション](https://www.amazon.co.jp/%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0%E3%82%A4%E3%83%B3%E3%83%88%E3%83%AD%E3%83%80%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3-%E7%AC%AC3%E7%89%88-%E7%B7%8F%E5%90%88%E7%89%88-%E4%B8%96%E7%95%8C%E6%A8%99%E6%BA%96MIT%E6%95%99%E7%A7%91%E6%9B%B8-%E3%82%B3%E3%83%AB%E3%83%A1%E3%83%B3/dp/476490408X), CLRS, の分割統治の章で見たことある.
- 階差を作って部分配列の和の最大化問題に言い換え→"真ん中"をまたぐ部分配列なら線形で最適解を計算出来て,またがない奴は部分問題の解になるから,(またぐやつ, またがない左右の部分問題) の3つに分割して統治する,という方針だったはず.
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

この解法、初めて見たかもしれません。面白い解法ですね。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

私も初めてみました。
この問題を見て最大部分配列問題として捉えるのは視点の幅がすごいなと思いました。

\end{align*}
$$ where $P_{j}:= \underset{0\le i < j } {\text{maximize}} \sum_{k=i}^{j-1}\text{diffs}[k]$.
- $P_{1} = \text{diffs}[0]$ かつ $j>1$に対して$$
\begin{align*}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

TeX表記がGitHubで正しく表示されていないみたいです。レビューするときも、ソースファイルの方を見るので、レビュワーにとっては好ましくなさそうです。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

ご指摘ありがとうございます.取り急ぎ,数式が正しく表示されるように修正しました.

また,今後数式を書くことがあれば,ソースファイルのほうで十分理解できる表記を用いようと思います.

def maxProfit(self, prices: List[int]) -> int:
acc_mins = itertools.accumulate(prices, min)
max_profit = 0
for buying, selling in zip(acc_mins, prices):
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

計算上問題は発生しないですが、当日までの最低価格ではなく、前日までの最低価格を使った方が直感的かと思いました。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

確かにそちらの方が直感的な説明と対応がとりやすいですね.
その方針でも改めて書いてみようと思います.

fix equations
<summary> 分割統治による $O(N \log N)$ 時間アルゴリズム </summary>

- [アルゴリズムイントロダクション](https://www.amazon.co.jp/%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0%E3%82%A4%E3%83%B3%E3%83%88%E3%83%AD%E3%83%80%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3-%E7%AC%AC3%E7%89%88-%E7%B7%8F%E5%90%88%E7%89%88-%E4%B8%96%E7%95%8C%E6%A8%99%E6%BA%96MIT%E6%95%99%E7%A7%91%E6%9B%B8-%E3%82%B3%E3%83%AB%E3%83%A1%E3%83%B3/dp/476490408X), CLRS, の分割統治の章で見たことある.
- 階差を作って部分配列の和の最大化問題に言い換え→"真ん中"をまたぐ部分配列なら線形で最適解を計算出来て,またがない奴は部分問題の解になるから,(またぐやつ, またがない左右の部分問題) の3つに分割して統治する,という方針だったはず.
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

私も初めてみました。
この問題を見て最大部分配列問題として捉えるのは視点の幅がすごいなと思いました。

Comment on lines +70 to +71
if begin == end:
return 0
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

begin == endのケースってあるんだっけ?と思ったら、株価データが一つしかないときのベースケースなんですね。
この下でend - begin == 1のベースケースがあるので、そこにたどり着くことなくbegin == endになるデータタセットがあるはずと考えるとすごく納得いきました。

Comment on lines +131 to +132
sell_today = diffs[0]
max_prof = sell_today
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

sell_todayが動詞っぽく感じられて、またprofと対応していない感じがしたので、対応関係があるとわかりやすいなと思いました。
local_max_prof, global_max_profなど

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

仰る通りだと思います.動詞から始まると関数名のようにも受け取れる点からも,変数名に改善の余地がありましたね.
「profと対応していない」のようにほかの変数との兼ね合いという観点も大事だと思います.今後意識してみます.ありがとうございます.


```

- `prices` が空配列のときと`len(prices) == 1`のときの扱いはステップ1のコードと同じように0を返す扱いをすることにした
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

空配列は問題上想定されていないですが、想定されていない入力値にはエラーで返したいという気持ちが個人的にはあります。
状況によってどちらかがいいかは変わると思います。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

仰る通り,どちらかに明確な(状況に左右されない)優劣が存在しないと思いますので,状況により適切な方を選べるようになっていることが大事なのかなと考えています.

def maxProfit(self, prices: List[int]) -> int:
if len(prices) < 2:
return 0
price_changes = [
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

changesのほうが時系列のニュアンスがあるみたいですね。
私はdiffsしか思いつかなかったのでとても良い表現だと思いました。

Comment on lines +192 to +193
- `maxProfit` の `if begin == end` → `if begin >= end: ~` に変更し, (`maxProfit`側でチェック済みだけれども)残しておく
- カバレッジは減ってしまう(この点では`assert begin < end` のほうが良い)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

こちらも個人的には入力として想定していないものはエラーにしたい気持ちがありassert推しです。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

__find_center_crossing_max_profit の方はfloat("-inf")を返しているので、privateメソッドではfloat("-inf")を返してpublicのmax_profitのところでどう返すか調整するでも良さそうと思いました。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

こちらも個人的には入力として想定していないものはエラーにしたい気持ちがありassert推しです。

__max_profit はprivateなメソッドなので,よりassertで済ましてしまいやすいかとは思います(制約を破るのはユーザではなくprivateなメソッドを呼び出す実装側の人=同僚や未来の自分なので).一方,仕様追加・修正の必要に迫られたときにこの__max_profit をより広いユースケースでもなるべく使えるように実装しておくことも,それはそれでありだとは思います.こちらについても,両方見えてくのがベターなのでしょうね.

__find_center_crossing_max_profit の方はfloat("-inf")を返しているので、privateメソッドではfloat("-inf")を返してpublicのmax_profitのところでどう返すか調整するでも良さそうと思いました。

こちらの方針とも迷ったのですが,「最大利益は0以上の値を返す」という知識が max_profitにも__max_profitにも埋め込まれてしまうのを嫌って,このように意思決定したと記憶しています.

<details>
<summary> 名前がついてる線形時間アルゴリズム</summary>

- なんか線形で解くやつに名前が付いていて,最大化問題を適宜書き換えると帰着できたはず
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

これだけ丁寧に数式に落とされていてすごいと思いました。
私が数学苦手なのもあり、読み解くのに時間がかかりましたが大変勉強になりましたmm

]
return self.__max_profit(price_changes, 0, len(price_changes))

def __max_profit(self, price_changes: List[int], begin: int, end: int) -> int:
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

同じmax_profitの名前でpublicなものをinterface, privateなものを実際の計算アルゴリズムとして分けた実装がとてもいいなと思いました。
pythonだとよくこういう書き方をするのでしょうね。普段使っていないので勉強になりました。

def maxProfit(self, prices: List[int]) -> int:
if len(prices) < 2:
return 0
today_max_profit = 0
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

"その日"というニュアンスでtoday("今日")が使われているのが少し違和感持ちました。(non-nativeです;)
today→current yesterday→previousのほうが時間軸が固定されずに良い気がしました。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

たしかに,どちらかというとループ中における"This day" であってTodayではないですね.ちょっとLLMとも相談しながら英語のニュアンスとして適切なものを探して,ステップ4として後日作成しようと思います.ありがとうございます.

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.

4 participants