C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

C#で上位5位を取得する方法

[トピック内 10 記事 (1 - 10 表示)]  << 0 >>

■93710 / inTopicNo.1)  C#で上位5位を取得する方法
  
□投稿者/ コウキ (1回)-(2020/01/18(Sat) 19:44:38)

分類:[C#] 

配列に入っている100個の数値のトップ5位までを取得したいのですが、
どのようにやると効率的でしょうか。

今やっているのは
for文でループさせて1個ずつ配列の数値を取得していき、
現在の1位以上であれば1位にして、現在の1位を2位にして、2位を3位にして・・・
というやり方で検討しているのですが、
取得した数値が2位以上、1位未満だった場合であれば、現在の2位を3位にして・・・
という形で、パターン複雑になりそうで、簡単な方法はありますでしょうか?
(現在取得した数値が、同じであっても上位に入れ替えます)
引用返信 編集キー/
■93711 / inTopicNo.2)  Re[1]: C#で上位5位を取得する方法
□投稿者/ PANG2 (329回)-(2020/01/18(Sat) 20:31:57)
ソート
引用返信 編集キー/
■93712 / inTopicNo.3)  Re[2]: C#で上位5位を取得する方法
□投稿者/ コウキ (2回)-(2020/01/18(Sat) 20:43:40)
No93711 (PANG2 さん) に返信
> ソート

ありがとうございます。
すみません、書き方が足りてませんでした。

配列の位置も必要情報になります。
配列の位置によって、属性が定まっており、ソートしてしまうとバラバラになってしまうためです。
引用返信 編集キー/
■93713 / inTopicNo.4)  Re[3]: C#で上位5位を取得する方法
□投稿者/ KOZ (82回)-(2020/01/18(Sat) 23:58:36)
2020/01/19(Sun) 00:05:09 編集(投稿者)
No93712 (コウキ さん) に返信
> 配列の位置も必要情報になります。
> 配列の位置によって、属性が定まっており、ソートしてしまうとバラバラになってしまうためです。

格納先をリストにすれば簡単です。

static void Main(string[] args) {
    var array = new int[10];
    var lst = new List<int>();
    var rnd = new System.Random();
    for (int i = 0; i < array.Length; i++) {
        array[i] = rnd.Next(0, 100);
    }
    foreach (var value in array) {
        bool inserted = false;
        for (int i = 0; i < lst.Count; i++) {
            if (value > lst[i]) {
                lst.Insert(i, value);
                inserted = true;
                break;
            }
        }
        if (!inserted) {
            lst.Add(value);
        }
        if (lst.Count > 5) {
            lst.RemoveAt(5);
        }
    }
    foreach (var value in lst) {
        Console.WriteLine("{0}", value);
    }
    Console.ReadKey();
}

引用返信 編集キー/
■93714 / inTopicNo.5)  Re[1]: C#で上位5位を取得する方法
□投稿者/ キングダム (53回)-(2020/01/19(Sun) 00:16:41)
No93710 (コウキ さん) に返信
100件中5件で、抽出する数が全体の数に対して小さいのでヒープを使えば計算効率は良い気がします。
100件が数として小さいので全部ソートしても良いとは思いますが。

LINQを使えば簡単です。
https://paiza.io/projects/FAb0mgQnIgEkuMF4HIgdYQ

数値と一緒に属性を得る方法はインデックスソートを使ったりクラスを定義して予めまとめてしまうと
いったやり方があります。
https://paiza.io/projects/j769AmxnBgR0_PRGxbp2sA
https://paiza.io/projects/6JyE_1OZtuqDf4c4wHFvzg

引用返信 編集キー/
■93715 / inTopicNo.6)  Re[2]: C#で上位5位を取得する方法
□投稿者/ キングダム (54回)-(2020/01/19(Sun) 01:14:05)
クイックソートならテッテケテーと配列を分けていくんで先頭の方だけ分割すれば
効率良いのじゃないかなと思ってググってみたところ

ここにいろんなやり方載ってました
https://ja.wikipedia.org/wiki/選択アルゴリズム#k個の最小・最大要素の選択

ヒープを使う、クイックソートを使う、マージソートを使う、トーナメント方式といったのがあるみたいです
引用返信 編集キー/
■93716 / inTopicNo.7)  Re[3]: C#で上位5位を取得する方法
□投稿者/ 魔界の仮面弁士 (2545回)-(2020/01/19(Sun) 10:02:30)
No93710 (コウキ さん) に返信
> 配列に入っている100個の数値のトップ5位までを取得したいのですが、

同値の順位については、どう扱うのでしょうか?
たとえば 4 位にあたる値が 3 件あった場合、
その次の値を 7 位とみなすのか、5 位とみなす実装なのか、という点です。

それによって、次のようなパターンが予想されます。

(1)1位、2位、3位、4位a、4位b、4位c、5位 の 7件が列挙される … 4位が何件あっても、その次の値は5位として扱う
(2)1位、2位、3位、4位a、4位b、4位c    の 6件が列挙される … 4位が3件あれば、その次の値を7位として扱う
(3)1位、2位、3位、4位a、4位b、       の 5件が列挙される … 4位が3件あっても、最終結果が 5 件となるように扱う


(1) に関しては、4位の時は「3件の配列」、1〜3位や5位は「1件の配列」を返すようにすれば、
全体としては 5 要素として扱うこともできそうです。

(3)の場合、取りこぼしが発生する可能性があるので、同値の場合に
元の順序を維持する必要があるのかどうかも争点になりそう。


■No93712 (コウキ さん) に返信
> 配列の位置も必要情報になります。
> 配列の位置によって、属性が定まっており、ソートしてしまうとバラバラになってしまうためです。

Linq の .Select((element, index) => 〜) を使えば、元の位置情報を残しておくことができます。

https://paiza.io/projects/tiEqGazmTklKn8d1Bzf_ow?language=csharp

引用返信 編集キー/
■93720 / inTopicNo.8)  Re[3]: C#で上位5位を取得する方法
□投稿者/ コウキ (3回)-(2020/01/19(Sun) 20:06:15)
皆様ありがとうございます。
いろいろな方法があるということで、大変勉強になります。
どれが最適なものかはすぐにわからないので、検証してみます。

>>魔界の仮面弁士さん
同じ数値が出てきた場合は、新しい方を優先していきます。
(配列のイメージでは、添字が大きいほうが優先度が高いという形です)
引用返信 編集キー/
■93721 / inTopicNo.9)  Re[4]: C#で上位5位を取得する方法
□投稿者/ コウキ (4回)-(2020/01/19(Sun) 20:29:41)
一応自分の当初の方式はこちらです。

            int[] Ary = new int[100];//本番は100個の数値が入っている
            int No1 = 0;
            int No2 = 0;
            int No3 = 0;
            int No4 = 0;
            int No5 = 0;
            int myValue = 0;

            for (int Cnt = 0; Cnt <= 100; Cnt++)
            {
                myValue = Ary[Cnt];

                if (myValue >= No1) //1位以上だった場合
                {
                    No5 = No4;
                    No4 = No3;
                    No3 = No2;
                    No2 = No1;
                    No1 = myValue;
                }
                else if (No1 > myValue && myValue >= No2) //1位未満 2位以上だった場合(2位以下を書き換え)
                {
                    No5 = No4;
                    No4 = No3;
                    No3 = No2;
                    No2 = myValue;
                }
                else if (No2 > myValue && myValue >= No3) //2位未満 3位以上だった場合(3位以下を書き換え)
                {
                    No5 = No4;
                    No4 = No3;
                    No3 = myValue;
                }
                else if (No3 > myValue && myValue >= No4) //3位未満 4位以上だった場合(4位以下を書き換え)
                {
                    No5 = No4;
                    No4 = myValue;
                }
                else if (No4 > myValue && myValue >= No5) //4位未満 5位以上だった場合(5位を書き換え)
                {
                    No5 = myValue;
                }
            }

引用返信 編集キー/
■93722 / inTopicNo.10)  Re[4]: C#で上位5位を取得する方法
□投稿者/ 魔界の仮面弁士 (2546回)-(2020/01/20(Mon) 02:05:33)
No93720 (コウキ さん) に返信
> 同じ数値が出てきた場合は、新しい方を優先していきます。
> (配列のイメージでは、添字が大きいほうが優先度が高いという形です)

同じ値の場合、添字が大きいほうを優先する…ということは、
No93716 の例でいうと、同値である 4位a、4位b、4位c の順番を決めるにあたり、
添字が a>b>c だったとすれば、4位a が最も優先されるということですよね。

では添え字を優先したうえで、その次の順位はどう扱うのでしょうか?


[1] 4位の数値は列挙されたので、他の同値(4位b と 4位c)はスキップして、次の値が7位として使われる。(7位なので列挙対象外)
 → 結果として 4 件だけが列挙される。4 件とも別の値。

[2] 4位の数値は列挙されたので、他の同値(4位b と 4位c)はスキップして、次の値が5位として使われる。(5位なので列挙対象)
 → 結果として 5 件列挙される。5 件とも別の値。

[3] 4位a の次の優先度となる 4位b が次の値として使われる。4位c は 6件目にあたるので列挙されない。(順位よりも件数優先)
 → 結果として 5 件列挙される。4 件目と 5 件目は同値だが、元の添字は 4 件目の方が大きい。

[4] 4位a の次の優先度となる 4位b が次の値として使われる。4位c も 5位以内なので列挙される。
  その次の値は 7位として扱われ、列挙されない。
 → 結果として 6 件列挙される。4〜6 件目は同値であり、その 3 つの元の添字は 4 件目が最も大きく、6件目が最も小さい。

[5] 4位a の次の優先度となる 4位b が次の値として使われる。4位c も 5位以内なので列挙される。
  その次の値は 5位として扱われ列挙されるが、その次の次の値は 6位となり、列挙対象外。
 → 結果として 7 件列挙される。4〜6 件目は同値であり、その 3 つの元の添字は 4 件目が最も大きく、6件目が最も小さい。




パターン[3] の例:
var result = source.Select((Element, Index) => new {Element, Index}).OrderByDescending(x => x.Element).ThenByDescending(x => x.Index).Take(5).ToArray();


パターン[2] の例:
var result = source.Select((Element, Index) => new { Element, Index }).OrderByDescending(x => x.Element).ThenByDescending(x => x.Index).GroupBy(x => x.Element).Take(5).Select(x => x.First()).ToArray();
引用返信 編集キー/

このトピックをツリーで一括表示


トピック内ページ移動 / << 0 >>

このトピックに書きこむ