基本情報技術者 2015年 春期 午後 問08
次のプログラムの説明及びプログラムを読んで、設問1〜3に答えよ。
与えられたn個のデータの中からk番目に小さい値を選択する方法として、クイックソートを応用したアルゴリズムを考える。クイックソートとは、n個のデータをある基準値以下の値のグループと基準値以上の値のグループに分割し(基準値はどちらのグループに入れても構わない)、更にそれぞれのグループで基準値を選んで二つのグループに分割するという処理を繰り返してデータを整列するアルゴリズムである。クイックソートを応用してk番目に小さい値を選択するアルゴリズムでは、データを二つのグループに分割した時点で、求める値はどちらのグループに含まれるかが確定するので、そのグループだけに、更に分割する処理を繰り返し適用する。グループの分割ができなくなった時点で、k番目に小さい値が選択されている。
〔プログラムの説明〕
n個の数値が格納されている配列xと値kを与えて、k番目に小さい値を返す関数Selectである。ここで、配列xの要素番号は1から始まる。また、配列xの大きさは、配列に格納される数値の個数分だけ確保されているものとする。Selectの処理の流れを次に示す。
(1) 行番号3〜4
k番目に小さい値を選択するために走査する範囲(以下、走査範囲という)の左端をTop、右端をLastとし、まず配列全体を走査範囲とする。
(2) 行番号5〜32
① 走査範囲に含まれる要素の数が1以下になるまで、②、③を繰り返す。
② 基準値Pivotを選び、走査範囲内の値で基準値以下のものを左に、基準値以上のものを右に集める(行番号6〜24)。
③ 走査範囲が基準値以下の値から成るグループと基準値以上の値から成るグループに分割されるので、k番目に小さい値が含まれるグループを新たな走査範囲とする(行番号25〜30)。
④ 繰返しが終了したときに、要素x[k]の値がk番目に小さい値として、選択される。
Selectの引数と返却値の仕様は次のとおりである。


設問1:
問題文を見る 関数Selectの追跡に関する次の記述中のに入れる正しい答えを、解答群の中から選べ。
関数Selectの引数で与えられた配列xの要素番号1〜7の内容が3, 5, 6, 4, 7, 2, 1であり、nが7、kが3のとき、配列xの走査範囲の左端Topと右端Lastの値は次のとおりに変化する。
・TopとLastの初期値は、それぞれ1と7である。
・Top < Lastが成り立つ間、次に示す(1)選択処理1回目の①〜③、(2)選択処理2回目の①〜③、…と実行する。
(1) 選択処理1回目
① 配列xの走査範囲を二つの部分に分ける基準値Pivotに配列xの3番目の要素x[3]の値6を設定する。次に、iにTopの値1、jにLastの値7を設定する。
② 配列xのTopからLastまでの走査範囲内にある数値を、6以下の数値のグループと6以上の数値のグループの二つに分ける処理を行う。その結果、配列xの内容は次のとおりになる。
3, 5, 1, 4, 2, 7, 6
③ aを設定して選択処理の2回目に進む。
(2) 選択処理2回目
① 基準値Pivotにx[3]の値1を設定する。
② 配列xのTopからLastまでの走査範囲内にある数値を、1以下の数値のグループと1以上の数値のグループの二つに分ける処理を行う。その結果、配列xの内容は次のとおりになる。
1, 5, 3, 4, 2, 7, 6
③ bを設定して選択処理の3回目に進む。
(3) 選択処理3回目
⋮
この選択処理を繰り返して、Top < Lastでなくなったときに処理を終了する。このとき、関数の返却値x[k]には与えられた数値の中からk番目に小さい値が選択されている。
a, bに関する解答群
ア:Topに値1、Lastに値5
イ:Topに値1、Lastに値6
ウ:Topに値2、Lastに値5
エ:Topに値2、Lastに値6
オ:Topに値3、Lastに値5
カ:Topに値3、Lastに値6
模範解答
a:ア、b:ウ
解説
解答の導き方
この設問では、配列全体を一度に並べ替えるのではなく、 番目に小さい値が含まれる範囲だけを少しずつ狭めます。配列の走査範囲は、左端の添字をTop、右端の添字をLastで表します。
プログラムでは、基準値Pivotより小さい値を左へ、大きい値を右へ移動します。分割後に番目の値がどちら側にあるかを調べ、次の走査範囲を決めます。
選択処理1回目
初期値はTopが1、Lastが7です。Pivotにはの値である6を設定します。
- 左からは、Pivotより小さい3と5を通過し、で止まります。はPivotと等しいためです。
- 右からは、がPivotより大きくないため、で止まります。
- なので、とを交換します。配列は「3, 5, 1, 4, 7, 2, 6」になります。
- 次にです。左から4を通過してで止まり、右からは2の位置で止まります。そこでとを交換します。配列は「3, 5, 1, 4, 2, 7, 6」になります。
- さらにとなります。なので、分割のループを抜けます。
ここでは以下です。そのため、3番目に小さい値は左側のグループにあります。プログラムの「」が成り立つので、Lastをにします。Topは1のままです。
したがって、選択処理1回目の後は、Topが1、Lastが5です。
選択処理2回目
走査範囲は添字1〜5です。Pivotには、現在のの値である1を設定します。
- 左からは、がPivotより小さくないため、で止まります。
- 右からは、、の順にPivotより大きい値を通過し、で止まります。このときです。
- なので、とを交換します。配列は「1, 5, 3, 4, 2, 7, 6」になります。交換後はです。
- はPivotより小さくないためは進まず、右からもがPivotより大きいため、を1まで戻します。となるので、分割のループを抜けます。
このときです。、つまりが成り立つので、3番目に小さい値は右側のグループにあります。プログラムに従ってTopをにします。は成り立たないため、Lastは5のままです。
したがって、選択処理2回目の後は、Topが2、Lastが5です。
誤りやすいポイント
- Pivotは選択処理のたびに設定し直します。2回目のPivotは、最初のPivotである6ではなく、現在のの値である1です。
- 配列全体が小さい順に並ぶわけではありません。番目の値が含まれる側だけを次の走査範囲にします。
- TopやLastは、交換した位置ではなく、分割後に番目の値が含まれるグループに合わせて更新します。
- 2回目は「」が成り立つためTopを更新します。「」は成り立たないので、Lastは更新しません。
FAQ
Q: なぜ基準値にを使うのですか?
A: Pivotは、値を左右に分けるための基準です。最初からが番目に小さい値だと決まっているわけではありません。分割後に番目の値がある側だけを調べ、走査範囲を狭めていきます。
A: Pivotは、値を左右に分けるための基準です。最初からが番目に小さい値だと決まっているわけではありません。分割後に番目の値がある側だけを調べ、走査範囲を狭めていきます。
Q: 配列を最後まで小さい順に並べる必要はありますか?
A: ありません。必要なのは番目に小さい値を見つけることです。関係のない側まで並べ替えず、対象の範囲だけを繰り返し分割します。
A: ありません。必要なのは番目に小さい値を見つけることです。関係のない側まで並べ替えず、対象の範囲だけを繰り返し分割します。
Q: Pivotと同じ値がある場合はどうなりますか?
A: 比較は「Pivotより小さい」「Pivotより大きい」という条件です。Pivotと等しい値はそれらの条件に当てはまらず、走査を止める位置や交換の対象になります。
A: 比較は「Pivotより小さい」「Pivotより大きい」という条件です。Pivotと等しい値はそれらの条件に当てはまらず、走査を止める位置や交換の対象になります。
関連キーワード: クイックソート、配列、分割統治法、探索範囲、選択アルゴリズム
設問2:
問題文を見る 次の記述中のに入れる正しい答えを、解答群の中から選べ。
引数で与えられた配列xの要素番号1〜7の内容が1, 3, 2, 4, 2, 2, 2であり、nが7、kが3のとき、選択処理が終了するまでにプログラム中のαの部分はc回実行され、γの部分はd回実行される。
c, dに関する解答群
ア:1
イ:2
ウ:3
エ:4
オ:5
カ:6
模範解答
c:イ、d:エ
解説
解答の導き方
この問題では、外側の繰返しで実行されるαと、値を交換するときに実行されるγを、それぞれ数えます。
走査範囲(今調べている配列の範囲)は最初、要素番号1〜7です。基準値Pivot(分割の基準にする値)は、プログラムの「Pivot ← x[k]」で選ばれます。ここではkが3なので、最初のPivotはx[3]の値である2です。
最初の外側の繰返し
αの部分でPivotやi、jを設定した後、内側のループで値を調べます。x[i]がPivotより小さい間はiを進め、Pivotより大きいx[j]があればjを戻します。iとjが交差する前なら、γの部分でx[i]とx[j]を交換します。
1回目は、x[1]が1でPivotの2より小さいため、iが1から2に進みます。jは、x[7]が2でPivotより大きくないため、そのまま7です。そこでx[2]の3とx[7]の2を交換します。
2回目は、x[3]もx[6]も2です。どちらも交換後の値は変わりませんが、iがjより小さいため、γの部分は実行されます。
3回目は、x[4]の4とx[5]の2を交換します。その後、iは5、jは4となります。次の確認でiがj以上になり、内側のループを抜けます。
最初の外側の繰返しでは、αはその開始時に1回実行されています。終了後、kはj以下なので走査範囲の右端Lastが4になります。走査範囲は要素番号1〜4です。
次の外側の繰返し
走査範囲が1〜4になったので、αがもう1回実行されます。Pivotは再びx[3]の値で、2です。
iはx[1]が1でPivotより小さいため2に進みます。jは要素番号4の値が2なので、そのまま4です。iはjより小さいため、x[2]の2とx[4]の2を交換します。この交換でも値は変わりませんが、γの部分は実行されます。
交換後、iとjはどちらも3です。iがj以上になったため内側のループを抜けます。続く処理でTopは4、Lastは2となり、TopがLastより小さくないので、外側の繰返しも終了します。
したがって、αは外側の繰返しごとに1回ずつ、合計2回実行されます。解答群ではイです。γは最初の外側の繰返しで3回、次の外側の繰返しで1回実行され、合計4回です。解答群ではエです。
誤りやすいポイント
- αは、外側の繰返し1回につき1回実行されます。γの実行回数とは数え方が異なります。
- γは、値が異なるときだけではなく、iがjより小さいときに実行されます。そのため、2と2を交換する場合も1回として数えます。
- iとjが交差した後は、γを実行しません。最後にiがj以上になったときは、内側のループを抜けます。
FAQ
Q: γの実行回数は、内側のループの回数と同じですか?
A: 同じではありません。内側のループでは値を調べる処理も行います。γは、iがjより小さい場合に値を交換する処理です。
A: 同じではありません。内側のループでは値を調べる処理も行います。γは、iがjより小さい場合に値を交換する処理です。
Q: 同じ値を交換する処理も数えるのはなぜですか?
A: プログラムは、交換する値が異なるかどうかではなく、iがjより小さいかどうかでγを実行するか決めているためです。
A: プログラムは、交換する値が異なるかどうかではなく、iがjより小さいかどうかでγを実行するか決めているためです。
Q: 配列全体を並べ替えているのですか?
A: いいえ。本文にあるとおり、k番目に小さい値が含まれるグループだけを次の走査範囲にします。そのため、配列全体を最後まで並べ替える必要はありません。
A: いいえ。本文にあるとおり、k番目に小さい値が含まれるグループだけを次の走査範囲にします。そのため、配列全体を最後まで並べ替える必要はありません。
関連キーワード: クイックソート、選択アルゴリズム、ピボット、配列、走査、要素交換
設問3:
問題文を見る 次の記述中のに入れる正しい答えを、解答群の中から選べ。
プログラム中のβの行x[i] < Pivotを誤ってx[i] ≦ Pivotとした。この場合、引数で与えられた配列xの要素番号1〜6の内容が1, 1, 1, 1, 1, 1であり、nが6、kが3のとき、e。また、引数で与えられた配列xの要素番号1〜6の内容が1, 3, 2, 4, 2, 2であり、nが6、kが3のとき、f。
e, fに関する解答群
ア:Lastに値0が設定される
イ:Pivotに値0が設定される
ウ:Topに値0が設定される
エ:処理が終了しない
オ:配列の範囲を越えて参照する
模範解答
e:オ、f:エ
解説
解答の導き方
この設問では、左側を調べる条件が本来の「x[i] < Pivot」ではなく、「x[i] ≦ Pivot」に変わったとき、処理がどうなるかを確認します。
本来の条件は、Pivotと同じ値に出会ったところで左側の探索を止めます。しかし、誤った条件ではPivotと同じ値でも探索を続けます。そのため、iが進みすぎて配列の外を参照したり、走査範囲が変わらず処理が繰り返されたりします。
eの場合
配列の要素はすべて1で、kは3です。最初に、走査範囲は要素番号1〜6、Pivotはx[k]、つまりx[3]の値である1になります。iは1から始まります。
誤った条件「x[i] ≦ Pivot」では、x[1]からx[6]まで、どの要素を見ても「1 ≦ 1」が成り立ちます。そのたびにiが1ずつ増え、iは7になります。次の条件判定ではx[7]を参照しますが、配列にあるのは要素番号1〜6です。配列の範囲を越えて参照するため、eはオです。
fの場合
最初の走査範囲は要素番号1〜6で、Pivotはx[3]の値である2です。
まず左側では、x[1]の1はPivot以下なのでiが2になります。x[2]の3はPivot以下ではないため、ここで左側の探索が止まります。右側では、x[6]の2はPivotより大きくないので、jは6のままです。iはjより小さいため、x[2]とx[6]を入れ替えます。その結果、配列は「1, 2, 2, 4, 2, 3」となり、iは3、jは5になります。
次に、左側ではx[3]の2もPivot以下なので、iが4になります。x[4]の4はPivot以下ではないため、探索が止まります。右側ではx[5]の2がPivotより大きくないため、jは5のままです。そこでx[4]とx[5]を入れ替えます。配列は「1, 2, 2, 2, 4, 3」となり、iは5、jは4になります。
iがj以上になったので、分割のループを抜けます。kは3でjは4なので、Lastはi−1、つまり4になります。Topは1のままです。
次の繰返しでは、走査範囲は要素番号1〜4で、Pivotは再びx[3]の2です。左側の探索では、x[1]からx[4]までの値がすべてPivot以下なので、iは5まで進みます。x[5]の4で探索が止まります。右側の探索はx[4]の2で止まり、iはj以上なので分割のループを抜けます。
ここでLastに設定される値は、また4です。Topも1のままなので、外側の条件「Top < Last」は引き続き成り立ちます。配列の値も変化せず、次の繰返しでも同じ状態になります。そのため処理が終了せず、fはエです。
誤りやすいポイント
- Pivotと同じ値に対して、正しい条件「x[i] < Pivot」なら左側の探索は止まります。「x[i] ≦ Pivot」では止まらず、iがさらに進みます。
- fではiが走査範囲の右端を越えてx[5]を参照しますが、配列自体の要素番号1〜6の範囲内です。走査範囲を越えることと、配列そのものの範囲を越えることは別です。
- fではLastが4のまま変わらず、Topも1のままです。外側の繰返し条件が成り立ち続けるため、処理が終わりません。
FAQ
Q: Pivotと同じ値で左側の探索を止める必要があるのはなぜですか?
A: 同じ値でも探索を続ける条件にすると、iが走査範囲を越えて進むことがあります。等しい値に出会ったところで止めれば、右側の探索との比較や値の入替えに進めます。
A: 同じ値でも探索を続ける条件にすると、iが走査範囲を越えて進むことがあります。等しい値に出会ったところで止めれば、右側の探索との比較や値の入替えに進めます。
Q: fでは走査範囲を越えたのに、なぜ「配列の範囲を越えて参照する」ではないのですか?
A: fで参照するx[5]は、現在の走査範囲1〜4の外ですが、配列の要素番号1〜6の内側にあります。配列そのものの範囲は越えていません。
A: fで参照するx[5]は、現在の走査範囲1〜4の外ですが、配列の要素番号1〜6の内側にあります。配列そのものの範囲は越えていません。
Q: fの処理が繰り返されるのはなぜですか?
A: 各繰返しの後もTopが1、Lastが4のままです。したがって「Top < Last」が成り立ち、同じ処理が再び実行されます。
A: 各繰返しの後もTopが1、Lastが4のままです。したがって「Top < Last」が成り立ち、同じ処理が再び実行されます。
関連キーワード: クイックソート、選択アルゴリズム、配列、添字、ループ、境界条件



