質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.53%

  • C

    3657questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

数値に重複のないビンゴカードをつくる。

受付中

回答 4

投稿

  • 評価
  • クリップ 0
  • VIEW 259

BONERer

score 11

 前提・実現したいこと

乱数を使ってビンゴカードの数値を得ます。そのとき、すべての数値は互いに異なる必要があります。

 発生している問題・エラーメッセージ

Get_number関数で、すべての数値が互いに異なるとき、Get_number関数を再帰的に呼び出さずに
Get_number関数を抜けるはずが、実際は抜けてくれないです。printf("********\n")によって
昇順ソート済みの重複のない配列を見やすくしています。
また今回の質問には関係ないことですが、Get_number関数ないで新たに配列subを作ったのは、
乱数なのに昇順ソートされて出てくるのが気持ち悪いからです。

該当のソースコード

#include <stdio.h>
#include <time.h>
#include <stdlib.h>

#define NUM 9
#define WIDTH 3

void Get_number(int p[]);
void Make_card(int p1[], int p2[][WIDTH]);
void Print_card(int p3[][WIDTH]);

int main(void)
{
        int num1[NUM] = {0};
        int num2[WIDTH][WIDTH] = {};
        //ビンゴカードの作成**********//
        srand((unsigned)time(NULL));
        Get_number(num1);
        printf("PRINTDEBUG\n");
        Make_card(num1, num2);
        Print_card(num2);
        return 0;
}

void Get_number(int p[])
{
        int i,j;
        int sub[NUM] = {0};
        int buffer=0;
        for(i=0; i<NUM; i++) {p[i]=rand()%20+1;}
        for(i=0; i<NUM; i++) {sub[i]=p[i];}
        //昇順にソートして全て異なる数字かを判定する
        for(i=0; i<NUM-1; i++) {
                for(j=i+1; j<NUM; j++) {
                        if(sub[i]>sub[j]) {buffer=sub[i]; sub[i]=sub[j]; sub[j]=buffer;}
                }
        }
        for(i=0; i<NUM; i++) {printf("%d ", sub[i]);}
        printf("\n");
        for(i=0; i<NUM-1; i++) {
                if(sub[i]==sub[i+1]) {Get_number(p);}
        }
        //全ての整数が異なるときにGet_number関数を抜けて
        //くれないことをわかりやすくするために書いてます。
        printf("******\n");
        return;
}

void Make_card(int p1[], int p2[][WIDTH])
{
        int i,j;
        int k=0;

        for(i=0; i<WIDTH; i++) {
                for(j=0; j<WIDTH; j++) {
                        p2[i][j]=p1[k];
                        k++;
                }
        }
        return;
}

void Print_card(int p3[][WIDTH])
{
        int i,j;

        for(i=0; i<WIDTH; i++) {
                for(j=0; j<WIDTH; j++) {
                        printf("%d\t", p3[i][j]);
                }
                printf("\n");
        }
        return;
}

 補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 4

+1

通常はBINGOの各文字について5-5-4-5-5抽選すればいいだけです
(Nは最初から真ん中を抜くのが基本だから)
配列を用意してシャッフルして頭から上記数量抽出すればいいでしょう

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+1

すべての数値が互いに異なるとき、Get_number関数を再帰的に呼び出さずにGet_number関数を抜けるはず

この部分に偽りはないのですが、

for(i=0; i<NUM; i++) {p[i]=rand()%20+1;}

ランダムに作った場合、理論的には (20!/20^20) = 1.16e-9 くらいの果てしなく少ない確率でしか起こりません。
単純に、すでリストにある番号だったら除外して再度ランダムな値をとるようにしてリストを作ったほうが早いと思いますよ。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/07/02 14:50

    訂正。N=9なので、(20!/(20-9)!)/20^9 = 0.065 くらいですね。まぁ、何度も繰り返したら起こりうるくらいだとは思いますが、再帰呼び出しがStackOverflowしないか気になります。

    キャンセル

  • 2018/07/02 16:45

    回答ありがとうございます。あまり考えてませんでしたが中々に低い確率だったので違う書き方を試してみます...ありがとうございました。

    キャンセル

0

ちゃんと抜けて、終了しますよ。

重複無くなるまでひたすら再帰呼び出しというのがいまいちなので、ループにしましょう。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/07/02 14:29 編集

    ああ、何度か実行するといつまでも続くことが多いですね。
    if(sub[i]==sub[i+1]) {Get_number(p);break;}
    でしょう。

    キャンセル

  • 2018/07/02 14:33

    他の回答にもありますが、そもそも効率の悪い方法です。
    × 数を全部作ってから1組でも一致すれば全部やり直し
    ○ 数を作る度に一致を調べ、一致すればそれだけやり直し

    キャンセル

  • 2018/07/02 14:40

    最初、2回実行して、2回ともそう長くならずに終了したのは、確率的にレアだったのかな。

    キャンセル

  • 2018/07/02 16:31

    回答ありがとうございます。break抜けは全く気づきませんでした; ; 。```for(i=0; i<NUM; i++) {p[i]=rand()%20+1;}```のところを重複がないかを確かめながら配列に入れていったほうが...ということですよね。それで作ってみます。ありがとうございました。

    キャンセル

0

まず乱数を必要個数生成したから、重複の有無を調査して、重複があれば再生成というアルゴリズムですね。これだと平均5~10回(ちゃんと計算していません)は生成しなおさないと重複のない数字列が得られません。

パッと見て間違ってるのはget_numberを再帰呼び出ししたあとも重複チェックを続けてるところです。なので2か所で重複がある場合無意味にもう一度Get_numberを呼び出してしまいます。

                if(sub[i]==sub[i+1]) {Get_number(p);}


ここはbreakを入れてループを抜けないといけません。

                if(sub[i]==sub[i+1]) {Get_number(p);break;}


ただしこれは効率が悪いだけで番号が生成できないわけではないですね。私のところで上のソースをコンパイルしたら出力はちゃんと出ました。乱数の巡り合わせが悪いと再起呼び出しの深さが深くなりすぎてエラーで止まります(無限にスタックがあればいつかは止まるはず)。

生成した数値に重複が2組(あるいは同じ数字が3回以上)あれば、余分な再帰が起きます。そしてその再帰でまた生成した数値に重複が2組あるとさらに無駄な再帰が・・・ということ。

あるいは、再帰ではなくてやり直しループで書くのも手です。できるだけ今のコードを生かすならば、こんなかんじ。

for(;;){
for(i=0; i<NUM; i++) {p[i]=rand()%20+1;}
for(i=0; i<NUM; i++) {sub[i]=p[i];}
//昇順にソートして全て異なる数字かを判定する
for(i=0; i<NUM-1; i++) {
for(j=i+1; j<NUM; j++) {
if(sub[i]>sub[j]) {buffer=sub[i]; sub[i]=sub[j]; sub[j]=buffer;}
}
}
for(i=0; i<NUM; i++) {printf("%d ", sub[i]);}
printf("\n");
for(i=0; i<NUM-1; i++) {
if(sub[i]==sub[i+1])break;
}
//全ての整数が異なるとき
if(i==NUM-1) return;
}

あと、subだけ並び替えてpをそのまま返していますが、通常のビンゴカードは小さい順に数が並び替えられていませんか?

なお、
1~Nまでの数からランダムにM個(重複しないで)選ぶよくある手段は次のようなものです。

int x[N];
int i,j;
//1~Nの数を配列に入れる
for(i=0;i<N;i++) 
  x[i]=i+1;

for(i=0;i<M;i++) {
  j= iからN-1までの整数が出てくる一様乱数();
 x[i]とx[j]を入れ替える;
}
x[0]~x[M-1]をソートする;

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/07/02 16:42

    回答ありがとうございます。再起慣れしてないからというのもあるかもしれませんが、1度再起されたらそのfor文は抜けるものと勘違いしてたようです。(break書かないとですね)最後に紹介していただいたのがよくわからないのでもう少し考えてみます。ありがとうございました。

    キャンセル

  • 2018/07/03 16:07

    1~20のカードが1枚づつあるとき、カードを一つ選んで束から抜く、ということ9回繰り返せば、重複無く1~20の中から数を9個選べます。これを一つの配列上でやっているわけです。0~i-1が選択済みのカード、i~N-1がこれからランダムに選ぶ候補カード。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.53%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

  • 解決済

    ビット演算を使った配列の初期化

    こんにちは 現在52枚のカードを使ったゲームを作成しています。 Sはスペード、Hはハート、Dはダイヤ、Cはクラブというようにアルファベット一文字を絵柄に見立てています。 下記

  • 受付中

    解決策が分かりません・・・

    5人の点数をキーボードから読み込んで、合計点、平均点、最高点、最低点を表示したいのですが(点数は0以上100以下)、 実行例 5人の点数を入力してください。 1番:95 2

  • 解決済

    結果の表示について

    課題で、 キーボードから入力された数値の平均を計算して表示し、平均以上の数値、平均より小さい数値を表示するプログラムを考えているのですが、 実行例 ./a.out

  • 解決済

    fgetsを使った文字列の分割

    前提・実現したいこと AOJ 1_5Aの問題で、よくないとされるscanf以外を使用した解決を図りたいです。 問題内容は、 トランプの枚数が足りないので現在持っているカードを入

  • 受付中

    プログラムを見やすく改良したい

    正常に動くプルグラムを見やすく改良したい。 具体的に教えていただければありがたいです。セグメンテーションフォルトでベスト7まで表示して停止します。173行あたりだと思うのですが、よ

  • 解決済

    C言語で上手く実行してくれないのですが…

    前提・実現したいこと C言語で自分の腕試しに簡易的なカードゲームを作ろうとしています。 発生している問題・エラーメッセージ コンパイルは問題なく出来ましたが、肝心の起動が上手く

  • 解決済

    C言語のソートプログラムについて

    前提・実現したいこと C言語で配列を昇順に入れ替えて、処理の前後の結果を表示するコードを書いています。 結果としては、以下のように表示されることを考えています。 a = [9,

  • 解決済

    c言語 入門レベル問題 配列の数字を並び替え

    配列要素1,2,3,4,5 を5,4,3,2,1に出せたいですが、何故か5,4,4,4,4になってしまいましたか? #define N 5 //配列の要素数 int main

同じタグがついた質問を見る

  • C

    3657questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。