• 締切済み

n番目に大きい数を求めるアルゴリズム(C言語)

研修の課題で以下のような課題がだされています。 ------- データ数5000,000の数値データ(順序はランダム)の中で、n番目に大きい数を求めなさい。 nは、0~100とする。 但し、ソートは一切使ってはいけない。 ------- ソートを使ってはいけないという縛りが…大変、厳しく・・・ クイックセレクト等、どれも必ずソートを要するものしか思いつかないのです。。。 下位n個の数値を保持する方法→(ソート要) クイックセレクト→(ソート要) 他に何か、ソートを使わない解法はあるのでしょうか。 アイディアをください。 よろしくお願いします。

みんなの回答

回答No.14

#10です。 ご返事が遅くなりまして申し訳ありません。 ご返答を一読した時に「!」と思いました。 確かに!-1000が最大値になる可能性もあるかも。 などなど。 まだまだアマチュアな自分でした。 それにしても、この本件の質問むずいっすね。 ありがとうございました。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.13

「同順位のデータが複数ある場合」については #4 で既に指摘されているものの, 質問者の反応がまったくないのでコンセンサスが取れていないところだと思います>#12. 回答者にまともに対応する気, あるのかなぁ....

回答No.12

ANo.10の回答者さんへの回答。 >最大値を-1000に書き換えていきます。 >そして、n回目の最大値を探すときに、最大値を表示します。 「1番目に大きいのが3個ある」と、nが1、nが2、nが3の時に、全て同じ答えになっちゃいます。 要素数が10個で、 20,1,20,2,20,3,4,5,6,7 と言うデータを想定してみましょう。 1番目に大きい値(最大値)は「20」、2番目は「7」、3番目は「6」の筈ですが、そうなりません。 また -1000,-1001,-1000,-1002,-1003,-1004,-1001,-1002,-1000,-1000 と言うデータの場合も、正常に動きません。 1番目に大きい値(最大値)は「-1000」ですが、そうなりません。

回答No.11

「人間が手作業で調べる場合の行動」を、そのままプログラミングしましょう。 人間が手作業で調べる場合は、ソートなんて使いませんから。 人間が手作業で調べる場合の手順を、以下に書いてみる。 1. 白紙のメモとペンを用意し、メモに「1回目」とメモする。 2. 5,000,000個の中から、一番端にあるのを1個取って左手で持つ。 3. 次のを右手で取る。 4. メモに「1回目」と書いてあったら、6.へ進む。 5. メモに「今の最大値」が書いてあって、メモに書いてある「今の最大値」と、右手に持ったのを比べ、メモと等しいか大きいなら、それを無視して元に戻して、3.から繰り返す。 6. 両手の2つを比べて、大きいほうを左手に残し、小さいほうは戻す。 7. 3.から6.の手順を順番に最後までやって、最後に左手に1つ残す。 8. メモに「今の最大値」が書いてあって、その「今の最大値」と、最後に左手に残ってたのを比べ、メモと等しいか大きいなら「n番目に大きいのは無かった」と報告して作業終了。 9. メモの「*回目」の数と「n」の数が一致したら、左手にあるのが「n番目に大きいもの」になるので、それを答えにして作業終了。 10. メモの「*回目」の数と「n」の数が一致してないなら、メモの「*回目」を書き直して1増やす。 11. 「今の最大値」として、左手に持ってる物の数値をメモして、左手にあるのを戻して、2.からやり直す。メモの「今の最大値」は「消して書き直し」で構わない。 あとは、これを「C言語に置き換えるだけ」です。

回答No.10

皆様のご意見を参考の上プログラム作成したものです。 私が本来の質問者様に代わって引き続き質問させて下さい。 ■質問内容 私はCを長年独学していたものですが、 プログラムの書き方やアルゴリズムの立て方に自信がありません。 以下の内容をご覧になって頂いて指摘していただけると助かります。 どんな指摘でも構いませんので、よろしくお願い致します。 ■仕様 最大値を1つずつ求めて、出る杭は打たれるように次々と 最大値を-1000に書き換えていきます。 そして、n回目の最大値を探すときに、最大値を表示します。 #define N 5000000; int i ,j= 0; //変数 double maxValue = 0; //最大値の初期化 int temp = 0; //一時的な最大値を確保 if (n == 0) printf("不明"); //n=0の場合は不明 else if (n > 0) { for (i = 0; i < n-1; i++) //上位n-1個分のデータの値を最低値にする { for (j = 0; j < N; j++) //すべてのデータから最大値を探す。 { if (maxValue < data[j]) //最大値を求める { maxValue = data[j]; temp = j; //一時的な添字をtempに記憶させる } } maxValue = 0; //最大値を初期化する(0に初期化) data[temp] = -1000; //最大値のデータを最低値に変更する。 } for (j = 0; j < N; j++) //今回の番で最大値を求める(n番目の最大値) { if (maxValue < data[j]) //最大値を求める { maxValue = data[j]; } } printf("maxValue_n = %f", maxValue); //n番目の最大値を表示(double型) } 以上です。

回答No.9

(1) 数値データから最大の数値を調べる関数を作る (2) 数値データから指定された数値未満で最大の数値を調べる関数を作る (1)と(2)はループだけでできるので、ソートする必要はない。 (1)で最大値を取得して、その最大値を指定して(2)を使えば2番目に大きい数値が得られる。 2番目に大きい数値を指定して(2)を使えば、3番目に大きい数値が得られる。 以下同様にすればn=0以外のn番目の数値が得られる。 n=0の時は、他の回答が指摘しているように仕様が不明。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.8

#1 が「ソートを使ってるからダメ」だったり #3 が「ヒープもアウト」とかいわれると, ここでいう「ソート」ってなんなんだ? 意識のすりあわせが必要だと思うので, ここでいう「ソート」とは何かを正確に書いてもらえませんか? 少なくとも, なぜ「ヒープもアウト」なのか理解できない (ヒープはただのデータ構造であってソートというアルゴリズムとは直接関係ない) ので. あと, 「クイックセレクト」ってなんだろう.

  • jjon-com
  • ベストアンサー率61% (1599/2592)
回答No.7

C言語を知らないのでJavaでコーディング。 import java.util.Random; class Q8100119 { static int M = 5000000; static int N = 100; static int[] array = new int[N]; public static void main(String[] args) { Random rand = new Random(); for (int i = 0; i < N; i++) { array[i] = rand.nextInt(); } int minIdx = getMinIndex(); for (int i = N; i < M; i++) { int k = rand.nextInt(); if (array[minIdx] < k) { array[minIdx] = k; minIdx = getMinIndex(); } } System.out.println("Answer: " + array[minIdx]); } static int getMinIndex() { int minIdx = 0; for (int i = 0; i < array.length; i++) { if (array[minIdx] > array[i]) { minIdx = i; } } return minIdx; } }

  • yama1718
  • ベストアンサー率41% (670/1618)
回答No.6

単純です。 1.最小値(a)を調べる(端から端までサーチ) 2.次に大きい値(b)を調べる(端から端までサーチ)   1)二番目の値の候補(c)を読む   2)次に読んだ値が現在の最小値(a)よりも小さければスキップする   3)次に読んだ値が(a)よりも大きく(b)より小さければ新しい候補(b)とする   4)それを最後まで繰り返せば二番目の値(b)が確定しますね。 3.次の順位の値を調べる   (a)に(b)を入れて2.を繰り返す。 4.それをn回繰り返せばその時調べている最小値がn番目の値という事ですね。 ただし、同じ値が複数ある場合は同一順位の処理が必要なので、もう少し工夫が必要ですけどね。 これならソートもデータ以外の配列も使いません。 ただし、効率は悪いですけどね。

  • tatsu99
  • ベストアンサー率52% (391/751)
回答No.5

考え方だけです。 データ数5000,000の配列のほかに 1.そのデータが使用済みかどうかを示すフラグだけの配列を5000,000個用意する。 ここでデータ[0]とフラグ[0]が対応する。 (データ[0]が有効ならフラグ[0]=1,無効ならフラグ[0]=0のように定義する) 同様にデータ[4999999]とフラグ[4999999]が対応する。 (データ[4999999]が有効ならフラグ[4999999]=1,無効ならフラグ[4999999]=0のように定義する) 2.このフラグ全体を1(有効)で初期化する。 3. 1番目に大きい数は、以下のようにして求める。 データの配列を順に検索し、最も大きい数値を求める。 このとき、その配列の何番目かも、記憶しておき(X番目とすると)、 そのフラグ[X]を無効にする。 4. 2番目に大きい数は、以下のようにして求める。 データの配列を順に検索し、最も大きい数値を求める。 但し、フラグが無効のデータはスキップする。 このとき、その配列の何番目かも、記憶しておき(X番目とすると)、 そのフラグ[X]を無効にする。 5.同様にしてn番目に大きい数値を求める。 ------------------------- この方式で、同じ値が存在する場合は、その値が、異なる順番に使われます。 データの値が10のものが2つあり、以外は全て0なら、 1番目は10 2番目は10 3番目は0 4番目は0 になります。 -------------------------- 又、入っているデータがint型でなおかつ、0以上の値であるという条件なら フラグの配列を作る必要はありません。 データに-1を設定し、データが-1なら無効データとすればよいです。 ------------------------- 不明点があれば、補足してください。