プログラムを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;
}
この手の問題は、アルゴリズムの考え方一つだと思います。
今回は、異なる数値しか使わないということは、あらかじめ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 ); }
ご参考まで
#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で一度使った数字はもう使わないのでひとつ詰めます。
この手順の問題は詰めるためのコストです。
詰めないで空白などに置き換えて、空白ならもう一度やり直すと言う手もあるのですが、最悪、終了しないことも有りうる(乱数なのでそんなことはないが)ということで詰める方法にしました。
なるべく踏襲して(これって動きました?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
補足もあると思うのでコメントをオープンしてください。
決してキレイとはいえない。
#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; }
同じ数字で構成されないだけならば、これでいいでしょう。
#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...
こんな感じでどうでしょう。
#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 みたいな乱数の使い方はあまりよくありません。
すんません、遊ばせてもらってます。
コンパイル通らない環境もあるかもしれませんが。
#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桁固定と解釈し、マジックナンバー多数。良い子はまねしない。
値の入替えも謎なコードになってます。良い子は絶対まねしない。
最初のは効率悪すぎました。ということでアルゴリズムは既出ですが、短さを追求してリベンジ。
#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 )] ); }
でもコードは汚いなぁ。
#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 が参考になると思います.
コメント(18件)
質問のままのソースでも十分だと私は思います。
日本語のコメントをいれるとそれらしくなるとは思います。
フラグを使わず、goto文を使ったほうがいいケースなのかも
しれませんが、使わないほうが良いでしょうね。
少々汚くても、処理が追えること、アルゴリズムがわかる
方がよいかと。
業務プログラムで使う方には、理由は告げずに、現場からご退場していただいております。
でも反省点も少し・・・。
最初の
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 )
と並ぶ傑作です。
コーディング規約に goto文を使わないことと書いてるかと。
必ず「goto をつかわない」とあるとはいえないと思いますが、なくても
使わない方がいいでしょうね。
使わない方が良いのは「業務だから」ではなく、
プログラム(可読性・信頼性・拡張性)への悪影響があるからでしょう。
それは業務でなくとも、スタイルとして身につけた方が良いと思います。
洗練されたコードは処理の流れもわかりやすいと思いますし、ほとんどは
それと同時に可読性も良いように思います。
これは相反するものではなく、どちらかといえば同一ベクトル上にあるもの
ではないでしょうか。
そういう意味で言えば、後に私が提示したのは非常に悪い例ですね^^;;。
極端な意見かもしれませんが、goto があるプログラムは、どのような理由
があれ読みやすいとは思えません。
それを使いたいと思ったときには、ほとんどの場合、構造を見直すことで
よりよい形に出来るように思います。
今日は暑いせいか、ちょっと熱くなってしまった。
愚見、失礼しました。
http://qwik.jp/senna/cstyle.html
■なぜ、C言語にはgotoが必要なのか?
http://d.hatena.ne.jp/tanakh/20041002
まあ、後者の場合でも、私は、
仕事で書くコードではgotoを使わない派でしょうね。
いい気になって楽しようと、goto 使いまくってました。
それも企業年金の重要なプログラムで、ある日トラブルが発生して原因調査をしたのですが・・・
直るには直ったのですが、その修正に確信が持てませんでしたね。
なんせ、5千ステップ級の本物のスパゲッティ状態だったので、そのとき本気で反省しました。安易にGOTO 使うんじゃなかったと。
みなさんどうもありがとうございました。
とても参考になったものや
これから解読が必要なものなどいろいろですが、
予想以上の回答があって、とても嬉しいです。
また同じような質問をするかもしれませんが、
よろしくお願いします。
アルゴリズムの設計に時間をかけられるようになれば、プログラマとして
一段の成長です。
とかいいながら、はずかしい間違いがもう一つ・・・
最初の回答の関数は swapNum のつもりでした。
Swat ってどこぞの狙撃部隊みたいだな・・・。
>あと、あげあし取りで申し訳ないですが、 pyopyopyoさんの
>char digits[10] = "0123456789";
>は、配列のサイズ11にするか
今回の場合はサイズは10のままで問題ないと思います.
アルゴリズム上 文字列の終端の '\0' は不要ですので.
C の規格では、変数サイズが小さい場合でも終端の'\0' だけは特別な
扱いとして、代入から除外されるのですね。
今回の件に関して、初心者の方には下記が参考になるかと思います。
http://www.st.rim.or.jp/~phinloda/cqa/cqa2.html
Q 【サイズを指定した配列の初期化】
>char digits[10] = "0123456789";
・・・
>今回の場合はサイズは10のままで問題ないと思います.
は、間違いですねぇ。
1バイト分、次の変数の領域を破壊します。(BC5で確認)
C99の規格を確認しましたが、確かに'\0'を省略したリテラルの初期化が記述されていますし、手持ちの Boaland C 5.5 では、この記述は正常な挙動をしているようです。
古いコンパイラあるいは、この規格に準拠していないコンパイラを使用していれば、領域破壊が発生するケースもあるかと思います。
先のコメントで提示した参照も96年の資料ですので、C89かC95からそのような仕様になったのでしょう。
C の当初の規格からそうであったら、まさに「勉強不足でした」ということになりますが。
なので古くからCをやっている人にとっては、違和感のある記述ですね。
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 に準拠していないんですかね?
const char digits[] = "0123456789"; という話は出てこないのでしょうか?
※スマソ,BC3.1(PC98) での確認でした。BC5(DOSV)は正常に動作しますねぇ。
ANSI 自体も改訂を重ねており、初版が C89(1989年)、C90、C95、C99と続きます。どこか途中の版から変わったと思っていたのですが、
(先のコメントは英語を誤解してたので、書き直しました^^;;。)
K&Rの第2版(ANSI 準拠 1989年発行)を確認してみたら、しっかり初版からの変更点として書かれていました。
ということで ANSI としては、最初からこの仕様はあったようです。
まことにもって、勉強不足でした。
pyopyopyo さん、失礼しました。
「1~Nの要素について順次乱数で決定した要素と内容を交換」するアルゴリズム
になっていますが、交換相手の求め方が良くない(乱数を常に1~Nの全域で計算
している)ため、各要素に対する各値の出現確率が均等になりません。
以下のような感じで改善。(比較のため あえて rand() % Nの形)
rand_num = rand() % numlen;
↓
rand_num = rand() % (numlen-i)+i;