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

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

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

Re[11]: c#の乱数の重複について


(過去ログ 177 を表示中)

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

■101616 / inTopicNo.1)  c#の乱数の重複について
  
□投稿者/ ぱー (1回)-(2023/03/20(Mon) 23:45:58)

分類:[.NET 全般] 

c#(.Net Framework 4.7.2)です。
windows formをランダムに表示させることはできたのですが、一回出てきたformがもう一回でてくる、いわゆる重複が起こってしまいます。
重複させないようにする方法などないでしょうか。

private Random random = new Random();

private void button1_Click(object sender, EventArgs e)
{
int minFormId = 1;
int maxFormId = 100;

var formName = typeof(Form1).FullName.TrimEnd('1') + random.Next(minFormId, maxFormId + 1);
var form = (Form)Activator.CreateInstance(Type.GetType(formName));
form.TopLevel = false;
this.Controls.Add(form);
form.Show();
form.BringToFront();
}

引用返信 編集キー/
■101617 / inTopicNo.2)  Re[1]: c#の乱数の重複について
□投稿者/ 伝説のカレー (82回)-(2023/03/21(Tue) 01:04:09)
No101616 (ぱー さん) に返信

private Queue<int> queue = new Queue<int>(Shuffle(new Random(), Enumerable.Range(1, 100).ToList()));

static List<int> Shuffle(Random random, List<int> list) {
  for (var i = list.Count; i > 1; i--) {
    var j = random.Next(i);
    var t = list[i - 1];
    list[i - 1] = list[j];
    list[j] = t;
  }
  return list;
}

private void button1_Click(object sender, EventArgs e) {
  var formName = typeof(Form1).FullName.TrimEnd('1') + queue.Dequeue();
  var form = (Form)Activator.CreateInstance(Type.GetType(formName));
  form.TopLevel = false;
  this.Controls.Add(form);
  form.Show();
  form.BringToFront();
}

引用返信 編集キー/
■101619 / inTopicNo.3)  Re[1]: c#の乱数の重複について
□投稿者/ 魔界の仮面弁士 (3600回)-(2023/03/21(Tue) 03:34:29)
No101616 (ぱー さん) に返信
> 一回出てきたformがもう一回でてくる、いわゆる重複が起こってしまいます。

Form1〜Form10 の 10 種あったとして、11 回押した場合はどうしたいのでしょうか?

もしも「7 回までしかボタンを押せない」などといった回数制限を設けたいのだとしたら、
ボタンを押した回数を数えておき、最大回数に達した時にはボタンをそれ以上押せなくなるよう、
button1.Enabled = false; が呼ばれるようなコードを書いておけばよいのです。


さて、重複についてですが:

サイコロは、何回ふっても 1〜6 の値が出ます。同じ値が出ることもあります。
この場合、1 回目と 10 回目で、確率に差はありません。

それに対して、「トランプで山札からカードを引いてくる」行為や「くじ引き」などでは、
回数を重ねるごとに選択肢の数が減っていきます。そのため重複しません。
その代わり引ける回数に制限がありますし、1 回目と 10 回目で確率が変化してしまいます。

回数制限をなくすには、「選択肢が無くなったら、捨てたトランプカードを山札に戻す」とか
「一回引いたくじは取り除くのではなく、再度、元の箱に戻してから再抽選する」ようにします。
この場合、すべて無くなってから選択肢をリセットするのか、毎回リセットするのかで重複度合いが変わります。


private Random random = new Random();
private int limit = 7;   // クリックできる上限回数
private int clickCount = 0; // クリックした回数
private List<Type> types = new List<Type>();  // 抽選箱

private void Reset()
{
  int minFormId = 1;
  int maxFormId = 10;

  // 抽選箱の中に、生成するクジをランダムに入れておく
  var t = typeof(Form1).FullName.TrimEnd('1');
  types = Enumerable.Range(minFormId, maxFormId - minFormId + 1)
    .Select(n => Type.GetType(t + n))
    .OrderBy(o => random.Next()).ToList();
}

private void button1_Click(object sender, EventArgs e)
{
  // クリック回数が上限に達したら、それ以上押せなくする
  clickCount++;
  if (clickCount >= limit) { button1.Enabled = false; }

  // クリック回数が上限に達する前にクジの残りが無くなったら、再度補充しなおす
  if (types.Count == 0) { Reset(); }

  // クジをひく
  var t = types.FirstOrDefault();
  if (t != null)
  {
    types.RemoveAt(0); // 引いたクジを取り除く

    // クジに書かれていたフォームを表示
    ((Form)Activator.CreateInstance(t)).Show(this);
  }
}
引用返信 編集キー/
■101622 / inTopicNo.4)  Re[1]: c#の乱数の重複について
□投稿者/ Azulean (1265回)-(2023/03/21(Tue) 10:23:14)
2023/03/21(Tue) 10:24:37 編集(投稿者)

No101616 (ぱー さん) に返信
> windows formをランダムに表示させることはできたのですが、一回出てきたformがもう一回でてくる、いわゆる重複が起こってしまいます。
> 重複させないようにする方法などないでしょうか。

「乱数」とは「"毎回"ランダムに出現する数値」です。
たとえば、1 〜 10 が出現する乱数について 10 回試行した際に 1 〜 10 が 1 回ずつ出現するのではなく、重複して出現するなどの偏りも起こりえます。
(厳密な表現ではないですが)前回までに出現した数値を考慮しないと考えましょう。

今回は「ランダムにシャッフルされた数値列」を求めているので、「乱数を用いつつ、シャッフルするアルゴリズムを実装する」ことが必要というわけです。
(事例はすでに書かれているようなので、言葉だけです)
引用返信 編集キー/
■101623 / inTopicNo.5)  Re[2]: c#の乱数の重複について
□投稿者/ ぱー (2回)-(2023/03/21(Tue) 17:55:25)
No101622 (Azulean さん) に返信
 No101619 (魔界の仮面弁士 さん) に返信
 No101617 (伝説のカレー さん) に返信
> 2023/03/21(Tue) 17:38:37 編集(投稿者)

自分が作っているのは4択クイズアプリで、「問題Form」と「答えForm」を分けています。

1.まずクイズスタートボタンを押して次のForm(問題Form)が開きランダムに問題を出題します。

2.出題される問題は4択で「ア、イ、ウ、エ」と表示されています。
 どれかをを押すと次のForm(答えForm)が開き、正解or不正解が表示されます。

3.「次へ」を押すと再び次のForm(問題Form)が開き、ランダムに問題を出題します。

現在3番でつまずいています。
教えていただいたプログラムで実行してみても重複してしまいます。
新しいFormを開くからでしょうか。


引用返信 編集キー/
■101624 / inTopicNo.6)  Re[3]: c#の乱数の重複について
□投稿者/ Azulean (1266回)-(2023/03/21(Tue) 18:28:27)
No101623 (ぱー さん) に返信
> 3.「次へ」を押すと再び次のForm(問題Form)が開き、ランダムに問題を出題します。
>
> 現在3番でつまずいています。
> 教えていただいたプログラムで実行してみても重複してしまいます。

状況がよくわかりません。
お二人が回答されているコードはあくまで「サンプル」であって、そのまま「次へ」ボタンに書いて良いコードではありません。


1. シャッフルするのは最初のフォーム、またはそれ以前のコードで「どこか一度だけ」実施するようにしてください。
2. 「次へ」ボタンでは受け取ったリストから先頭を取り出し、その取り出した分を消した残りを次の Form に伝達するようにしてください。

引用返信 編集キー/
■101625 / inTopicNo.7)  Re[3]: c#の乱数の重複について
□投稿者/ 伝説のカレー (83回)-(2023/03/21(Tue) 19:58:16)
私が思ってたより難しい問題でしたね

出題のたびにランダムなリストのインスタンスを生成したらよろしくないですね

どうやるのがいんですかね
出題オブジェクトにランダムなリストを持たせるとして
私が思いついたところでは

・出題オブジェクトをグローバルにアクセスできるようにする
・出題オブジェクトを各Formで受け渡していく
・出題オブジェクトを仲介して出題,回答を行う

の3種類ですかね
言葉で説明するのがなかなか厄介ですね

> ・出題オブジェクトを仲介して出題,回答を行う

私がイメージしてるのはこんな感じで、デザインパターンのMediatorパターンです
https://light.dotup.org/uploda/light.dotup.org47180.png

引用返信 編集キー/
■101627 / inTopicNo.8)  Re[3]: c#の乱数の重複について
□投稿者/ 魔界の仮面弁士 (3602回)-(2023/03/21(Tue) 21:35:05)
No101623 (ぱー さん) に返信
>> int minFormId = 1;
>> int maxFormId = 100;
> 自分が作っているのは4択クイズアプリで、「問題Form」と「答えForm」を分けています。

えぇと…それで何故、フォームが 10 枚も 100 枚も必要になってしまうのですか?

問題文のたびに、画面が閉じたり開いたりが繰り返されるのは、
画面遷移を管理するのも煩雑ですし、利用者目線で見ても鬱陶しい気がします。

4択問題が 100問あったとしても、問題画面・回答画面は
それぞれ 1 枚あれば十分ではないでしょうか。
同じ画面のまま、文章などを差し替えるだけで良いと思うのです。


たとえば、MessageBox.Show を使う時ってどうしていますか?
表示したいメッセージ内容が 5 種類あったとしても、それは単に引数を差し替えるだけで、
表示内容を切り替えられますよね。それと同じように、問題文だけを差し替えて
同じ画面を使いまわすようにした方が、フォームを開きなおす手間も省けますし、
データ管理の点で見ても、問題文の追加などが容易になると思うのですが…。
引用返信 編集キー/
■101628 / inTopicNo.9)  Re[4]: c#の乱数の重複について
□投稿者/ ぱー (4回)-(2023/03/21(Tue) 21:55:10)
No101622 (Azulean さん) に返信
 No101619 (魔界の仮面弁士 さん) に返信
 No101617 (伝説のカレー さん) に返信
> 2023/03/21(Tue) 17:38:37 編集(投稿者)

3人の方本当にありがとうございます。
確かに魔界の仮面弁士 さんがおっしゃる通り問題、回答画面は1つずつで大丈夫そうですね、、
問題、回答画面を1つにして、伝説のカレー さんがイメージしているような感じにしようと思います。

本当に助かります。
引用返信 編集キー/
■101629 / inTopicNo.10)  Re[5]: c#の乱数の重複について
□投稿者/ ぱー (6回)-(2023/03/22(Wed) 00:05:52)
No101622 (Azulean さん) に返信
 No101619 (魔界の仮面弁士 さん) に返信
 No101617 (伝説のカレー さん) に返信
> 2023/03/21(Tue) 17:38:37 編集(投稿者)

問題、回答Formを1つずつにした場合、どのようにシャッフルのプログラムを書けばよいのでしょうか。

問題を差し替える工程をランダムにすればよいのでしょうか。
引用返信 編集キー/
■101630 / inTopicNo.11)  Re[6]: c#の乱数の重複について
□投稿者/ 魔界の仮面弁士 (3603回)-(2023/03/22(Wed) 02:09:26)
2023/03/22(Wed) 02:49:39 編集(投稿者)

No101629 (ぱー さん) に返信
> 問題、回答Formを1つずつにした場合、どのようにシャッフルのプログラムを書けばよいのでしょうか。

作り方は色々とあるので、「こうしなければいけない」といった決まりはありません。
まずは自分なりに実装してみましょう。

たとえば問題文や回答を、「配列」「List<>」「Queue<>」などといった任意のコレクションで管理しておけば、
問題数の増減にも対処しやすくなりますし、乱数で扱う際にも管理しやすいと思います。

そのうえで、先の「トランプの山札」や「くじ引き」に相当する方法を採ることで
同じ問題が重複して選択されることを防ぐことができます。

つまり一度選んだ問題は、問題一覧のコレクションから取り除くようにするということです。
そのためのシャッフル処理については、たとえばこのような手法がとれます。


(案1) コレクションに詰める際に、あらかじめ LINQ で .OrderBy(x => Guid.NewGuid()) などとしてランダムに並べておく。
 https://dobon.net/vb/dotnet/programing/arrayshuffle.html
 コレクションから取り出すときは、毎回、そのコレクションの先頭要素を取り出し、それをコレクションから取り除く。

(案2) コレクションには連番でそのまま詰めておき、取り出すときに、何番の問題を取り出すのかを乱数で求める。
 たとえば、残りの問題が 15 個あるのなら、0〜14 の範囲の乱数を得るようにして、
 その選んだ番号の問題をコレクションから取り出して画面に表示し、
 コレクションからその番号の問題を削除する。→ 例: List<> クラスの RemoveAt メソッド


コーディングが楽なのは案1かな?



> 自分が作っているのは4択クイズアプリで
将来的には、「たまにしか選ばれない問題」「よく出題される問題」などと、
問題文によって出題頻度を調整するといった作りこみもできそうですね。

問題文の難易度を易しい・普通・難しいの 3 グループに分けておいて、乱数生成を
「優しいグループから 60% の確率、普通から 30% の確率、難しいから 10% の確率で、合計 10 問を選ぶ」
「優しいグループから 0% の確率、普通から 30% の確率、難しいから 70% の確率で、合計 10 問を選ぶ」
などと調整するとか…。

あるいは連続して正解した場合は問題の難易度を上げ、誤答が続けば難易度を下げるとか…。

クイズミリオネアの「フィフティ・フィフティ」のように、一時的に4択から2択に絞り込めるボーナス機能を設けるとか…。
引用返信 編集キー/
■101631 / inTopicNo.12)  Re[7]: c#の乱数の重複について
□投稿者/ ぱー (8回)-(2023/03/22(Wed) 07:10:33)
No101630 (魔界の仮面弁士 さん) に返信
> 2023/03/22(Wed) 02:49:39 編集(投稿者)

なるほど、、
難しいけど面白いですね!!
自分の思い通りに作成できるように頑張ります。
ありがとうございます!!
引用返信 編集キー/
■101632 / inTopicNo.13)  Re[8]: c#の乱数の重複について
□投稿者/ shu (1294回)-(2023/03/22(Wed) 08:02:40)
No101631 (ぱー さん) に返信
> ■No101630 (魔界の仮面弁士 さん) に返信
>>2023/03/22(Wed) 02:49:39 編集(投稿者)
>
> なるほど、、
> 難しいけど面白いですね!!
> 自分の思い通りに作成できるように頑張ります。
> ありがとうございます!!

過去にVisual Basic中学校さんで回答した内容が多少役にたつかもしれません。
http://rucio.cloudapp.net/ThreadDetail.aspx?ThreadId=10978
引用返信 編集キー/
■101633 / inTopicNo.14)  Re[8]: c#の乱数の重複について
□投稿者/ furu (202回)-(2023/03/22(Wed) 11:08:37)
No101631 (ぱー さん) に返信
> 難しいけど面白いですね!!
件数少ないないなら
使用済みでないのを見つかるまで探すというのが
わかりやすくて好きです。

使用済みはHashSetなどで別に管理してもいいですが
Listの項目をnullにすることにすると変数増えず楽です。

{
    var list = new List<Form>(){Form1, Form2, Form3, Form4};
    var random = new Random();
    for (var i= 0; i < list.Count; i++)
    {
        var j = 次(list, random);  //未使用の項目のインデックスをランダム取得
        list[j] = null;            //未使用にする
    }
}
// 未使用の項目のインデックスをランダム取得
int 次(List<Form> list, Random random)
{
    int i;
    do
        i = random.Next(list.Count);
    while (list[i] == null);
    return i;
}
// 未使用の項目のインデックスをランダム取得(再帰バージョン)
int 次(List<Form> list, Random random)
{
    var i = random.Next(list.Count);
    return list[i] == null ? 次(list, random) : i;
}

引用返信 編集キー/
■101634 / inTopicNo.15)  Re[9]: c#の乱数の重複について
□投稿者/ furu (203回)-(2023/03/22(Wed) 11:26:58)
No101633 (furu さん) に返信
コメント訂正(編集キーも入れ忘れ)
> list[j] = null; //未使用にする
list[j] = null; //使用済にする
引用返信 編集キー/
■101645 / inTopicNo.16)  Re[10]: c#の乱数の重複について
□投稿者/ ぱー (10回)-(2023/03/23(Thu) 13:45:24)
1つのFormでIf文などを使って1〜10問程度の問題文を作成し、その問題をシャッフルするプログラムを作るにはどうすればよいのでしょうか。
引用返信 編集キー/
■101647 / inTopicNo.17)  Re[11]: c#の乱数の重複について
□投稿者/ 伝説のカレー (84回)-(2023/03/23(Thu) 16:47:42)
モデリングしましょう

class 問題 {
  public string 問い;
  public List<string> 選択肢;
  public int 正解番号;

  public 問題(string 問い, List<string> 選択肢, int 正解番号) {
    this.問い = 問い;
    this.選択肢 = 選択肢;
    this.正解番号 = 正解番号;
  }
}

var 問題List = new List<問題>() {
  new 問題("ドイツの首都は?",new List<string>(){"ミュンヘン", "ベルリン"}, 1),
  new 問題("ファイガの上位魔法は?",new List<string>(){"ファイギョ", "ファイジャ"}, 0)
};

あとはリストをシャッフルして画面に表示して完成です

引用返信 編集キー/


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

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -