Conversation
| } | ||
| ``` | ||
|
|
||
| (💭 夜中にダラダラやってたのでかかった時間は15分を全然超えてますが、意外と出来るものですね。ちゃんと考えを整理しながら解法を段階的にアップデートしていって理解が深まったので、今後も長い間覚えていられそうです。) |
| ### 数学的の組み合わせの問題として計算 | ||
|
|
||
| [組み合わせの公式](https://manabitimes.jp/math/1352#4)を使って解く。 Constraints の範囲内でも最大で 198! を扱うことになり、その結果は int の最大値 2^31 - 1 を簡単に超えてしまう (int が扱えるのは 12! まで)。そのため java.math パッケージの BigInteger を使う。 | ||
| (💭 [Math クラス](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Math.html) や [math パッケージ](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/math/package-summary.html) を眺めてみましたが、意外と factorial や combination 算出用の関数はないんですね) |
There was a problem hiding this comment.
There was a problem hiding this comment.
ありがとうございます。Java はこれとか Google Guava あたりと一緒に使うイメージ強いですね。
まあ import すればいいので困ることはないですが、他の言語だとサポートしてる関数が標準ライブラリに無いとちょっと不便に感じます。
| ``` | ||
|
|
||
| 寝る前の暇な時間にコードを眺めていたら、[m - 2][n - 2] のマスが常に2になることから、右端の列と底の行は全部1にしておき、それらを走査対象から外せばイテレーション内の条件分岐を無くせることに気づいた。 | ||
| ゴールのマスは基本的に正しくは 0 なのだけど、スタート地点の値 (unique path の数) の算出には基本的に影響しないので、見やすさのためにこのような実装にした。 |
There was a problem hiding this comment.
ゴールのマスは基本的に正しくは 0 なのだけど
これはなぜなのでしょうか?あとそもそも何を計算しているのかもお聞きしたいです。
There was a problem hiding this comment.
@fhiyo
返信が大変遅くなりました。
pathCountCache の各要素にはゴールに到達するまでに必要なステップ数を入れています。ゴールのマスからゴールのマスにたどり着くのに必要なステップ数は0なので、上記のようにコメントしました。
(常に0であることが正しいので「基本的に」は余計ですね)
| this.m = m; | ||
| this.n = n; |
There was a problem hiding this comment.
n, mをメンバ変数にしてるのはどうしてなんでしょうか?
あと、変更されない変数にはfinal? をつけてimmutableにしたほうが良いかなと思いました
There was a problem hiding this comment.
@Yoshiki-Iwasa
返信が大変遅くなりました。
n, mをメンバ変数にしてるのはどうしてなんでしょうか?
引数として渡すと記述が冗長になると感じてこのようにしていました。しかし人によっては違和感ありますよね。
あと、変更されない変数にはfinal? をつけてimmutableにしたほうが良いかなと思いました
他の言語だとそうではないと思うのですが、Java だと慣習的に、クラスメンバー以外にはあまり final をつけない (定数であることを明示しない) ことが多いと感じるのでこのようにしています。そういえば前別のプルリクエストで同じ話をしてました: #27 (comment)
| } | ||
|
|
||
| private void findUniquePathRecursively(int x, int y) { | ||
| if (x == n - 1 && y == m - 1) { |
There was a problem hiding this comment.
@Yoshiki-Iwasa
返信が大変遅くなりました。
書き漏らしていました。一貫性が保てておらずよろしくないので修正しておきました。
| class Solution { | ||
| public int uniquePaths(int m, int n) { | ||
| if (m <= 0 || n <= 0) { | ||
| return 0; |
There was a problem hiding this comment.
マイナスの処理を考えるの、勉強になりました。
個人的にはマイナスが入力されたらエラーを返すと思います。
例えばユーザーがうっかりマイナスを入れちゃったとなったとき(3, 5と入力しようとしてうっかり-3, 5としたときなど)、0が返ってくるとユーザー側が困惑しそうな気もします。
意図的にマイナスの場合を調べたいときってあまりないかなと…
There was a problem hiding this comment.
@nittoco
返信が大変遅くなりました。
ご指摘ありがとうございます。自分も最近、引数が不正だった場合は所定の値を返すのでなく、エラーを返すなりした方が良いのではないかと考えていたところでした。
こちらでした修正のように、今後は所定の exception を throw しようと考えています: #30 (comment)
| int[][] pathCountCache = new int[m][n]; | ||
| for (int y = m - 1; y >= 0; y--) { | ||
| for (int x = n - 1; x >= 0; x--) { | ||
| boolean inXBorder = x + 1 == n, inYBorder = y + 1 == m; |
There was a problem hiding this comment.
こういう書き方ってJavaだとよくあるんでしょうか
前CSライザップで小西さんが言ってたように、=と==が混乱しそうな気がします
There was a problem hiding this comment.
@nittoco
返信が大変遅くなりました。
はい、宣言するいくつかの変数の型が同じ場合、このように一行でまとめて宣言するのは普通の書き方だと思います。しかし、仰るとおり = と == が混在する代入文が同じ行に並んでいるとちょっと煩雑な印象がありますね。
例えば以下のように書いたら見やすくなるでしょうか?
boolean inXBorder = x + 1 == n;
boolean inYBorder = y + 1 == m;
もし = と == が混在するのが読みづらい原因であれば、nittoco さん的に (言語はなんでもいいので) どのような書き方が好ましいのか参考までに聞かせてもらえると嬉しいです。Java だと、比較演算の結果を変数に代入するのであればこの書き方は多分避けられないはず...?
|
|
||
| private void findUniquePathRecursively(int x, int y) { | ||
| if (x == 0 && y == 0) { | ||
| this.uniquePathCount++; |
There was a problem hiding this comment.
自分はここではインクレメント処理せず、初期値を1にすると思います。(下ではそうしてますが)
| } | ||
|
|
||
| private BigInteger combination(int m, int n) { | ||
| return factorial(m).divide(factorial(n).multiply(factorial(m - n))); |
There was a problem hiding this comment.
微妙なラインですが、一気に処理されるとちょっと見にくいので分母と分子を別変数にしてもいいかなという気もしました。
https://leetcode.com/problems/unique-paths/description/