IT1-CODE-POCKET

最大値を探す・値を探す処理の読み方

「いちばん大きい値は」「その値は何番目にあるか」——探す処理も出題の定番です。 集計と同じく箱を用意して更新していく形ですが、 更新の条件と「位置を覚える」という考え方が加わります。

BASIC

最大値は「今までの一番」と比べ続ける

最大値を求めるプログラムは、「今までの最大」を覚えておく箱を1つ用意し、 それより大きい値が出てきたときだけ入れ替える、という形です。

Nums = [4, 9, 2, 7]
mx = Nums[0]                 ← 最初の値を仮の最大とする

i を 0 から 要素数(Nums) - 1 まで 1 ずつ増やしながら繰り返す:
|もし Nums[i] > mx ならば:
⎿⎿ mx = Nums[i]

表示する(mx)
iNums[i]mx より大きい?mx
開始前4
04×(同じ)4
199
22×9
37×9

初期値の置き方に2通りある

書き方 利点 注意点
mx = Nums[0] どんな値でも必ず正しく動く
mx = 0 式が短い すべて負の数だと 0 が答えになってしまう

mx = 0 と書いてよいのは、データに0以上しか出てこないと分かっている場合だけです。 「この初期値でよいか」を問う出題があるので、根拠を言えるようにしておいてください。

最小値なら不等号を逆にするだけ

最小値を求めるときは Nums[i] < mn と向きを変えます。 不等号の向きだけで最大と最小が入れ替わるため、穴埋めで狙われます。

EXERCISE 1

例題1:更新条件を選ぶ

配列の最大値を mx に求めるには、[ ア ]に何を入れればよいですか。

Nums = [4, 9, 2, 7]
mx = Nums[0]

i を 0 から 要素数(Nums) - 1 まで 1 ずつ増やしながら繰り返す:
|もし [ ア ] ならば:
⎿⎿ mx = Nums[i]

表示する(mx)

選択肢:① Nums[i] > mx ② Nums[i] < mx ③ Nums[i] == mx ④ mx > Nums[i]

答えと解説を見る

答え:① Nums[i] > mx

更新したいのは「今までの最大 mx より、いま見ている値のほうが大きい」ときです。 そのまま日本語を式にすると Nums[i] > mx になります。

  • ② Nums[i] < mx … 小さいときに更新するので、結果は最小値の 2 になります
  • ③ Nums[i] == mx … 同じ値のときだけ更新するので、mx は 4 のまま変わりません
  • ④ mx > Nums[i] … ② と同じ意味です。左右を入れ替えると不等号の向きも逆になるため、 「> が使われているから最大値だ」と形だけで判断すると引っかかります

④ は特に間違えやすい選択肢です。「新しい値」が左辺にあるかどうかを必ず確認してください。 迷ったら、Nums[1] = 9mx = 4 を実際に当てはめて、 更新されるべき場面で条件が成り立つかを確かめるのが確実です。

EXERCISE 2

例題2:更新される回数を数える

次のプログラムにおいて、best = Nums[i] は何回実行されますか。

Nums = [6, 2, 8, 8, 3, 9]
best = 0

i を 0 から 要素数(Nums) - 1 まで 1 ずつ増やしながら繰り返す:
|もし Nums[i] > best ならば:
⎿⎿ best = Nums[i]

表示する(best)

選択肢:① 2回 ② 3回 ③ 4回 ④ 6回

答えと解説を見る

答え:② 3回

iNums[i]> best ?best更新
066 > 0 ○61回目
122 > 6 ×6
288 > 6 ○82回目
388 > 8 ×8
433 > 8 ×8
599 > 8 ○93回目

ポイントは i = 3 の「同じ8」です。 8 > 8 は成り立たないため、更新されません。 ここを数えてしまうと ③ の4回になります。

もし条件が Nums[i] >= best だったら、同じ値でも更新されるので4回になります。 最終的な best はどちらも 9 で変わりませんが、「回数」を聞かれると答えが変わります>>= の違いは、この形での定番の問われ方です。

POSITION

値ではなく「位置」を覚える

「最大値はいくつか」ではなく「それは何番目か」を聞かれることがあります。 その場合は、値ではなく添字を覚えます。

mx_pos = 0

i を 0 から 要素数(Nums) - 1 まで 1 ずつ増やしながら繰り返す:
|もし Nums[i] > Nums[mx_pos] ならば:
⎿⎿ mx_pos = i

比べる相手が mx ではなく Nums[mx_pos] になる点に注目してください。 「覚えているのは位置なので、値を見るにはもう一度配列を引く」——この形が 選択法(選択ソート)でもそのまま使われます。

見つからなかったことを表す -1

探索では、見つからなかった場合をどう表すかが問われます。 添字は 0 から始まるため、pos = 0 を初期値にすると 「先頭で見つかった」と区別がつきません。そこで添字としてあり得ない -1 を使います。

-1 はもうひとつの使い道があります。 「まだ記録していない」という印としても使えるため、 もし 条件 かつ pos == -1 ならば と書けば 最初に条件を満たした1回だけ記録できます。次の例題で確認します。

EXERCISE 3

例題3:最初の1回だけ記録する

合計が target 以上になった最初の添字だけを pos に入れるには、[ ア ]に何を入れればよいですか。

Point = [12, 8, 15, 6, 20]
target = 30
total = 0
pos = -1

i を 0 から 要素数(Point) - 1 まで 1 ずつ増やしながら繰り返す:
|total = total + Point[i]
|もし total >= target かつ [ ア ] ならば:
⎿⎿ pos = i

表示する(pos)

選択肢:① pos == -1 ② pos == i ③ pos >= 0 ④ i == 0

答えと解説を見る

答え:① pos == -1

total >= target だけだと、条件を満たすたびに pos = i が実行され、 最後に満たした位置で上書きされてしまいます。

iPoint[i]total30以上?pos
01212×-1
1820×-1
215352
36412 のまま
420612 のまま

i = 2 で pos = 2 が入ると、それ以降は pos == -1 が成り立たなくなるため、 上書きされずに残ります。これが「最初の1回だけ記録する」仕組みです。

  • ③ pos >= 0 … 逆の条件です。最初は -1 なので一度も記録されず、pos は -1 のままになります
  • ④ i == 0 … i が0のときだけなので、この例では一度も記録されません

この「-1 を見張り番に使う」テクニックは、 線形探索シミュレーション型の問題でも繰り返し出てきます。

BREAK

見つけたら止める(打ち切り)

探し物が見つかった時点で、それ以降を調べる必要はありません。 繰り返しを抜ける を使うと、そこで繰り返しが終わります。

Items = [4, 7, 3, 6, 5]
target = 18
total = 0
used = 0

i を 0 から 要素数(Items) - 1 まで 1 ずつ増やしながら繰り返す:
|total = total + Items[i]
|used = used + 1
|もし total >= target ならば:
⎿⎿ 繰り返しを抜ける

表示する(used)

合計が 4 → 11 → 14 → 20 と増え、i = 3 で18以上に達して抜けます。 used は 4 です。最後の 5 は一度も足されません。

書き方 結果 調べる回数
繰り返しを抜ける 最初に見つかった位置 見つかるまで(データ次第)
pos == -1 を条件に足す 最初に見つかった位置 最後まで(一定)
条件を足さない 最後に見つかった位置 最後まで(一定)

上の2つは答えが同じでも、調べる回数が違います。 「何回比較したか」を問われる問題では、この違いが直接得点に関わります。 動きを確かめたい場合は線形探索シミュレーターで 条件の書き方を切り替えて比べてみてください。

EXERCISE 4

例題4:見つかったかどうかを記録する

配列 Nums に target が含まれているかを found に入れるプログラム表記として、正しいものを選びなさい。

Nums = [3, 8, 1, 5]
target = 1

# found に True または False を入れる

選択肢:

① found = False
   i を 0 から 要素数(Nums) - 1 まで 1 ずつ増やしながら繰り返す:
   |もし Nums[i] == target ならば:
   ⎿⎿ found = True

② found = True
   i を 0 から 要素数(Nums) - 1 まで 1 ずつ増やしながら繰り返す:
   |もし Nums[i] == target ならば:
   ⎿⎿ found = False

③ found = target

④ found = Nums[0]
答えと解説を見る

答え:①

例題3では「位置」を記録しました。今回は「あったか、なかったか」だけを記録します。 この TrueFalse を入れる変数をフラグ(旗)と呼びます。

フラグの作り方は決まっています。「起きていない側」から始めて、起きたら1回だけ倒す——それだけです。

  1. 調べる前は False … まだ1つも見ていないので「見つかっていない」が正しい状態
  2. 見つかったら True … 1つでも一致すれば True にする
  3. 最後まで False なら「なかった」 … 一度も True にならなかったことが答えになる
iNums[i]target と同じ?found
開始前False
03×False
18×False
21True
35×True

i = 3 で条件が成り立たなくても、found は True のままである点に注目してください。 found = False に戻す処理はどこにもないので、 一度 True になったら最後まで Trueです。これがフラグの大事な性質です。

  • … True から始めて、見つかったら False にしています。意味が完全に逆で、 「見つからなかったら True」というおかしな結果になります
  • ③ found = target … found に 1 という数値が入るだけで、探していません
  • ④ found = Nums[0] … 先頭の 3 が入るだけです
これが線形探索そのものです。 線形探索シミュレーターで、 フラグが False から True に変わる瞬間を目で確かめられます。 なお実際の問題では、見つかった時点で 繰り返しを抜ける を足した形もよく出ます。 答えは同じで、調べる回数だけが減ります。

EXERCISE 5

例題5:用意された関数を使う

配列 Nums の中から最大値を表示するプログラム表記として、正しいものを選びなさい。

Nums = [3, 9, 1, 6]

# 最大値を表示する

選択肢:① 表示する(最大値(Nums)) ② 表示する(最小値(Nums)) ③ 表示する(要素数(Nums)) ④ 表示する(Nums[0])

答えと解説を見る

答え:① 表示する(最大値(Nums))

答えそのものは簡単ですが、ここで確認したいのは別のことです。 このページの冒頭で書いた「4行の繰り返し」と、この1行がまったく同じ処理だと分かるかどうかです。

自分で書く場合 関数を使う場合
mx = Nums[0]
繰り返し
もし Nums[i] > mx ならば
mx = Nums[i]
最大値(Nums)

共通テストでは両方の書き方が出ます最大値()最小値()要素数()合計() のような関数が使われているときは、 その1行を「繰り返し1つ分」に頭の中で展開して読むのがコツです。

  • ② 最小値(Nums) … 1 が表示されます。関数名の1文字違いなので、急いでいると読み飛ばします
  • ③ 要素数(Nums) … 4 が表示されます。「個数」であって「値」ではありません
  • ④ Nums[0] … 3 が表示されます。 これは「最大値を求める処理の1行目」だけを書いた状態です。 仮の最大を置いただけで、まだ比べていません
④ を選んでしまう人は、実は理解が進んでいます。 mx = Nums[0] という初期値の置き方を覚えているからこそ選んでしまう選択肢だからです。 足りないのは「そのあと全部と比べる」部分です。 「仮に置く」と「比べて更新する」はセットで1つの処理——ここまで言えれば、このテーマは十分に身についています。

CODE

3つの表記での書き方の違い

Nums = [4, 9, 2, 7]
mx = Nums[0]
mx_pos = 0

i を 0 から 要素数(Nums) - 1 まで 1 ずつ増やしながら繰り返す:
|もし Nums[i] > mx ならば:
||mx = Nums[i]
⎿⎿ mx_pos = i

表示する(mx, mx_pos)
まとめて処理する書き方もあります。 擬似言語には 最大値(Nums)、Python には max(nums)、 JavaScript には Math.max(...nums) という書き方があり、1行で最大値が求まります。 ただし「何番目か」は分かりません。位置が必要なときは、上のように自分で追う必要があります。

CHECK

このテーマのチェックリスト

探索が読めるようになったら、最後はシミュレーション型の問題です。 ここまでの型を組み合わせた、発展レベルの読み方を扱います。

次に読むページ