同じ数字を使わないランダムな4桁の整数を表示する

プログラムをC言語でスッキリ書いて欲しいです。

自分で書くとどうしてもフラグを使うような
ちょっと汚いプログラムになってしまいます。

参考までに僕が書いたプログラムを載せておきます。

-----

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

#define DIGIT 4

int main(void){
int i, j;
int pos = 0;
int num[3];
int rand_num;
int flag = 0;

srand((unsigned)time(NULL));

while(pos < DIGIT) {
rand_num = rand() % 10;
for(i = 0; i < pos; i++) {
if (num[i] == rand_num) {
flag = 1;
break;
}
}

if (flag == 0) {
num[pos] = rand_num;
pos++;
} else {
flag = 0;
}
}

for(j = 0; j < DIGIT; j++) {
printf("%d", num[j]);
}
printf("\n");

return 0;
}

回答の条件
  • URL必須
  • 1人2回まで
  • 登録:
  • 終了:2007/08/03 21:40:14
※ 有料アンケート・ポイント付き質問機能は2023年2月28日に終了しました。

回答9件)

id:Mook No.1

回答回数1314ベストアンサー獲得回数393

ポイント16pt

この手の問題は、アルゴリズムの考え方一つだと思います。


今回は、異なる数値しか使わないということは、あらかじめ0~9の数値を用意して、それをランダムに入れ替え先頭から必要な桁数を表示するというアルゴリズムにしてみました。

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

#define DIGIT           4           /* DIGIT は9以下 */
#define SHUFFLE_NUM     1000        /* 入れ替え回数 */

/*-------------------------------------------------------
 * 配列 num の x 番目のデータと y 番目のデータを入れ替え
 *------------------------------------------------------*/
static void swatNum( char *num, int x, int y ) {
    char tmp;
    tmp = num[x];
    num[x] = num[y];
    num[y] = tmp; 
}
/*-------------------------------------------------------
 * DIGIT 桁数の異なる数値を出力
 *------------------------------------------------------*/
void main(void){
    char num[11] = "0123456789";
    int i;

    srand((unsigned)time(NULL));
    for ( i=0 ; i<SHUFFLE_NUM ; i++ )
        swatNum( num, rand() % 10, rand() % 10 );
    num[DIGIT] = NULL;
    printf("%s\n", num );
}

ご参考まで

http://www1.cts.ne.jp/~clab/algorithm/algorithm.html

id:tobeoscontinue No.2

回答回数220ベストアンサー獲得回数59

ポイント16pt
#include<time.h>

#define DIGIT 4

int main(void){
char digits[] = {'0','1','2','3','4','5','6','7','8','9'}, num[5];
int n = 10;
int rand_num, i, m;

srand((unsigned)time(NULL));

for(i = 0; i < DIGIT; i++) {
 rand_num = rand() % n;
 num[i] = digits[rand_num];
 n--;
 for (m = rand_num; m < n; m++) digits[m] = digits[m+1];
}
num[DIGIT] = '\0';
printf("%s\n", num);
return 0;
}

0から9までの文字列を準備します。(digits)

rand_numで一度使った数字はもう使わないのでひとつ詰めます。

この手順の問題は詰めるためのコストです。

詰めないで空白などに置き換えて、空白ならもう一度やり直すと言う手もあるのですが、最悪、終了しないことも有りうる(乱数なのでそんなことはないが)ということで詰める方法にしました。

http://q.hatena.ne.jp/

id:castiron No.3

回答回数418ベストアンサー獲得回数30

ポイント16pt

なるべく踏襲して(これって動きました?numの配列の数が足りる?)


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

#define DIGIT 4

int main(void){
int i;
int use_num[9]={0,1,2,3,4,5,6,7,8,9}; //重複しないための配列
int num[4];
int rand_num;
int tmp;  //一時保存用
int last;  //配列の最後の位置

srand((unsigned)time(NULL));
/****************ここをループさせれば何個でも数字を作れる************/
last=9;
for(i=0;i<DIGIT;i++){
   rand_num=(int)rand()%10;
   //使う数字を配列の最後の数字と交換
        tmp=use_num[last];
        use_num[last]=use_num[rand_num];
        use_num[rand_num]=tmp; 
   //必ずlastの位置にある数字を格納するようにする(lastをデクリメントすることで以降使わない)
        num[i]=use_num[last--]; 
}
for(j = 0; j < DIGIT; j++) {
        printf("%d", num[j]);
}
printf("\n");
/****************ここをループさせれば何個でも数字を作れる************/
return 0;
} 

</pre>
	
	

http://q.hatena.ne.jp/1185539901

補足もあると思うのでコメントをオープンしてください。

id:dungeon-master No.4

回答回数571ベストアンサー獲得回数40

ポイント16pt

決してキレイとはいえない。

http://q.hatena.ne.jp/ダミー。

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

#define DIGIT 4

int main(void){

char num[]="0123456789";
int numlen=10;
char cur;
int i;
int rand_num;

srand((unsigned)time(NULL));

for( i=0 ; i< DIGIT ; i++ ){
  rand_num = rand() % numlen;
  cur=num[i];
  num[i]=num[ rand_num ];
  num[rand_num ]= cur;
}

num[DIGIT]=0;
printf("%s\n", num );
return 0;
} 
id:Vacuum No.5

回答回数55ベストアンサー獲得回数4

ポイント15pt

同じ数字で構成されないだけならば、これでいいでしょう。

#include <stdio.h>

#include <stdlib.h>

#include <time.h>

int main (int argc,char *argv[]) {

  int dec;

  char str[8];

  srand((unsigned)time(NULL));

  while(1) {

    dec = rand()%10000;

    sprintf(str,"%04d\n",dec);

    if((str[0] == str[1])||(str[0] == str[2])||(str[0] == str[3]) ||

      (str[1] == str[2])||(str[1] == str[3])||(str[2] == str[3])) {

      continue;

    }

    printf(str);

  }

  return (0);

}

http://ja.wikipedia.org/wiki/%E3%83%A1%E3%83%AB%E3%82%BB%E3%83%B...

id:SuikawariKengou No.6

回答回数1ベストアンサー獲得回数0

ポイント16pt

こんな感じでどうでしょう。

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

#define DIGIT 4

int main(void){
  int i, pos, tmp ;
  int num[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 } ;

  srand((unsigned)time(NULL)) ;
  for (i = 9; i >= 0; --i) {
    tmp = num[i] ;
    pos = num[(int)((float)(i+1) * rand() / RAND_MAX)] ;
    num[i] = num[pos] ;
    num[pos] = tmp ;
  }

  for (i = 0; i < DIGIT; ++i) {
    printf("%d", num[i]);
  }
  printf("\n");

  return 0;
}

それほど影響は無いかもしれませんが、 rand() % 10 みたいな乱数の使い方はあまりよくありません。

Manpage of RAND

id:dungeon-master No.7

回答回数571ベストアンサー獲得回数40

ポイント15pt

すんません、遊ばせてもらってます。

コンパイル通らない環境もあるかもしれませんが。

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

int main(void){

char num[]="0123456789";
int i, k;

srand((unsigned)time(NULL));

for( i=0 ; i<4 ; i++ ){
  k = rand() % 10;
  if( k!=i ) num[i]^=num[k]^=num[i]^=num[k];
}

printf("%4.4s\n", num );
return 0;
}

4桁固定と解釈し、マジックナンバー多数。良い子はまねしない。

値の入替えも謎なコードになってます。良い子は絶対まねしない。

http://q.hatena.ne.jp/ダミー

id:Mook No.8

回答回数1314ベストアンサー獲得回数393

ポイント15pt

最初のは効率悪すぎました。ということでアルゴリズムは既出ですが、短さを追求してリベンジ。

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

#define DIGIT   4    /* DIGIT は10以下 */

void main(void){
    char num[11] = "0123456789";
    int i, x;
    srand((unsigned)time(NULL));

    for( i=0 ; i<DIGIT ; num[x] = num[9-i++] )
       printf("%c", num[x = rand() % ( 10 - i )] );
}

でもコードは汚いなぁ。

http://ja.wikipedia.org/wiki/C%E8%A8%80%E8%AA%9E

id:pyopyopyo No.9

回答回数377ベストアンサー獲得回数98

ポイント15pt
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define NUM 4
#define EMPTY -1

int
main()
{
    char digits[10] = "0123456789";
    int i;

    srand(time(NULL));

    for (i=0; i<NUM; ++i) {
	int j = rand()%10;
	while (EMPTY==digits[j])
	    j=(j+1)%10;
	printf("%c", digits[j]);
	digits[j]=EMPTY;
    }
    printf("\n");

    return 0;
}

tobeoscontinue さんと同じような発想ですが,数字の選び方を工夫してあります.


あと蛇足ですが,他の方も指摘されているように,一般に rand() % 10 のような書き方は良くないとされています.詳細は

http://ja.wikipedia.org/wiki/Rand が参考になると思います.

  • id:KUROX
    アルゴリズムを変えないのなら、
    質問のままのソースでも十分だと私は思います。

    日本語のコメントをいれるとそれらしくなるとは思います。
    フラグを使わず、goto文を使ったほうがいいケースなのかも
    しれませんが、使わないほうが良いでしょうね。

    少々汚くても、処理が追えること、アルゴリズムがわかる
    方がよいかと。
  • id:virtual
    定義の問題ですが、最上位に0(ゼロ)があっても4桁の整数とみなすのかな?
  • id:Vacuum
    C言語関連の質問(お題目)は、みなさんこだわりがあるようで、勉強になります。ちなみに、goto は使えるけど使ってはいけません。
    業務プログラムで使う方には、理由は告げずに、現場からご退場していただいております。
  • id:Mook
    最近は C を使わなくなりましたが、やはり面白いですね。
    でも反省点も少し・・・。

    最初の
     num[DIGIT] = NULL;
    は tobeoscontinue さんのように
     num[DIGIT] = '\0';
    が正しいですね。

    後の
    printf("%c", num[x = rand() % ( 10 - i )] );
    も putchar() を使うべきところですね。その方がアセンブラのステップ数としてはずっと短いはずです。
    (最近はみんなこんなの気にしないんでしょうね、きっと。)

    あと、あげあし取りで申し訳ないですが、 pyopyopyoさんの
    char digits[10] = "0123456789";
    は、配列のサイズ11にするか、tobeoscontinue さんのように
    char digits[10] = {'0','1','2','3','4','5','6','7','8','9'};
    とするかですね。


    dungeon-master さんの
     num[i]^=num[k]^=num[i]^=num[k];
    は久しぶりに考えました。古き良き(悪しき?)コードですね。 個人的には
    #define UCaseASC(x) ( x & 0xDF )
    #define LCaseASC(x) ( x | 0x20 )
    と並ぶ傑作です。
  • id:KUROX
    業務の場合は、
    コーディング規約に goto文を使わないことと書いてるかと。
  • id:Mook
    コーディング規約は開発グループや会社によっても異なるでしょうから、
    必ず「goto をつかわない」とあるとはいえないと思いますが、なくても
    使わない方がいいでしょうね。

    使わない方が良いのは「業務だから」ではなく、
    プログラム(可読性・信頼性・拡張性)への悪影響があるからでしょう。
    それは業務でなくとも、スタイルとして身につけた方が良いと思います。

    洗練されたコードは処理の流れもわかりやすいと思いますし、ほとんどは
    それと同時に可読性も良いように思います。
    これは相反するものではなく、どちらかといえば同一ベクトル上にあるもの
    ではないでしょうか。

    そういう意味で言えば、後に私が提示したのは非常に悪い例ですね^^;;。

    極端な意見かもしれませんが、goto があるプログラムは、どのような理由
    があれ読みやすいとは思えません。
    それを使いたいと思ったときには、ほとんどの場合、構造を見直すことで
    よりよい形に出来るように思います。

    今日は暑いせいか、ちょっと熱くなってしまった。

    愚見、失礼しました。
  • id:KUROX
    ■gotoを許可しているコーディングルール
    http://qwik.jp/senna/cstyle.html
    ■なぜ、C言語にはgotoが必要なのか?
    http://d.hatena.ne.jp/tanakh/20041002

    まあ、後者の場合でも、私は、
    仕事で書くコードではgotoを使わない派でしょうね。
  • id:Vacuum
    もう、20年も前になるでしょうか。コボルプログラマだったころ
    いい気になって楽しようと、goto 使いまくってました。
    それも企業年金の重要なプログラムで、ある日トラブルが発生して原因調査をしたのですが・・・
    直るには直ったのですが、その修正に確信が持てませんでしたね。
    なんせ、5千ステップ級の本物のスパゲッティ状態だったので、そのとき本気で反省しました。安易にGOTO 使うんじゃなかったと。
  • id:kanouk
    まとめてで申し訳ありませんが、
    みなさんどうもありがとうございました。

    とても参考になったものや
    これから解読が必要なものなどいろいろですが、
    予想以上の回答があって、とても嬉しいです。

    また同じような質問をするかもしれませんが、
    よろしくお願いします。

  • id:Mook
    頭の体操代わりなので、宿題の丸投げでなければこの手の質問は歓迎です。

    アルゴリズムの設計に時間をかけられるようになれば、プログラマとして
    一段の成長です。

    とかいいながら、はずかしい間違いがもう一つ・・・
    最初の回答の関数は swapNum のつもりでした。
    Swat ってどこぞの狙撃部隊みたいだな・・・。
  • id:pyopyopyo
    Mookさんのコメントの以下の部分ですが

    >あと、あげあし取りで申し訳ないですが、 pyopyopyoさんの
    >char digits[10] = "0123456789";
    >は、配列のサイズ11にするか

    今回の場合はサイズは10のままで問題ないと思います.
    アルゴリズム上 文字列の終端の '\0' は不要ですので.
  • id:Mook
    なるほど、こちらの勉強不足でした。

    C の規格では、変数サイズが小さい場合でも終端の'\0' だけは特別な
    扱いとして、代入から除外されるのですね。

    今回の件に関して、初心者の方には下記が参考になるかと思います。
    http://www.st.rim.or.jp/~phinloda/cqa/cqa2.html
    Q 【サイズを指定した配列の初期化】
  • id:Vacuum
    >あと、あげあし取りで申し訳ないですが、 pyopyopyoさんの
    >char digits[10] = "0123456789";
    ・・・
    >今回の場合はサイズは10のままで問題ないと思います.

    は、間違いですねぇ。
    1バイト分、次の変数の領域を破壊します。(BC5で確認)
  • id:Mook
    今回の件の難しいことは、言語の仕様そのものが変遷しているということです。
    C99の規格を確認しましたが、確かに'\0'を省略したリテラルの初期化が記述されていますし、手持ちの Boaland C 5.5 では、この記述は正常な挙動をしているようです。

    古いコンパイラあるいは、この規格に準拠していないコンパイラを使用していれば、領域破壊が発生するケースもあるかと思います。

    先のコメントで提示した参照も96年の資料ですので、C89かC95からそのような仕様になったのでしょう。
    C の当初の規格からそうであったら、まさに「勉強不足でした」ということになりますが。

    なので古くからCをやっている人にとっては、違和感のある記述ですね。
  • id:pyopyopyo
    気になったので調べてみました.

    http://c-faq.com/ansi/nonstrings.html

    によると

    Q: Is char a[3] = "abc"; legal? What does it mean?
    A: It is legal in ANSI C (and perhaps in a few pre-ANSI systems),
    :

    :
    References: ISO Sec. 6.5.7

    とのことです.ANSI C で仕様として定められているみたいです.

    BC5は ANSI C に準拠していないんですかね?
  • id:Vacuum
    話を戻してすいませんが・・・
    const char digits[] = "0123456789"; という話は出てこないのでしょうか?
    ※スマソ,BC3.1(PC98) での確認でした。BC5(DOSV)は正常に動作しますねぇ。
  • id:Mook
    先に書いた、Cxx というのはいずれも ANSI 規格のことです。
    ANSI 自体も改訂を重ねており、初版が C89(1989年)、C90、C95、C99と続きます。どこか途中の版から変わったと思っていたのですが、
    (先のコメントは英語を誤解してたので、書き直しました^^;;。)

    K&Rの第2版(ANSI 準拠 1989年発行)を確認してみたら、しっかり初版からの変更点として書かれていました。
    ということで ANSI としては、最初からこの仕様はあったようです。

    まことにもって、勉強不足でした。
    pyopyopyo さん、失礼しました。
  • id:dungeon-master
    私が付けた回答4の方法は、実は厳密に考えると交換結果に偏りがあります。
    「1~Nの要素について順次乱数で決定した要素と内容を交換」するアルゴリズム
    になっていますが、交換相手の求め方が良くない(乱数を常に1~Nの全域で計算
    している)ため、各要素に対する各値の出現確率が均等になりません。

    以下のような感じで改善。(比較のため あえて rand() % Nの形)
    rand_num = rand() % numlen;

     rand_num = rand() % (numlen-i)+i;

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

回答リクエストを送信したユーザーはいません