線形探索法(linear search)とは
線形探索法(linear search)は、沢山のデータを一直線に並べて先頭(又は末尾)から順番に調べて、特定のデータを見つけ出すアルゴリズムです。
結果を記録する変数を用意してデータを順番に調べていき、特定のデータがあれば結果の変数を更新する流れで実装出来ます。
イメージとしてはシャッフルされたトランプの山があって、その中から特定のカードを探すために上から1枚ずつめくっていくような感じになります。
特定の値が有るか無いかを調べる
問題
データ数 n=10
の配列 a=[5, 2, 3, 6, 1, 9, 7, 4, 6, 5]
の中に、対象の値 k=6
が有るか無いかを調べる。
処理の流れ
- 探索結果を
result=false
で初期化する 配列 a
の先頭から順に参照するループを設定する
(添字 idx
を0 から 9(n - 1)
の間でカウントアップしてa[idx]
を参照)- もし
a[idx]==k(6)
がtrue
ならresult = true
で更新する - breakでループから抜ける
(有無の確認なので、見つかった時点でその後の探索は不要)
- もし
- 探索結果
result
を確認する(trueなら有り / falseなら無し)
サンプルコード1
添字を使って配列の先頭から順に参照する方法
n = 10 a = [5, 2, 3, 6, 1, 9, 7, 4, 6, 5] k = 6 result = false 0.upto(n - 1) do |idx| if a[idx] == k result = true break end end p result # > true
サンプルコード2
eachメソッドで配列の値を先頭から順に参照する方法
n = 10 a = [5, 2, 3, 6, 1, 9, 7, 4, 6, 5] k = 6 result = false a.each do |num| if num == k result = true break end end p result # > true
サンプルコード3
include?メソッドを使う方法
n = 10 a = [5, 2, 3, 6, 1, 9, 7, 4, 6, 5] k = 6 p a.include?(k) # > true
特定の値が何個有るかを調べる
問題
データ数 n=10
の配列 a=[5, 2, 3, 6, 1, 9, 7, 4, 6, 5]
の中に、対象の値 k=6
が何個有るかを調べる。
処理の流れ
- 探索結果を
result=0
で初期化する 配列 a
の先頭から順に参照するループを設定する
(添字 idx
を0 から 9(n - 1)
の間でカウントアップしてa[idx]
を参照)- もし
a[idx]==k(6)
がtrue
ならresult += 1
で更新する
- もし
- 探索結果
result
を確認する
サンプルコード1
添字を使って配列の先頭から順に参照する方法
n = 10 a = [5, 2, 3, 6, 1, 9, 7, 4, 6, 5] k = 6 result = 0 0.upto(n - 1) do |idx| if a[idx] == k result += 1 end end p result # > 2
サンプルコード2
eachメソッドで配列の値を先頭から順に参照する方法
n = 10 a = [5, 2, 3, 6, 1, 9, 7, 4, 6, 5] k = 6 result = 0 a.each do |num| if num == k result += 1 end end p result # > 2
サンプルコード3
countメソッドを使う方法
n = 10 a = [5, 2, 3, 6, 1, 9, 7, 4, 6, 5] k = 6 p a.count(k) # > 2
特定の値がどこにあるかを調べる
先頭から見たときに最初に現れる添え字を調べる
問題
データ数 n=10
の配列 a=[5, 2, 3, 6, 1, 9, 7, 4, 6, 5]
の中で、先頭からみた時に最初に対象の値 k=6
が現れる場所の添字を調べる。
処理の流れ
- 探索結果を
result=nil
で初期化する
(探索で絶対に出てこない値に設定する) 配列 a
の先頭から順に参照するループを設定する
(添字 idx
を0 から 9(n - 1)
の間でカウントアップしてa[idx]
を参照)- もし
a[idx]==k(6)
がtrue
ならresult = idx
で更新する - breakでループから抜ける
(ループを抜けないとk(6)
が複数あった場合、result
を上書きしてしまう)
- もし
- 探索結果
result
を確認する
サンプルコード1
添字を使って配列の先頭から順に参照する方法
n = 10 a = [5, 2, 3, 6, 1, 9, 7, 4, 6, 5] k = 6 result = nil 0.upto(n - 1) do |idx| if a[idx] == k result = idx break end end p result # > 3
サンプルコード2
each_with_indexメソッドで配列の値と添字を先頭から順に参照する方法
result = nil a.each_with_index do |num, idx| if num == k result = idx break end end p result # > 3
サンプルコード3
indexメソッドを使う方法
n = 10 a = [5, 2, 3, 6, 1, 9, 7, 4, 6, 5] k = 6 p a.index(k) # > 3
先頭から見たときに最後に現れる添え字を調べる
問題
データ数 n=10
の配列 a=[5, 2, 3, 6, 1, 9, 7, 4, 6, 5]
の中で、先頭からみた時に最後に対象の値 k=6
が現れる場所の添字を調べる。
処理の流れ
- 探索結果を
result=nil
で初期化する
(探索で絶対に出てこない値に設定する) 配列 a
の末尾から順に参照するループを設定する
(添字 idx
を9(n - 1) から 0
の間でカウントダウンしてa[idx]
を参照)- もし
a[idx]==k(6)
がtrue
ならresult = idx
で更新する - breakでループから抜ける
(ループを抜けないとk(6)
が複数あった場合、result
を上書きしてしまう)
- もし
- 探索結果
result
を確認する
サンプルコード1
添字を使って配列の末尾から順に参照する方法
n = 10 a = [5, 2, 3, 6, 1, 9, 7, 4, 6, 5] k = 6 result = nil (n - 1).downto(0) do |idx| if a[idx] == k result = idx break end end p result # > 8
サンプルコード2
each_with_indexメソッド、reverse_eachメソッドで配列の値と添字を末尾から順に参照する方法
(わざわざこうして実装する必要はないと思いますのでご参考まで...)
result = nil a.each_with_index.reverse_each do |num, idx| if num == k result = idx break end end p result # > 8
サンプルコード3
rindexメソッドを使う方法
n = 10 a = [5, 2, 3, 6, 1, 9, 7, 4, 6, 5] k = 6 p a.rindex(k) # > 8
対象の値が現れる全ての添え字を調べる
問題
データ数 n=10
の配列 a=[5, 2, 3, 6, 1, 9, 7, 4, 6, 5]
の中で、対象の値 k=6
が現れる全ての添字を調べる。
処理の流れ
- 探索結果を
result=[]
で初期化する
(複数の値が入る可能性があるので、空の配列を設定する) 配列 a
の先頭から順に参照するループを設定する
(添字 idx
を0 から 9(n - 1)
の間でカウントアップしてa[idx]
を参照)- もし
a[idx]==k(6)
がtrue
ならresult
にidx
を追加する
- もし
- 探索結果
result
を確認する
サンプルコード1
添字を使って配列の先頭から順に参照する方法
n = 10 a = [5, 2, 3, 6, 1, 9, 7, 4, 6, 5] k = 6 result = [] 0.upto(n - 1) do |idx| if a[idx] == k result << idx end end p result # > [3, 8]
サンプルコード2
selectメソッドを使う方法(2種類)
n = 10 a = [5, 2, 3, 6, 1, 9, 7, 4, 6, 5] k = 6 # example1 p 0.upto(n - 1).select { |idx| a[idx] == k } # > [3, 8] # example2 p a.each_index.select { |idx| a[idx] == k } # > [3, 8]
最小値・最大値を調べる
最小値を調べる
問題
データ数 n=10
の配列 a=[5, 2, 3, 6, 1, 9, 7, 4, 6, 5]
の中から最小値を調べる。
処理の流れ
- 探索結果を
min_num=Float::INFINITY
で初期化する
(プラス無限大+∞で初期化することで先頭の値を初期値にする) 配列 a
の先頭から順に参照するループを設定する
(添字 idx
を0 から 9(n - 1)
の間でカウントアップしてa[idx]
を参照)- もし
a[idx] < min_num
ならmin_num = a[idx]
で更新する
- もし
- 探索結果
min_num
を確認する
サンプルコード1
添字を使って配列の先頭から順に参照する方法
n = 10 a = [5, 2, 3, 6, 1, 9, 7, 4, 6, 5] min_num = Float::INFINITY 0.upto(n - 1) do |idx| if a[idx] < min_num min_num = a[idx] end end p min_val # > 1
サンプルコード2
eachメソッドで配列の値を先頭から順に参照する方法
n = 10 a = [5, 2, 3, 6, 1, 9, 7, 4, 6, 5] min_num = Float::INFINITY a.each do |num| if num < min_num min_num = num end end p min_num # > 1
サンプルコード3
minメソッドを使う方法
n = 10 a = [5, 2, 3, 6, 1, 9, 7, 4, 6, 5] p a.min # > 1
最大値を調べる
問題
データ数 n=10
の配列 a=[5, 2, 3, 6, 1, 9, 7, 4, 6, 5]
の中から最大値を調べる。
処理の流れ
- 探索結果を
max_num=-Float::INFINITY
で初期化する
(マイナス無限大-∞で初期化することで先頭の値を初期値にする) 配列 a
の先頭から順に参照するループを設定する
(添字 idx
を0 から 9(n - 1)
の間でカウントアップしてa[idx]
を参照)- もし
a[idx] > max_num
ならmax_num = a[idx]
で更新する
- もし
- 探索結果
max_num
を確認する
サンプルコード1
添字を使って配列の先頭から順に参照する方法
n = 10 a = [5, 2, 3, 6, 1, 9, 7, 4, 6, 5] max_num = -Float::INFINITY 0.upto(n - 1) do |idx| if a[idx] > max_num max_num = a[idx] end end p max_num # > 9
サンプルコード2
eachメソッドで配列の値を先頭から順に参照する方法
n = 10 a = [5, 2, 3, 6, 1, 9, 7, 4, 6, 5] max_num = -Float::INFINITY a.each do |num| if num > max_num max_num = num end end p max_num # > 9
サンプルコード3
maxメソッドを使う方法
n = 10 a = [5, 2, 3, 6, 1, 9, 7, 4, 6, 5] p a.max # > 9
最小値と最大値を調べる
問題
データ数 n=10
の配列 a=[5, 2, 3, 6, 1, 9, 7, 4, 6, 5]
の中から最小値と最大値を調べる。
処理の流れ
- 探索結果を初期化する
min_num=Float::INFINITY
(プラス無限大+∞で初期化することで先頭の値を初期値にする)
max_num=-Float::INFINITY
(マイナス無限大-∞で初期化することで先頭の値を初期値にする)
配列 a
の先頭から順に参照するループを設定する
(添字 idx
を0 から 9(n - 1)
の間でカウントアップしてa[idx]
を参照)- もし
a[idx] < min_num
ならmin_num = a[idx]
で更新する - もし
a[idx] > max_num
ならmax_num = a[idx]
で更新する
- もし
- 探索結果
min_num, max_num
を確認する
サンプルコード1
添字を使って配列の先頭から順に参照する方法
n = 10 a = [5, 2, 3, 6, 1, 9, 7, 4, 6, 5] min_num = Float::INFINITY max_num = -Float::INFINITY 0.upto(n - 1) do |idx| if a[idx] < min_num min_num = a[idx] end if a[idx] > max_num max_num = a[idx] end end p [min_num, max_num] # > [1, 9]
サンプルコード2
eachメソッドで配列の値を先頭から順に参照する方法
n = 10 a = [5, 2, 3, 6, 1, 9, 7, 4, 6, 5] min_num = Float::INFINITY max_num = -Float::INFINITY a.each do |num| if num < min_num min_num = num end if num > max_num max_num = num end end p [min_num, max_num] # > [1, 9]
サンプルコード3
minmaxメソッドを使う方法
n = 10 a = [5, 2, 3, 6, 1, 9, 7, 4, 6, 5] p a.minmax # > [1, 9]
条件を組み合わせて調べる
偶数かつ最小値を調べる
問題
データ数 n=10
の配列 a=[5, 2, 3, 6, 1, 9, 7, 4, 6, 5]
の中から偶数かつ最小値を調べる。
処理の流れ
- 探索結果を
min_even=Float::INFINITY
で初期化する
(プラス無限大+∞で初期化することで最初に出た偶数値を初期値にする) 配列 a
の先頭から順に参照するループを設定する
(添字 idx
を0 から 9(n - 1)
の間でカウントアップしてa[idx]
を参照)- もし
a[idx] < min_even
かつa[idx]%2==0
ならmin_even= a[idx]
で更新する
- もし
- 探索結果
min_even
を確認する
サンプルコード1
添字を使って配列の先頭から順に参照する方法
n = 10 a = [5, 2, 3, 6, 1, 9, 7, 4, 6, 5] min_even = Float::INFINITY 0.upto(n - 1) do |idx| if a[idx] < min_even && a[idx] % 2 == 0 min_even = a[idx] end end p min_even # > 2
サンプルコード2
eachメソッドで配列の値を先頭から順に参照する方法
n = 10 a = [5, 2, 3, 6, 1, 9, 7, 4, 6, 5] min_even = Float::INFINITY a.each do |num| if num < min_even && num % 2 == 0 min_even = num end end p min_even # > 2
サンプルコード3
selectメソッド、minメソッドを使う方法
※ スッキリ書けるのですが、selectを行ってからminを行うのでサンプル1・2より処理が遅いです。
n = 10 a = [5, 2, 3, 6, 1, 9, 7, 4, 6, 5] p a.select { |num| num.even? }.min # > 2
奇数かつ最大値を調べる
問題
データ数 n=10
の配列 a=[5, 2, 3, 6, 1, 9, 7, 4, 6, 5]
の中から奇数かつ最大値を調べる。
処理の流れ
- 探索結果を
max_odd=-Float::INFINITY
で初期化する
(マイナス無限大-∞で初期化することで最初に出た奇数値を初期値にする) 配列 a
の先頭から順に参照するループを設定する
(添字 idx
を0 から 9(n - 1)
の間でカウントアップしてa[idx]
を参照)- もし
a[idx] > max_odd
かつa[idx]%2!=0
ならmax_odd=a[idx]
で更新する
- もし
- 探索結果
max_odd
を確認する
サンプルコード1
添字を使って配列の先頭から順に参照する方法
n = 10 a = [5, 2, 3, 6, 1, 9, 7, 4, 6, 5] max_odd = -Float::INFINITY 0.upto(n - 1) do |idx| if a[idx] > max_odd && a[idx] % 2 != 0 max_odd = a[idx] end end p max_odd # > 9
サンプルコード2
eachメソッドで配列の値を先頭から順に参照する方法
n = 10 a = [5, 2, 3, 6, 1, 9, 7, 4, 6, 5] max_odd = -Float::INFINITY a.each do |num| if num > max_odd && num % 2 != 0 max_odd = num end end p max_odd # > 9
サンプルコード3
selectメソッド、maxメソッドを使う方法
※ スッキリ書けるのですが、selectを行ってからmaxを行うのでサンプル1・2より処理が遅いです。
n = 10 a = [5, 2, 3, 6, 1, 9, 7, 4, 6, 5] p a.select { |num| num.odd? }.max # > 9
今回のまとめ
線形探索法の手順
- 結果記録用の変数を準備して適切な値で初期化する
- 繰り返し処理でデータの探索を行い、条件分岐で結果記録用の変数を上書きする
- 必要に応じてループを抜ける処理を入れる
- ループを抜けた後、結果記録用の変数を確認する
複数条件の探索
- 繰り返し中の条件をアンド(&&)で繋げば複数条件に対応できる
- メソッドを繋げると(メソッドチェーン)スッキリ書けるが処理速度は遅くなる
線形探索は流れが単純なので実装しやすいですが、要素数が大量になると探索に時間がかかるという弱点もあります。
線形探索に慣れてきたら他の探索アルゴリズムにも挑戦してみましょう!