ページ 1 / 1
乱数が重複
Posted: 2013年6月19日(水) 14:44
by Z400FX
code
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void *card(void){
int num,i;
char *test[13] = {"A","2","3","4","5","6","7","8","9","10","J","Q","K"};
srand((unsigned)time(NULL));
num = rand() % 13;
return test[num];
}
int main (void){
char c;
char *funyu;
while(1){
printf("Hit or Stand?(h/s)¡§");
scanf("%c",&c);
if(c == 's')break;
funyu = card();
printf("%s\n",funyu);
}
return 0;
}
code
乱数が重複しないようにするにはどうすれば良いですか?
トランプのカードの種類も出さなくてはいけません。
Re: 乱数が重複
Posted: 2013年6月19日(水) 14:50
by 超絶右留斗羅天才プログラマー
乱数が重複するのは、使う乱数を決めるための関数
srand((unsigned)time(NULL));
をcard関数内で何度も呼び出しているのが原因です。
main関数内で一度だけ呼び出すようにしてみましょう。
Re: 乱数が重複
Posted: 2013年6月19日(水) 15:01
by Z400FX
main関数内にしましたが重複してしまいます。
Re: 乱数が重複
Posted: 2013年6月19日(水) 15:07
by KORYUOH
srand((unsigned)time(NULL));
をwhile文の中でやっていませんか?
Re: 乱数が重複
Posted: 2013年6月19日(水) 15:09
by Z400FX
やっていません。
Re: 乱数が重複
Posted: 2013年6月19日(水) 15:10
by 超絶右留斗羅天才プログラマー
一応、正常なプログラムを貼り付けておきます(*´▽`*)
どうしても分からなくなったら、スポイラーを開いて確認してみましょう。
► スポイラーを表示
コード:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
char *card(void){
int num,i;
char *test[13] = {"A","2","3","4","5","6","7","8","9","10","J","Q","K"};
num = rand() % 13;
return test[num];
}
int main (void){
char c;
char *funyu;
srand((unsigned)time(NULL));
while(1){
printf("Hit or Stand?(h/s)¡§");
scanf("%c",&c);
if(c == 's')break;
funyu = card();
printf("%s\n",funyu);
}
return 0;
}
Re: 乱数が重複
Posted: 2013年6月19日(水) 15:21
by Z400FX
最初のアドバイスでそれをやってみましたが、重複したので改めて質問させて頂いたのですが・・・。
Re: 乱数が重複
Posted: 2013年6月19日(水) 15:27
by h2so5
Re: 乱数が重複
Posted: 2013年6月19日(水) 15:45
by Z400FX
探してもらったところすみませんが順列ではなく
実行結果は
Hit or Stand?(h/s):h
A
Hit or Stand?(h/s):h
10
Hit or Stand?(h/s):h
Q
Hit or Stand?(h/s):h
5
Hit or Stand?(h/s):h
2
Hit or Stand?(h/s):h
2
Hit or Stand?(h/s):h
K
Hit or Stand?(h/s):h
4
Hit or Stand?(h/s):h
2
こうなるのですが、見た通り2が重複してますよね?
これを解決したいのです!
Re: 乱数が重複
Posted: 2013年6月19日(水) 15:48
by KORYUOH
もし、連続して同じカードを引きたくないのであれば前の値を保持しておき同じであるならば生成しなおす方法をお勧めしますが、
すべて重複したくないのであるならば配列をシャッフルしてしまうのがいいと思います。
Re: 乱数が重複
Posted: 2013年6月19日(水) 15:52
by Z400FX
1度引いたカードはその実行中には2度と出てこないようにしたいのです。
Re: 乱数が重複
Posted: 2013年6月19日(水) 15:58
by KORYUOH
それでしたら配列の中身をランダムにシャッフルして取得するたびに配列の添え字をインクリメントするのが簡単でしょう。
ただし、配列の大きさ以上に取得使用としたときの対応が必要になりますが。
Re: 乱数が重複
Posted: 2013年6月19日(水) 16:05
by h2so5
Z400FX さんが書きました:1度引いたカードはその実行中には2度と出てこないようにしたいのです。
それを実現するのがランダムな順列です。
Re: 乱数が重複
Posted: 2013年6月19日(水) 16:06
by 超絶右留斗羅天才プログラマー
先程は失礼致しました。
以下を全て直すと実現できます。
①scanfの内容をscanf("%s",&c);に直しておきます。
②card関数の戻り値をchar型とします。
③funyuの変数をchar型とします。
④card関数に新しくstatic int型の変数を一つ用意し、初期値を13とします。
⑤card関数のtestをchar型にし、"1"とくくっていたものを'1'と全て直します。
⑥num=rand() % maxと変更します。
⑦戻り値を保持するchar型変数にtest[num]を保持します。
⑧一度選んだものを潰すために、for文を回します。
⑨maxの値を一つ引きます。
⑩保持しておいたものをreturnします。
⑪13回以上引くとエラーが起きるので、13回以降は'\0'をreturnします。
コードは以下のようになります。
► スポイラーを表示
コード:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
char card(void){
int num,i;
static int max = 13;
static char test[13] = {'A','2','3','4','5','6','7','8','9','10','J','Q','K'};
if(max== 0)return '\0';
num = rand() % max;
char ret = test[num];
for( i = num; i < max - 1; i++ )
test[i] = test[i+1];
max--;
return ret;
}
int main (void){
char c;
char funyu;
srand((unsigned)time(NULL));
while(1){
printf("Hit or Stand?(h/s)¡§");
scanf("%s",&c);
if(c == 's')break;
funyu = card();
printf("%c\n",funyu);
}
return 0;
}
Re: 乱数が重複
Posted: 2013年6月19日(水) 16:59
by usao
他のトピックと酷似しているようですが,
こっちではスートの概念がない(1~13の13枚しかカードがない)ということでいいのかな?
http://dixq.net/forum/viewtopic.php?f=3&t=13282
forで回さずとも1要素だけ動かせば良いように思います.
コード:
//for( i = num; i < max - 1; i++ )
// test[i] = test[i+1];
test[num] = test[max-1];
Re: 乱数が重複
Posted: 2013年6月19日(水) 21:34
by みけCAT
自分が昔書いたライブラリ(をC言語に移植したもの)を貼っておきます。
► スポイラーを表示
コード:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define UNIQUERAND_MAX 13
#define BIT_MAX UNIQUERAND_MAX
int UniqueRand_bitTable[BIT_MAX];
void UniqueRand_bitInit() {
int i;
for(i=0;i<BIT_MAX;i++)UniqueRand_bitTable[i]=0;
}
void UniqueRand_bitAdd(int pos,int value) {
pos++;
while(pos<=BIT_MAX) {
UniqueRand_bitTable[pos-1]+=value;
pos+=pos & (-pos);
}
}
int UniqueRand_bitSum(int pos) {
int sum=0;
pos++;
while(pos>0) {
sum+=UniqueRand_bitTable[pos-1];
pos-=pos & (-pos);
}
return sum;
}
void UniqueRand_init() {
UniqueRand_bitInit();
}
void UniqueRand_registerNumber(int num) {
if(num<0 || num>=BIT_MAX)return;
if(UniqueRand_bitSum(num)-(num==0?0:UniqueRand_bitSum(num-1))!=0)return;
UniqueRand_bitAdd(num,1);
}
void UniqueRand_unRegisterNumber(int num) {
if(num<0 || num>=BIT_MAX)return;
if(UniqueRand_bitSum(num)-(num==0?0:UniqueRand_bitSum(num-1))==0)return;
UniqueRand_bitAdd(num,-1);
}
int UniqueRand_nextInt() {
int left,right,mid;
int max=BIT_MAX-UniqueRand_bitSum(BIT_MAX-1);
if(max<=0)return -1;
int pos=rand()%max;
left=0;right=BIT_MAX-1;
while(left<=right) {
mid=(left+right)/2;
if(mid-UniqueRand_bitSum(mid)>=pos)right=mid-1; else left=mid+1;
}
UniqueRand_registerNumber(right+1);
return right+1;
}
int main(void) {
int i;
srand((unsigned int)time(NULL));
UniqueRand_init();
for(i=0;i<UNIQUERAND_MAX;i++) {
printf("%d\n",UniqueRand_nextInt());
}
return 0;
}
使い方
1. UNIQUERAND_MAXを自分が使いたい乱数の最大値+1に設定する。
2. UniqueRand_init()関数を呼び出して初期化する。
3. UniqueRand_nextInt()関数で次の乱数が取得できる。
4. UniqueRand_resigterNumber(int num)関数である数を出たことにできる。
5. UniqueRand_unRegisterNumber(int num)関数である数を出ていないことにできる。
移植元のC++のコードはここにあります。
http://dixq.net/forum/blog.php?u=536&b=3091
Re: 乱数が重複
Posted: 2013年6月20日(木) 10:07
by Z400FX
'A','2','3','4','5','6','7','8','9','10','J','Q','K'};
超絶右留斗羅天才プログラマーさん
この部分が謎なのですが?
Re: 乱数が重複
Posted: 2013年6月20日(木) 10:21
by Z400FX
皆様どうもありがとうございました
Re: 乱数が重複
Posted: 2013年6月20日(木) 11:53
by 超絶右留斗羅天才プログラマー
シングルクォーテーションでくくると、何故か文字化けしてしまうのですよね(ノ_・;)
⑤と⑪に該当する部分ですので、元のプログラムを元に変更してください。
他に文字化けしている所も、元のプログラムを参照してください。
Re: 乱数が重複
Posted: 2013年6月21日(金) 13:01
by Z400FX
トランプのカードを1 枚ひく(カードのスーツと数字を返す) 関数を作れ。1, 11, 12, 13 につい
ては、それぞれA, J, Q, K と表示させること。スペード、ハート、ダイヤ、クラブについては、そ
れぞれS, H, D, C と表示させること。また、トランプは1 組だけ使用するものとし、同じカード
が重複してひかれることは許されない。さらに、プログラムを実行し直すたびに、異なるカード
が出るようにすること。
/*
// トランプを引くプログラム
*/
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
//トランプを引く処理
void *pull(void){
int rand_cards,rand_suit; //乱数を格納する変数
int suit[4] = {1,2,3,4}; //トランプの絵柄
char *cards[13] = {"A","2","3","4","5","6","7","8","9","10","J","Q","K"}; //トランプの数字
srand((unsigned)time(NULL)); //乱数を初期化する。この処理がないと乱数が初期化されずに同じ結果がでる。
rand_suit = rand() % 4; //乱数を生成してスートを決めて変数に格納する。
srand((unsigned)time(NULL));
rand_cards = rand() % 13; //乱数を生成して数字を決めて変数に格納する。
printf("%d%s\n",suit[rand_suit],cards[rand_cards]); //結果を表示
}
int main(void){
char c;
while(1){ //無限ループ
printf("Hit or Stand?(h/s):");
scanf(" %c",&c);
if(c == 's')break;
pull();
}
return 0;
}
あれから考えてみましたが、やはり分かりません。
Re: 乱数が重複
Posted: 2013年6月21日(金) 13:18
by KORYUOH
ソース部分はcodeタグで囲っていただきたいです
数値とスート別々に管理するのではなく、構造体で管理するのはいかがでしょうか?
Re: 乱数が重複
Posted: 2013年6月21日(金) 13:25
by usao
「何が」わからないのでしょうか?
当初は13枚で考えていたように見えたのですが,13枚の場合はできたのでしょうか?
13枚から52枚に増えたからといって特段何も変わらないと思うのですが.
Re: 乱数が重複
Posted: 2013年6月21日(金) 13:36
by Z400FX
usaoさん
int suit[4]={1,2,3,4};のところをD,S,Q,Hにしたいのですが、どうすれば良いですか?
ちなみにchar型にしても出来ません。
結果が重複しないため、52回実行すると自動的に終了したい。
出来るのならば教えてください。
Re: 乱数が重複
Posted: 2013年6月21日(金) 13:58
by Z400FX
usaoさん
出来そうですか?
Re: 乱数が重複
Posted: 2013年6月21日(金) 13:59
by KORYUOH
単に52回にしたいのであれば無限ループではなく有限ループに変更するだけで対応可能です。
ソースを読んだところpullを行うたびにsrandしてしまっていますがこれは意図してやっていますか?
Re: 乱数が重複
Posted: 2013年6月21日(金) 14:02
by non
char *cards[13] = {"A","2","3","4","5","6","7","8","9","10","J","Q","K"};
今、現在こうなっているのを
char *cards[52] = {"SA","S2","S3","S4","S5","S6","S7","S8","S9","S10","SJ","SQ","SK","HA","H2","H3","H4","H5","H6", 以下略 };
にするのはどうでしょう。
Re: 乱数が重複
Posted: 2013年6月21日(金) 14:15
by Z400FX
KORYUOHさん
これではいけないのですか?
有限ループがわかりません・・・。
Re: 乱数が重複
Posted: 2013年6月21日(金) 14:26
by KORYUOH
これでも可能です。
もし無限ループでやりたいのであればカウンターでbreakさせなければなりませんが。
有限ループはforでやる方法とwhileでやる方法があります。
たとえば確実に一定回数やる場合はfor
条件が正しい間だけやる場合はwhileでやるとよいでしょう
Re: 乱数が重複
Posted: 2013年6月21日(金) 14:28
by non
KORYUOH さんが前に言われている
>もし、連続して同じカードを引きたくないのであれば前の値を保持しておき同じであるならば生成しなおす方法をお勧めしますが、
>すべて重複したくないのであるならば配列をシャッフルしてしまうのがいいと思います。
この2つの方法のどちらかで行うのだと思いますが、いつまでたってもその方向に進んでいきませんね。
Re: 乱数が重複
Posted: 2013年6月21日(金) 14:38
by usao
>int suit[4]={1,2,3,4};のところをD,S,Q,Hにしたいのですが、どうすれば良いですか?
>ちなみにchar型にしても出来ません。
意味がわかりません.
char suit[4] = { 'D', 'S', 'Q', 'H' };
とか書きたいのでしょうか?
>52回
>有限ループがわかりません・・・。
あなた自身が「無限ループ」を書いて,ユーザ入力の結果によって終了するようにコードを書いているのですが…??
一定回数だけ繰り返したいのであれば,そのように書くだけだと思いますが.
「一定回数繰り返させる書き方がわからない」とかいう話なのであれば…さすがに回答しがたいです.
(この課題に挑む前にまずはCを一から勉強してください,としか言えない)
Re: 乱数が重複
Posted: 2013年6月21日(金) 19:08
by 超絶右留斗羅天才プログラマー
こちらでどうでしょうか(*´ω`*)
► スポイラーを表示
コード:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
char* pull(void){
static int max = 52;
static char* card[52] = {
"s1","s2","s3","s4","s5","s6","s7","s8","s9","s10","sJ","sQ","sK",
"h1","h2","h3","h4","h5","h6","h7","h8","h9","h10","hJ","hQ","hK",
"c1","c2","c3","c4","c5","c6","c7","c8","c9","c10","cJ","cQ","cK",
"d1","d2","d3","d4","d5","d6","d7","d8","d9","d10","dJ","dQ","dK",
};
if(max == 0)return "\0";
int s = rand() % max;
char ret[256];
strcpy( ret, card[s] );
card[s] = card[max-1];
max--;
return ret;
}
void main(){
char in[2], out[4];
srand((unsigned int)time(NULL));
while(1){
printf("Hit or Stand?(h/s):");
scanf("%s",&in);
if(strcmp(in,"h")!=0)continue;
if(strcmp(in,"s")==0)break;
strcpy( out, pull() );
if(strcmp(out,"\0")==0)break;
printf("%s\n",out);
}
}
Re: 乱数が重複
Posted: 2013年6月21日(金) 19:17
by softya(ソフト屋)
傍観してましたが、超絶右留斗羅天才プログラマーさんが他の人のやっていることを台無しにしそうなのでコメントさせていただきました。
面倒でしょうが、答えに誘導する形での回答をお願いします。
どうしても、直接的なコードが必要だと思うのならプレイベートメッセージで他の人に確認をとるとか協調をお願いします。
【補足】あっ、私に強制権はありませんのでお願いになります。
Re: 乱数が重複
Posted: 2013年6月21日(金) 20:01
by usao
あらあら またダイレクトにコード投下されてますね…まぁその方針にはあえて文句は言いませんけど.
とりあえずコードについては
return ret;
とか,どうかと思います.