
Ubuntu日本語フォーラム

ログインしていません。
つい最近始めてubuntuをインストールして使用し始めました。linuxそのものが初めてで、分からないことだらけなのですがよろしくお願いします。
cもまだ勉強を始めたばかりなのですが、今までwindowsでborlandを使用してコンパイルしていました。今回ubuntuにて、以前に作ったプログラムが動くかためしてみようと思い、gccでコンパイルしてみました。
内容はビンゴゲームで乱数発生→ビンゴカード作成→開票といった手順です。
ターミナルでgccを使ってみました。すると、コンパイルは成功したようで実行できるファイル(a.out)が作成されました。そのファイルを実行すると実際にビンゴができたのですが、ここで問題が起きました。
重複のない乱数を発生してビンゴカードを作成したはずが、まったく乱数になっておらず連続した数字がカードに代入されています。
おかしいと思い同じソースコードをwindowsのほうでコンパイルしてみるとまったく問題なく乱数を発生できます。
詳しい内容を説明することができなくて申し訳ないのですが、おそらく乱数の発生にはrand関数とtime関数を用いています。
インクルードしているのはstdio.h stdlib.h time.h math.hです。
ターミナルにてgcc test.c とコマンドを入力してコンパイルしています。 -lmをつけても変わりませんでした。
自分なりに調べてみてこれが問題では?と思ったのは以下のものです。
・libraryがインストールされていない?ーーーでもコンパイルは通る
・コマンド(オプションの付け方等)が間違っている?
・これ以外にもインストールしなくてはならないものがある?
といろいろ考えてみたものの、何をすればよいのかまったく分からない状態です。いろいろ検索して調べて見ましたが、まったく分かりませんでした。
これからは、linuxでプログラミングの勉強をしていきたいと思っています。どうかみなさま御教授ください。よろしくお願いします。
オフライン
cogito 様へ.
私は C 言語には詳しくないので,回答ではないのですが,
ソースや端末で入力した内容を貼り付けると回答者の方も
回答しやすくなると思います.
公開できないソースだとしても部分的(関数を使っている行だけ等)な
ものであれば,公開できると思います.
ソースが長いものであれば,
http://paste.ubuntu.com/
などのサービスを利用して,その URL を貼り付けると良いと思います.
また,今回の問題に限ったことではないのですが,
システムの環境(OS のバージョンやマシンの性能)に依存する
問題の場合もありますので,環境も示しておくと良いと思います.
オフライン
hellfire様 返信ありがとうございます。
確かにおっしゃるとおりでした。環境等を示さずに質問してしまうとは、本当に何もわかっておらず恥ずかしい限りです。
ubuntu 11.04 usbメモリにインストールしてブートしています。
インストールしたばかりで、特にソフトウェア等は入れていません。
以下にソースコードを貼り付けさせて頂きます。非常に稚拙で申し訳ないのですが目を通していただけると助かります。
このソースコードをwindowsの環境でコンパイルするときちんとビンゴカードに乱数が代入されますが、ubuntu でコンパイルするとビンゴカードに26~の数字が順番に代入されます。ちなみに、3列揃って勝ちというルールになっています。
もし可能であれば、皆様の環境でコンパイル、実行していただき、きちんと乱数が代入されているか見ていただきたいのです。また、その時にターミナルで入力したコマンドも教えていただけると幸いです。
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#include<time.h>
int make(void);
int main(void){
int a[5][5];
int b[5][5];
int A[99];
int B[99];
int random=0;
int i,j,k;
int x=0;
int yoko[]={0,0,0,0,0};
int tate[]={0,0,0,0,0};
int naname1=0;
int naname2=0;
int cashe=0;
int reacha=0;
int reachb=0;
int bingoa=0;
int bingob=0;
int hanteia[5][5]={0};
int hanteib[5][5]={0};
int number1[99];
int number2[99];
char f;
while(1){
bingoa=0;
bingob=0;
x=0;
for(i=0;i<5;i++){
for(k=0;k<5;k++){
hanteia[i][k]=0;
hanteib[i][k]=0;
}
}
printf("\nstart---press s finish---press f\n");
scanf(" %c",&f);
if(f=='s'){ //sだった場合 ゲーム開始
srand((unsigned int)time(NULL)); //seedを決める。
for(i=0;i<99;i++){
A[i]=i+1; //Aに1~99代入
B[i]=make();
}
for(i=1;i<100;i++){
//バブルソート
for(k=0;k<99-i;k++){
if(B[99-i]<B[98-i-k]){
cashe=B[99-i];
B[99-i]=B[98-i-k];
B[98-i-k]=cashe;
cashe=A[99-i];
A[99-i]=A[98-i-k];
A[98-i-k]=cashe;
}
}
} //バブルソート終了
for(i=0;i<5;i++){ //ビンゴカード作成
for(k=0;k<5;k++){
a[i][k]=A[25+k+5*i];
} //for-kの終了{
} //for-iの終了{
for(i=0;i<99;i++){
A[i]=i+1; //Aに1~100代入
B[i]=make();
}
for(i=1;i<100;i++){
//バブルソート
for(k=0;k<99-i;k++){
if(B[99-i]<B[98-i-k]){
cashe=B[99-i];
B[99-i]=B[98-i-k];
B[98-i-k]=cashe;
cashe=A[99-i];
A[99-i]=A[98-i-k];
A[98-i-k]=cashe;
}
}
}
for(i=0;i<5;i++){ //ビンゴカード作成
for(k=0;k<5;k++){
b[i][k]=A[25+k+5*i];
} //for-kの終了{
} //for-iの終了{
for(i=0;i<99;i++){
number1[i]=i+1; //numberに1~99代入
number2[i]=make();
}
for(i=1;i<100;i++){
//バブルソート
for(k=0;k<99-i;k++){
if(number2[99-i]<number2[98-i-k]){
cashe=number2[99-i];
number2[99-i]=number2[98-i-k];
number2[98-i-k]=cashe;
cashe=number1[99-i];
number1[99-i]=number1[98-i-k];
number1[98-i-k]=cashe;
}
}
} //バブルソート終了
a[2][2]=0;
b[2][2]=0; //真ん中に0を代入
printf("\n\n\nplayer 1\n\n");
for(i=0;i<5;i++){ //aのビンゴカード表示
for(k=0;k<5;k++){
printf("%2d ",a[i][k]);
}
printf("\n");
} //aの表示の終了{
printf("\n\n\nplayer 2\n\n");
for(i=0;i<5;i++){ //bのビンゴカード表示
for(k=0;k<5;k++){
printf("%2d ",b[i][k]);
}
printf("\n");
} //bの表示の終了{
while(1){ //ビンゴ開始
hanteia[2][2]=1;
hanteib[2][2]=1;
printf("\nget a number---press n reset---press r finish---press f\n");
scanf(" %c",&f);
if(f=='n'){
bingoa=0;
bingob=0;
random=number1[x];
printf("\nnumber=%d",random);
for(i=0;i<5;i++){
for(k=0;k<5;k++){
if(a[i][k]==random){
a[i][k]=0;
hanteia[i][k]=1;
} //判定aの終了{
if(b[i][k]==random){
b[i][k]=0;
hanteib[i][k]=1;
} //判定bの終了{
} //for-kの終了{
} //for-iの終了{
//aリーチビンゴ判定開始
for(i=0;i<5;i++){
tate[i]=0; //縦等の初期化
yoko[i]=0;
}
naname1=0;
naname2=0;
reacha=0;
for(i=0;i<5;i++){
for(k=0;k<5;k++){
tate[i]+=hanteia[i][k];
yoko[i]+=hanteia[k][i];
} //for-kの終了{
} //for-iの終了{
naname1=hanteia[0][0]+hanteia[1][1]+hanteia[2][2]+hanteia[3][3]+hanteia[4][4];
naname2=hanteia[4][0]+hanteia[3][1]+hanteia[2][2]+hanteia[1][3]+hanteia[0][4];
for(i=0;i<5;i++){
if(tate[i]==4){
reacha+=1;
}
if(yoko[i]==4){
reacha+=1;
}
} //for-iの終了{
if(naname1==4){
reacha+=1;
}
if(naname2==4){
reacha+=1;
}
//aのリーチ数終了
//aのビンゴ判定開始
for(i=0;i<5;i++){
if(tate[i]==5){
bingoa+=1;
}
if(yoko[i]==5){
bingoa+=1;
}
} //for-iの終了{
if(naname1==5){
bingoa+=1;
}
if(naname2==5){
bingoa+=1;
}
//aのビンゴ判定終了
//bリーチ判定開始
for(i=0;i<5;i++){
tate[i]=0; //縦等の初期化
yoko[i]=0;
}
naname1=0;
naname2=0;
reachb=0;
for(i=0;i<5;i++){
for(k=0;k<5;k++){
tate[i]+=hanteib[i][k];
yoko[i]+=hanteib[k][i];
} //for-kの終了{
} //for-iの終了{
naname1=hanteib[0][0]+hanteib[1][1]+hanteib[2][2]+hanteib[3][3]+hanteib[4][4];
naname2=hanteib[4][0]+hanteib[3][1]+hanteib[2][2]+hanteib[1][3]+hanteib[0][4];
for(i=0;i<5;i++){
if(tate[i]==4){
reachb+=1;
}
if(yoko[i]==4){
reachb+=1;
}
} //for-iの終了{
if(naname1==4){
reachb+=1;
}
if(naname2==4){
reachb+=1;
}
//bのリーチ数終了
for(i=0;i<5;i++){
if(tate[i]==5){
bingob+=1;
}
if(yoko[i]==5){
bingob+=1;
}
} //for-iの終了{
if(naname1==5){
bingob+=1;
}
if(naname2==5){
bingob+=1;
}
//bのビンゴ判定終了
printf("\n\n\nplayer 1\n\n");
for(i=0;i<5;i++){ //aのビンゴカード表示
for(k=0;k<5;k++){
printf("%2d ",a[i][k]);
}
printf("\n");
} //aの表示の終了{
printf("\n\n\nplayer 2\n\n");
for(i=0;i<5;i++){ //bのビンゴカード表示
for(k=0;k<5;k++){
printf("%2d ",b[i][k]);
}
printf("\n");
} //bの表示の終了{
if(bingoa>=3||bingob>=3){ //ビンゴ表示判定開始
if(bingoa>=3){
printf("player 1---bingo!\n");
} //ifの終了{
if(bingob>=3){
printf("player 2---bingo!\n");
} //ifの終了{
if(bingoa>=3&&bingob<3){
printf("\nplayer 1 is the winner!!\n");
} //ifの終了{
if(bingob>=3&&bingoa<3){
printf("\nplayer 2 is the winner!!\n");
} //ifの終了{
if(bingoa>=3&&bingob>=3){
printf("\nthe game ended in a draw!!");
} //ifの終了
break;
} //ビンゴ表示判定終了
else {
if(bingoa==2||bingob==2){
if(bingoa==2&&reacha!=0){
printf("player 1---one more to go!\n");
} //aのone more to go
if(bingob==2&&reachb!=0){
printf("player 2---one more to go!\n");
} //bのone more to go
} //ifの終了
} //else {
x++;
} //if-nの終了{
if(f=='r'){
break;
} //if-rの終了{
if(f=='f'){
exit(0);
} //if-fの終了{
} //whileとじ{
} //ifでsの場合の終了{
else if(f=='f'){ //if-fだった場合に終了
exit(0);
} //else if の終了{
} //while終了{
return 0;
} //mainの終了{
int make(void){ //乱数発生関数
return 1+(int)(rand()*(99-1+1)/(1+RAND_MAX)); //乱数発生
} //乱数発生関数の終了{
オフライン
すみません。
マシンの環境を記すのを忘れました。
主に二台のPCで使用しています。
cpu:core2 quad Q6600
gpu:radeonHD 5770
cpu:core2 duo U7700
gpu:オンボード
オフライン
以下の関数ですが
int make(void){ //乱数発生関数
return 1+(int)(rand()*(99-1+1)/(1+RAND_MAX)); //乱数発生
} //乱数発生関数の終了なぜRAND_MAXで割っているのでしょう?一度printfでRAND_MAXの値を確認してみること、make関数が何を返しているのか確認してみましょう。
あと余談ですが
(99-1+1)は99ですよね。
オフライン
乱数がおかしいのかも と、疑っているところが明確になっているのなら、printfを1行埋め込めば、少なくとも、今の「よくわからないけど怪しい」という状態からは次に進めると思います。
make()に(rand()の値を一時変数にでも入れて)printfを埋め込んで実行してみてはいかがでしょうか。自身で書いたロジックを理解しているなら、「どこまで期待通りに動いているのか」「どこが期待していたものと違うのか」を調べれてゆけば良いのではと思います。
プログラムを作成するというのは、APIやら何やらを駆使してコードを書けるかというよりも、思ったとおり動かないときに問題を絞り込んで解決できるかどうかのほうが重要になるますから、この機会にその辺の覚えると良いかと思います。
あと、もう1つ重要なのは、「1つのプラットフォーム(の1つのコンパイラ)で動いたからソースの問題はない・・・とは言えない」ことです。コンパイラのメーカが異なるだけでも提供関数が違ったりしますから(コンパイラやライブラリのベンダーの差異で同名の関数でも仕様が違うこともたまにあります)。意外と、「うまく動いていた方が偶然の産物(仕様で保障されない動作)だった」ということは多いです。
オフライン
*stdlibの乱数についてのウィキ http://ja.wikipedia.org/wiki/Rand
*コードをアップするときは、そのまま上げると、醜いとか、表示がおかしくなるので、[code][/code](実際には、半角アルファベットで入力します)
で囲いましょう。
*通常、コード全体を上げると、長くなりすぎ、ハード、ソフト、ヒューマン全てに負荷がかかり過ぎる場合があるので、問題箇所のみで十分な場合は、その部分のみアップしましょう。
オフライン
自己レスです。一応解決致しました。ただ、新たな疑問が生じてしまいました。
array様weyk様返信ありがとうございます。
お二方のおっしゃられたとおり乱数発生の部分だけいろいろ調べてみました。そのおかげで解決することができました。有難うございました。
実は、そもそもコンパイルの時点でinteger overflow in expressionと言う警告が出ていました。
乱数の発生に関してはhttp://homepage3.nifty.com/mmgames/c_guide/21-02.htmlを参考にさせていただいておりまして、ほとんどそのまま使わせていただいたしだいです。
乱数を発生させる関数内においてint型なのでそのまま整数でいいだろうと思い、1 99 等を入力しておりましたが、これをホームページにのっとり1.0 99.0 等表記した場合にはこの様な警告は生じず、また、乱数発生についても正常に行われました。
警告について調べてみると、そのままなのですが、オーバフローをしているとのことでした。しかしなぜオーバーフローをしているのか分かりません。もしお分かりの方がいらっしゃいましたらお教えください。
ただ、1.0等表記した際はdouble型となり、オーバーフローしなかったのでは?と考えています。
コンパイラによって、動作が異なるというのは本当に初耳で勉強になりました。
しかしなぜ、borlandでコンパイルした場合にはオーバフローしなかったのでしょうか?また、以前別のlinux(centOS)のgcc でコンパイルした場合には問題はありませんでした。(バージョン等は分かりません。)
オフライン
return 1+(int)(rand()*(99-1+1)/(1+RAND_MAX));
の部分は正しくは以下では?
return 1 + (int)((double)rand() * 99 / (double)RAND_MAX);
RAND_MAXが整数型の最大値と一致する場合、rand() * 99 <= RAND_MAXとなり、上記コードを何度実行しても1しか返しません。
ただ、キャストしたり割ったり掛けたりするのは気持ち悪いですし、どの環境でもdoubleの最大値がRAND_MAX*99より大きいとは限らないと思うので、以下のコードが最も適切かと思います。
return 1 + rand() % 99;
オフライン
si様vbk様返信ありがとうございます。
コードのアップの仕方も分かっておらず申し訳ありませんでした。以後気をつけます。
おっしゃるとおり、1 99 等でやった場合には乱数ではなく1しか出力されておりませんでした。
やはり、double型に直すのが適切なのでしょうか。
オフライン
なんかおかしいけど、根本の原因はどこだろう とは思っていたのですが、おそらく以下が理由です。
途中もすべて整数(intやunsinged int)で計算しているため、「RAND_MAXが、UINT_MAXよりも十分に小さいはず」ということを前提が必要です。
この条件は、大抵のintが32bit以上の環境ではこの条件を満していますが、intが16bitベースの環境やGNU C Libraryでは満たせません。そういう環境で試してなかったので条件に合致していた ということのようです。
GNU C Libraryの該当関数郡については、以下参照。
http://www.gnu.org/software/libc/manual/html_node/ISO-Random.html#ISO-Random
ライブラリの説明に、「In other libraries, it may be as low as 32767. 」と書かれているので、やはり珍しいんでしょうね。
剰余を使う場合、「結果の、非常に些細な偏り」をどうするのかが悩ましいところです(実質上は問題にならない差異)
オフライン
Linuxにおけるrand関数の実装は、下位ビットも上位ビットと同程度の精度のようですから、剰余を取る方法で問題ないと思います。
http://archive.linux.or.jp/JM/html/LDP_man-pages/man3/rand.3.html
また、他の環境でも下位ビットで高精度な乱数を生成したい場合は、C標準ではありませんがrandom関数を使う手もあるかもしれません。
オフライン
weyk様vbk様返信ありがとうございます。
やっとすっきりしました。本当に有難うございました。
環境がちがうことで条件を満たしていなかったということですね。
以後乱数を発生させる場合は教えていただいた方法を用いたいと思います。
オフライン