Skip to content

Conversation

hroc135
Copy link
Owner

@hroc135 hroc135 commented Feb 5, 2025

// left and right are half open index such that [left, right)
left := 0
right := len(nums)
for {

Choose a reason for hiding this comment

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

このforはGO特有の書き方なのですね。
個人的には他の言語のwhileに合わせて条件を for left < right {...} として最後にreturn leftをしたくなります。
これはGO言語では一般的に使われるものでしょうか?(GOのことあまり知らずすみません)🙇‍♂️

気になった理由としては、44行目にのif文の条件を必ず満たすのか直感的にわかりづらかったためです。
調べたところ大丈夫そうですがコンパイラがエラーを吐かないのかなと思いました。

Copy link
Owner Author

Choose a reason for hiding this comment

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

おっしゃる通り自分も後でコードを見返した時に同じことを思ったのでstep3で「条件を for left < right {...} として最後にreturn left」するコードに変えました。Go言語の一般的な書き方かどうかというよりも、単に自分がわかりにくい書き方をしていました

無限ループをコンパイラがどう扱うのかについて自分も気になって色々試してみたところ、不思議な現象が起きました。まず、step1のコードのように無限ループを回してある条件を満たすとreturnするという場合にコンパイルエラーは生じません。さらに、下記コードでもコンパイルエラーは生じませんでした。int型を返す関数なのにreturn文がないよ、というエラーが出ることを期待したのですが、正常にコンパイルできてしまいました。"golang how compiler treats infinite loop with no return"などと調べてみても情報は見つからなかったです、、

func noReturnLoop() int {
	i := 0
	for {
		i++
	}
}

Choose a reason for hiding this comment

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

@hroc135
説明いただきありがとうございました。step2以降は直感的にもわかりやすいと感じました。
自分の調べた限りでもGOだとコンパイラ可能ということしか見つけられませんでした。。。

@Ryotaro25
Copy link

Binary searchで躓いていて重点的に学習中なのですが各ステップのコメントすごく勉強になりました!

nums[middle] == targetの時にすぐ答えを返すということをしなかったが、
今回のようにstrictly ascendingならそうした方が直感的だと思った
- 停止性についての確認の仕方
- https://github.com/seal-azarashi/leetcode/pull/38#discussion_r1845409634
Copy link

Choose a reason for hiding this comment

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

そうですね。
ループごとに担当者がいると考えて、最終的な結果にミスがあったときに、どのループのときの責任なのか、というような考え方が分かりやすいでしょうか。
https://docs.google.com/document/d/11HV35ADPo9QxJOpJQ24FcZvtvioli770WWdZZDaLOfg/edit?tab=t.0#heading=h.c15qprmvxkc2

```Go
func searchInsert(nums []int, target int) int {
left := 0
right := len(nums) - 1

Choose a reason for hiding this comment

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

答えが存在しうる範囲は[0, len(nums)]なので、最初にright=len(nums)-1としてバグが発生しないことを理解するのに時間がかかった

if nums[middle] == target {
return middle
}
if nums[middle] < target {
Copy link

Choose a reason for hiding this comment

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

この部分は左と右で対称の処理となるため、 continue せず if else で書いたほうが分かりやすくなるかもしれません。

- 初期状態として、配列の全ての要素が[left, right]にちょうど含まれている必要があるので、
left=0, right=len(nums)-1
5. 用いた区間の種類に対し、適切なループ不変条件を、理由を理解したうえで、設定できるか?
- ループ終了時に、false,false],[true,trueとなって欲しい([はleft, ]はright)ので終了条件はleft > right
Copy link

Choose a reason for hiding this comment

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

閉区間を使うのであれば、ループ終了時に false,false,[true],true と、一番左の true だけが区間の中に含まれていて欲しいと思いました。

Copy link
Owner Author

Choose a reason for hiding this comment

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

ご指摘ありがとうございます。配列が false,false,...,false の場合に困ると思って left と right が最終的に交差するように問題を設定したのですが、考え直してみて末尾にダミーの true を置いたら解決すると思い書き直してみました

  1. bool値からなる長さlen(nums)+1の配列を用意し、false: target未満、true: target以上又は末尾要素として false,...,false,true,...,true の一番左のtrueの位置を求める問題と捉える。末尾要素をいつでもtrueに設定することにより、全部falseからなる配列にならないようにする
  2. 位置を求めるにあたり答えの含まれる範囲を狭めていく
  3. 閉区間を用いる
  4. 初期値を[left, right] = [0, len(nums)] とする
  5. 終了条件は left == right なので不変条件は left < right
  6. middleを切り捨てでとる。nums[middle] < target なら left を middle+1 に更新。nums[middle] >= target なら right を middle に更新。middleは切り捨てなので left <= middle < right が成り立って毎ループ left -> middle+1 か right -> middle のどちらかが起こるので区間は少なくとも1狭まっていくので停止する
func searchInsert(nums []int, target int) int {
	left := 0
	right := len(nums)
	for left < right {
		middle := left + (right-left)/2
		if nums[middle] < target {
			left = middle + 1
		} else {
			right = middle
		}
	}
	return left
}

Copy link

Choose a reason for hiding this comment

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

ありがとうございます。

ただ、ちゃぶ台返しになって大変申し訳ないのですが、この問題については、境界を求める問題として捉えると、求める位置が一番右側の要素の一つ右の場合でも、ストレートフォワードに答えが求まると思います。

Copy link
Owner Author

Choose a reason for hiding this comment

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

「境界を求める」と「一番左のtrueを求める」は同じ意味だと思っていたのですが違っていたのですね。

結局一番左のtrueを求める問題を解こうと思ったら閉区間を使っているつもりでも開区間を使った時のコードと同じになりました。

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.

5 participants