ページ 1 / 1
重複しない乱数の作成
Posted: 2008年10月17日(金) 14:13
by もげ
質問です。
乱数の発生について質問ですが、重複しない乱数の発生は成功したのですが、重複せずに乱数を取得するプログラムができません。
ちなみに、メイン文には書かずに、プロトタイプ宣言をして重複しない乱数を発生させる関数を作っています。
乱数系列は
srand((unsigned) time(&t));
を使っています。
/*乱数発生関数。整数Nを受け取り0から(N-1)までの整数をランダムに返す*/
int rando(int num,int i){
int ret;
int j=0;
int r[num];
for(;;){
ret = rand();
ret = ret % num;
if(num<i)break;
r = ret;
int c=0;
for(j=0;j<=i;j++)if(r[j]==ret)c++;
if(c==1)break;
}
return ret;
}
メイン文の発生箇所ですが一部をのけてみます。
for (i=0;i<item_MAX;i++){
weight = rando(item_weight_MAX,i);
}
自分で作ったのですが失敗しております。
新規で何かいい案があればよろしくお願い致します。
また、付け加える要素があればよろしくお願いいます。
Re:重複しない乱数の作成
Posted: 2008年10月17日(金) 14:21
by バグ
すみません、質問の意味がよく分かりません。
重複しない乱数の取得というのは、例えば、関数を10回実行したら0~9までの数値が重複なくランダムな順番で収納されるという意味ですか?
Re:重複しない乱数の作成
Posted: 2008年10月17日(金) 14:27
by もげ
メイン文のとこでNという数を定義でき、0~Nまでの数値をが重複なくランダムな順番で収納されるという意味です。
質問の意味が分かりにくく、申し訳ありませんでした。
よろしくお願い致します。
Re:重複しない乱数の作成
Posted: 2008年10月17日(金) 14:31
by Mist
私だったら、N回乱数生成関数をコールするのではなく、N個分の配列を渡してセットしてもらうようにします。
そうすれば、関数内で重複チェックできます。
Re:重複しない乱数の作成
Posted: 2008年10月17日(金) 14:39
by 御津凪
こういうことでしょうか。
void shuffle( int* ary, int n ){
int i,j,k;
for(i = 0;i < n;++i) ary = i;
for(i = 0;i < n-1;++i){
j = i + (rand() % (n - i));
k = ary[j];
ary[j] = ary;
ary = k;
}
}
与えられた配列 ary に、0 ~ n までの数値を(重複なく)ランダムにセットする関数です。
中でやっていることの意味は、自分で読み解いてみてください。
そのほうが、自分の力になります。
Re:重複しない乱数の作成
Posted: 2008年10月17日(金) 14:39
by もげ
具体に的にはどのように書くのでしょうか?
数時間考えたのですが、うまくいかなくて困っています。(泣)
お忙しくなければ、助言を頂けないでしょうか?
Re:重複しない乱数の作成
Posted: 2008年10月17日(金) 14:42
by もげ
御津凪さんありがとうございます。
実際にためにて、プログラムをうっていこうと思います。
>中でやっていることの意味は、自分で読み解いてみてください。
もちろん、自分の力で読み解いてみようと思います。
お忙しいのに、ありがとうございました。
Re:重複しない乱数の作成
Posted: 2008年10月17日(金) 14:46
by バグ
無駄なループを極力なくすなら、こんな感じ?
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define N 10
void shuffle(int* buf, int size);
int main(void)
{
srand((unsigned)time(NULL));
int data[N];
shuffle(data, N);
for (int i = 0; i < N; ++i)
{
printf("%d\n", data);
}
return 0;
}
void shuffle(int* data, int size)
{
// 初期化
for (int i = 0; i < N; ++i)
{
*(data + i) = i;
}
// シャッフル
for (int i = 0; i < N - 1; ++i)
{
int id = rand() % (N - 1);
int swap = *(data + id);
*(data + id) = *(data + N - 1);
*(data + N - 1) = swap;
}
}
Re:重複しない乱数の作成
Posted: 2008年10月17日(金) 14:48
by バグ
ごめん、shuffle関数の第2引数を使ってなかった…OTL
そんな訳で、修正版…
このアルゴリズム、なんか名前があったはずなんだけど、忘れてしまいましたわ…(^_^;)
void shuffle(int* data, int size)
{
// 初期化
for (int i = 0; i < size; ++i)
{
*(data + i) = i;
}
// シャッフル
for (int i = 0; i < size - 1; ++i)
{
int id = rand() % (size - 1);
int swap = *(data + id);
*(data + id) = *(data + size - 1);
*(data + size - 1) = swap;
}
}
Re:重複しない乱数の作成
Posted: 2008年10月17日(金) 14:53
by もげ
バグさんご親切にありがとうございます。
お二方の教えていただいた内容を十分に活用していきたいと思います。
今から実際にいろいろ打ってみますが、うまく成功しなかったらよろしくお願い致します。
現在、遺伝的アルゴリズムを勉強中なのですが難しいです><
Re:重複しない乱数の作成
Posted: 2008年10月17日(金) 14:55
by もげ
ちなみにソースプログラムのせておきますが、参考にさせていただいたプログラムをいじったかたちです。
うまく、乱数が重複しない形を作成できればいいのですが^^;
Re:重複しない乱数の作成
Posted: 2008年10月17日(金) 15:30
by もげ
実際に教えていただいた感じで関数を作成いたしましたが、うまくできませんでした。
上に添付したプログラムにあうように作成するにはどうすればいいのでしょうか?
正直、お手上げで迷ってます;;
お忙しいとは思いますが、何かとよろしくお願い致します。
平均計算用の関数は未完成なので気にしないでください。
Re:重複しない乱数の作成
Posted: 2008年10月17日(金) 15:33
by やそ
>うまく、乱数が重複しない形を作成できればいいのですが^^;
さて実行した結果はどうだったのでしょう?
rando(・・・)内で定義した配列は上手く機能していたでしょうか?
返り(戻り)値retは重複せずに済みましたか?
rando(・・・)をみてもバグさんや御津凪さんが提示してくれたロジックを参考にしているようにはあまり見えなかったのは私だけ??
あのロジックって以前、トランプのシャッフルの方法のトピックで出ていたシャッフルのロジックですね^^
(まあ、どこでもありがちとも言いますが^^;)
Re:重複しない乱数の作成
Posted: 2008年10月17日(金) 15:44
by もげ
>rando(・・・)をみてもバグさんや御津凪さんが提示してくれたロジックを参考にしているようにはあまり見えなかったのは私だけ??
添付したのは、参考にする前のものなので^^;
VMで仮想Linuxでやってるので、LinuxからWindouwsにコピーができないのです;;
ひとまず、掲示してくれた方に失礼ですのでもう少し粘ってみます!
Re:重複しない乱数の作成
Posted: 2008年10月17日(金) 16:01
by バグ
>>やそさん
似ていますが、微妙に違います。
ループに無駄が多すぎると某所でツッコミをくらいまして…(^_^;)
今回の方法だとループ回数が『配列の要素数-1回』でいけるみたいです。
Re:重複しない乱数の作成
Posted: 2008年10月17日(金) 16:18
by 管理人
今までのrandのような形で使いたいのかなと思い、こんなの用意してみました。
my_rand.hの内容は理解する必要ありません。
このままソースファイルと同じフォルダにこの名前で保存すればOKです。
my_srand(10,20);
と書くと、今後my_rand関数を呼べば10~20の数値のどれかが重複無く返ってきます。
この場合12回以上my_randを呼ぶとエラーになります。
my_rand.h内で領域を確保しているため、乱数を使い終わったら解放
delete_my_rand
する必要があるので注意して下さい。
基本的にmain.cppの内容だけ見てもらったら大丈夫かと思います。
設定して、乱数を呼んで、消去という流れです。
なお、main.cppという名前はこれでなくても構いません。
----------------------------------------------------------
/* my_rand.h */
----------------------------------------------------------
int *MyRandArrayPointer; //乱数入れる入れ物
int MyRandCallNum; //今何回呼ばれたか
int MyRandArrayNum; //入れ物の個数
//my_srandで設定した乱数を順番に取り出す。
//エラーの場合0を返し、error_flagが-1になる。
int my_rand(int *error_flag){
int ret;
if(MyRandArrayNum<=MyRandCallNum){
*error_flag=-1;
return 0;
}
ret=*(MyRandArrayPointer + MyRandCallNum);
MyRandCallNum++;
*error_flag=0;
return ret;
}
//from~toまでの乱数を設定する
//エラーの返り値は-1
int my_srand(const int from, const int to){
int i,n;
//toの方がfromが小さいか、intの扱える範囲を超えていたら
if( (n = to+1-from) <= 0 || from<=-INT_MAX || to>=INT_MAX)
return -1;//エラー
//配列要素確保
MyRandArrayPointer = (int *)malloc( n * sizeof(int) );
//fromからtoまでを格納
for( i=from ; i<=to ; i++ ){
*MyRandArrayPointer=i;
MyRandArrayPointer++;
}
//ポインタ戻す
MyRandArrayPointer-=n;
//シャッフル
for ( i=0 ; i<n ; i++ ){
int id = rand() % n;
int swap = *(MyRandArrayPointer + id);
*(MyRandArrayPointer + id) = *(MyRandArrayPointer + i);
*(MyRandArrayPointer + i ) = swap;
}
MyRandCallNum=0;
MyRandArrayNum=n;
return 0;
}
void delete_my_rand(){
free(MyRandArrayPointer);
}
----------------------------------------------------------
/* main.cpp */
----------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "my_rand.h"
int main(void){
int error_flag;
srand((unsigned)time(NULL));//乱数初期化
my_srand(10,14);//MY乱数初期化。10~14の数値を設定する
printf("%d\n",my_rand(&error_flag));//10~14のどれかが表示される
printf("%d\n",my_rand(&error_flag));//10~14のどれかが表示される
printf("%d\n",my_rand(&error_flag));//10~14のどれかが表示される
printf("%d\n",my_rand(&error_flag));//10~14のどれかが表示される
printf("%d\n",my_rand(&error_flag));//10~14のどれかが表示される
printf("%d\n",my_rand(&error_flag));//エラー(上で5個しかセットしてないので)
printf("%d\n",my_rand(&error_flag));//エラー(この時error_flagには-1が入っている)
delete_my_rand();//MY乱数消去
return 0;
}
----------------------------------------------------------
実行結果
10
11
14
13
12
0
0
----------------------------------------------------------
こういう使い方が適切かどうかわかりませんが、
何か参考になればどうぞ^^;
Re:重複しない乱数の作成
Posted: 2008年10月17日(金) 17:09
by もげ
管理人様ありがとうございます。
あれから、やってみたのですが、値の取得がきちんとできていない為かー1とい値をずっととりつづけています。
どこに原因があるのでしょうか?
ご助力お願いします。
Re:重複しない乱数の作成
Posted: 2008年10月17日(金) 17:16
by 管理人
う~む、ソースコード無しにどこがわるいですか?
と言われるのは、病院に来ていない患者を診断して下さいと言われるようなもので^^;
>ー1とい値をずっととりつづけています。
これはmy_randを呼んだ時のerror_flagが-1ということでよかったでしょうか?
メインファイルの他の文はなしで、
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "my_rand.h"
int main(void){
int error_flag;
srand((unsigned)time(NULL));//乱数初期化
my_srand(10,14);//MY乱数初期化。10~14の数値を設定する
printf("%d\n",my_rand(&error_flag));//10~14のどれかが表示される
delete_my_rand();//MY乱数消去
return 0;
}
これだけならちゃんと表示できますか?
これでうまくいくならここから段々と元も戻していって、どこが悪いか見つけてみてはどうでしょう?
後、お使いのコンパイラ、OSなどの環境は何ですか?
Re:重複しない乱数の作成
Posted: 2008年10月17日(金) 17:45
by もげ
my_rand.h: In function ‘my_srand’:
my_rand.h:23: error: ‘INT_MAX’ undeclared (first use in this function)
my_rand.h:23: error: (Each undeclared identifier is reported only once
my_rand.h:23: error: for each function it appears in.)
管理人様の張って頂いたものをコピーさせていただきますたが、エラーがでてしまいました。
環境は
コンパイラ gcc
OSはFedora9です。
私が添付したソースプログラムにも対応できないみたいです。
ほんとに無知ですいません。
なんとか今日中にソースプログラムの方に関数として作りたいんですがね;;
Re:重複しない乱数の作成
Posted: 2008年10月17日(金) 18:01
by non
RAND_MAXかな?
Re:重複しない乱数の作成
Posted: 2008年10月17日(金) 18:20
by non
勘違いです。ごめんなさい。
#include <limits.h>
をつけるのかもしれません。
Re:重複しない乱数の作成
Posted: 2008年10月17日(金) 18:29
by バグ
ちなみにC++を使用するのは禁止ですか?
Re:重複しない乱数の作成
Posted: 2008年10月17日(金) 18:30
by もげ
#include <limits.h> を追加しましたが、うまくいきませんでした><
重複しない乱数ならうまくいくのに、重複しない乱数って難しいんですね;;
正直ギブアップ寸前なので、どなたか添付したソースで作成してくれるお方はおられませんか?
正直やってくださいは、規約に違反するかもしれないですが分からないので;;
もし、違反で皆様の反感をかうようであれば諦めますが;w;
Re:重複しない乱数の作成
Posted: 2008年10月17日(金) 18:31
by もげ
できれば、C言語がいいですね;;>バグさん
C++はやってないのでいっさい分からないのです><
Re:重複しない乱数の作成
Posted: 2008年10月17日(金) 18:44
by non
INT_MAX を 例えば 32767 に書き換えたらうまく動きますか?
Re:重複しない乱数の作成
Posted: 2008年10月17日(金) 18:46
by もげ
数字に変えたらうごきました!>nonさん
なんでだろう;;
でも、自分が添付したファイルに適用できるのでしょうか><
Re:重複しない乱数の作成
Posted: 2008年10月17日(金) 18:55
by non
INT_MAXは整数の最大値です。 limits.hの中で宣言されていると思ってましたが、
もげさんとは環境のちがいなのでしょうか?
とりあえず、もげさんのプログラムではMAXが20のようなので32767で問題ないと思います。
もっと、詳しい人のコメントを待ちましょう。
もげさんのプログラムを見て思ったんですが、MAXが全部20でいいのかな?
重量0ってのもありなんですか?いらんお世話ですが。
Re:重複しない乱数の作成
Posted: 2008年10月17日(金) 19:01
by もげ
毎回変えれるようにしてあるので、MAXは変更できるようにしてあります。
よって、30にしたりすることもあります。
0~Nまでの乱数にしているので0っていうことも可能性としてあります。
環境はどうなのでしょうかね?
あまり詳しくないので分かりません;;
ちょっとしたことでも言っていただけるのでありがたいです;;>nonさん
皆様よろしくお願い致します。
Re:重複しない乱数の作成
Posted: 2008年10月17日(金) 19:11
by non
もしかして、
#include "my_rand.h"
の後に
#include <limits.h>
を追加したのでは?
"my_rand.h"より前に入れました?
Re:重複しない乱数の作成
Posted: 2008年10月17日(金) 19:18
by もげ
前にいれたら成功しました!
管理人様がくれたプログラムで実行したら実行できました。
ありがとうございます>nonさん
ただ、自分のプログラムではうまくいかずです;;
Re:重複しない乱数の作成
Posted: 2008年10月17日(金) 21:09
by 管理人
う、お返事遅くなってすみません。リナックスでしたか。
先ほどいった様に、少しずつ作っていってはどうですか?
とりあえずサンプルは動くんですよね?
それなら、とりあえずサンプルをそのまんま貼り付けて作っていけばどうでしょう?
int main(void){
int error_flag;
srand((unsigned)time(NULL));//乱数初期化
my_srand(10,14);//MY乱数初期化。10~14の数値を設定する
[color=#00ffff>//ここでmy_rand(&error_flag)を使う[/color]
delete_my_rand();//MY乱数消去
return 0;
}
その「ここで」の部分で使ってます?乱数消した後で使ったりしてません?
どうしても解決しないならソース丸ごとこちらに投稿してもらうほかなさそうです。
Re:重複しない乱数の作成
Posted: 2008年10月17日(金) 21:32
by Justy
管理人さんへ。
http://www.play21.jp/board/formz.cgi?ac ... e=&id=dixqのコードでちょっと質問が。
たいしたことじゃないのかもしれませんが、シャッフルのところで偏りがおこる
可能性があるような気がします。
RAND_MAXが 0x7fffしかない環境で toと fromの間が 0x8001以上あると、
my_srand()でのシャッフル処理で交換対象である iと idのうち idの方は
0~0x7fff、前半 0x8000個分しか交換対象になりません。
となると 0x8001個目以降にあった大きめの数字は前半に偏り気味になりませんか??
# あ、あと fromのチェックに INT_MINではなく -INT_MAXを使ったのは何ででしょう?
Re:重複しない乱数の作成
Posted: 2008年10月17日(金) 21:54
by 管理人
Justyさんご指摘ありがとうございます。
>fromのチェックに INT_MINではなく -INT_MAXを使ったのは何ででしょう?
う、そういえばプラスの方が1つ少ないはずですね。
仰るとおりINT_MINを使うべきでした。
(2バイトint -32768~+32767)
(4バイトint -2147483648~+2147483647)
ですね。
>大きめの数字は前半に偏り気味になりませんか??
確かにINT_MAXでエラー判定をしているわりに、大きな値に対応出来ていませんでしたね;
この場合シャッフルで使っている乱数をINT_MAXの値まで出るようにしてやるのが最善の解決策なんでしょうか。
INT_MAX / RAND_MAX 回rand()を加算してやればよさそうですが、
intが4バイトで、RAND_MAXが2バイトの環境の場合、相当な加算回数になりそうですね。
それとも自前で乱数の関数用意したほうがいいんでしょうか。
Re:重複しない乱数の作成
Posted: 2008年10月17日(金) 22:30
by Justy
>仰るとおりINT_MINを使うべきでした。
あ、やっぱりそうでしたか。
>そういえばプラスの方が1つ少ないはずですね。
2の補数表現を採用している環境ではそうですね。
>この場合シャッフルで使っている乱数をINT_MAXの値まで出るようにしてやるのが最善の解決策なんでしょうか
>それとも自前で乱数の関数用意したほうがいいんでしょうか。
自前というか、そこらにあるまともな乱数生成ライブラリを使った方がいいです。
どうしても rand()でということなら(微妙な偏りが心配ですが)
1回目の rand()の値をX倍したものと2回目の rand()%Yを足すと
最大 INT_MAXになるような関数を作ればいいんじゃないかなぁ、と一瞬思いましたが、
環境によってはやっぱりこれじゃだめですね。
# いっそのこと fromから toは RAND_MAX個しかダメ、としてしまってもいいのかも。
Re:重複しない乱数の作成
Posted: 2008年10月17日(金) 22:49
by 管理人
>いっそのこと fromから toは RAND_MAX個しかダメ、としてしまってもいいのかも。
そうですね。まぁそこまで大きな値は使わないでしょうし、
手抜き仕様で、RAND_MAX個までということにしておきます^^;
Re:重複しない乱数の作成
Posted: 2008年10月18日(土) 10:31
by バグ
なんとなくクラス化してみました。
コンストラクタで、最大値(unsigned int型ですが、0~INT_MAXの範囲でしか指定できません)と、擬似乱数列の種を設定してやって、あとはRand関数を呼び出せば、指定した最大値未満(10と指定した場合は0~9)の数値をランダムな順番で返します。
返す値が無くなったら、自動でリセットして、再び新たな順番で値を返します。
#include <cstdlib>
#include <vector>
class CRandom
{
private:
std::vector<int> m_data;
unsigned int m_max;
unsigned int m_count;
public:
CRandom(unsigned int max, unsigned int seed)
{
// 初期化
m_max = 0;
m_count = 0;
m_data.clear();
SetMaxValue(max);
Srand(seed);
}
virtual ~CRandom(void)
{
// 念の為に破棄する
m_data.clear();
}
void Srand(unsigned int seed)
{
srand(seed);
}
bool SetMaxValue(unsigned int max)
{
if (max > INT_MAX || max == 0)
{
// int型の最大値よりも大きい値、もしくは0が入力された場合はfalseを返す
return false;
}
else
{
// メンバ変数の初期化
m_count = 0;
m_max = max;
m_data.clear();
m_data.resize((std::vector<int>::size_type)max);
// データバッファを初期化
for (unsigned int i = 0; i < m_max; ++i)
{
m_data = i;
}
// データバッファをシャッフル
for (unsigned int i = 0; i < m_max - 1; ++i)
{
int id = rand() % (m_max - 1);
int swap = m_data[id];
m_data[id] = m_data[m_max - 1];
m_data[m_max - 1] = swap;
}
return true;
}
}
int Rand()
{
// サイズが0の場合は常に0を返す
if (m_max == 0)
{
return 0;
}
// 乱数が一巡した場合はリセットする
if (m_count >= m_max)
{
SetMaxValue(m_max);
}
// 乱数を返しつつ、カウントを進める
return m_data[m_count++];
}
};
Re:重複しない乱数の作成
Posted: 2008年10月18日(土) 11:52
by Justy
>なんとなくクラス化してみました
ちょww、スワップ処理で idと m_max-1の組でしか行わないなんて・・・。
m_maxが RAND_MAXより大きかったら、RAND_MAX + 1個目以降は最後の要素以外シャッフルされないような。
せっかく C++なんでstd::random_shuffle(m_data.begin(), m_data.end());を
使えばいいのではないでしょうか。
# m_maxは m_data.size()で代用できるので要らないですよね。
Re:重複しない乱数の作成
Posted: 2008年10月19日(日) 01:13
by バグ
>>Justyさん
すみません、STLはまだまだ勉強不足だったようです(^_^;)
書き直してみました。こんな感じでどうでしょう?
#include <vector>
#include <algorithm>
#include <cstdlib>
#include <ctime>
class CRandom
{
public:
CRandom()
{
}
int operator()(int max) const
{
double data = static_cast<double>(std::rand()) / static_cast<double>(RAND_MAX);
return static_cast<unsigned int>(data * max);
}
};
class CShuffle
{
private:
std::vector<int> m_data;
size_t m_count;
public:
CShuffle(size_t max, unsigned int seed)
{
m_count = 0;
m_data.clear();
Srand(seed);
SetMax(max);
}
virtual ~CShuffle(void)
{
m_data.clear();
}
void Srand(unsigned int seed)
{
std::srand(seed);
}
bool SetMax(size_t max)
{
if (max > RAND_MAX || max > INT_MAX || max == 0)
{
return false;
}
else
{
m_count = 0;
m_data.clear();
m_data.resize(max);
for (int i = 0; i < static_cast<int>(m_data.size()); ++i)
{
m_data = i;
}
CRandom random;
std::random_shuffle(m_data.begin(), m_data.end(), random);
return true;
}
}
int GetValue()
{
if (m_data.size() == 0)
{
return 0;
}
if (m_count >= m_data.size())
{
SetMax(m_data.size());
}
return m_data[m_count++];
}
};
Re:重複しない乱数の作成
Posted: 2008年10月19日(日) 01:16
by バグ
CRandomクラスのコンストラクタ部の字下げがズレてますね…すみません(^_^;)
class CRandom
{
public:
CRandom()
{
}
int operator()(int max) const
{
double data = static_cast<double>(std::rand()) / static_cast<double>(RAND_MAX);
return static_cast<unsigned int>(data * max);
}
};
Re:重複しない乱数の作成
Posted: 2008年10月19日(日) 02:23
by バグ
うわ、戻り値の型が違うし…OTL
class CRandom
{
public:
CRandom()
{
}
int operator()(int max) const
{
double data = static_cast<double>(std::rand()) / static_cast<double>(RAND_MAX);
return static_cast<int>(data * max);
}
};
Re:重複しない乱数の作成
Posted: 2008年10月20日(月) 01:02
by Justy
>書き直してみました。こんな感じでどうでしょう?
おーーー、いろいろ変わっていますね。
いいのではないでしょうか。
Re:重複しない乱数の作成
Posted: 2008年10月20日(月) 08:58
by バグ
作ってはみたものの、使い道が思いつかないなぁ…とか思っていたら、ワールドルールテトリスのNEXTに使用できそうかな?とかひらめきました(笑)
気が向いたら実装してみようかな?
Re:重複しない乱数の作成
Posted: 2008年10月20日(月) 21:53
by Justy
んー、たしかにあんまり思いつかないですね。
クイズの出題の順番・・・くらいです。
Re:重複しない乱数の作成
Posted: 2008年10月20日(月) 23:23
by バグ
他には、トランプや麻雀なんかのテーブルゲームや、カードゲームのデッキの管理とか…でしょうか?
Re:重複しない乱数の作成
Posted: 2008年10月21日(火) 00:39
by Justy
カードゲーム系ですか。
ゲームの種類によっては使えるかもしれませんね。
ちょっとまじめに考えてみると、基本的に(順番はどうであれ)既に一意になっている
オブジェクトリストそのものをシャッフルしてしまえば済んでしまう場合は
使わないと思うので、
・ 対象が数値であること
(何か別のオブジェクトリストに対するインデックスだとしたら、その別のリストが増減しないこと)
・ 1巡中は同じ数字が出ないこと
・ 1巡しきる可能性があること
・ 1巡したら、リセットして再度利用できること
というような条件の幾つかが揃わないとなかなか使わないと思われます。
そう考えるとその全てを満たすテトリスの NEXTはとてもベストなんですよね。
と、それで思いつきました。
ビンゴゲームのあたり番号とかも(再度利用する必要はないかもしれませんが)
最低3つの条件を満たしているので使えそうです。
Re:重複しない乱数の作成
Posted: 2008年10月21日(火) 01:08
by YuO
一般にはカードゲームなどで必須ですが,それ以外でも使いようがあります。
例えば,表示下限の順位近くにおいて同位が多数の時に,表示する物をいくつか選ぶためにも使えます (シャッフル後,先頭N個を使う)。
# 面倒だからSQL ServerだとORDER BYの最後のキーにNEWIDつけてTOPでぶった切ったりしちゃいますが。
さて,テトリスのNEXTは使わない方がよいです。
落ち物ゲームでは,一般的に全プレーヤーの公平を保つために,落とす物体の順序を同一にしますが,これを実現するには,疑似乱数の式と種だけ決めるのがよいでしょう。
シャッフルで行うには,一ゲームの平均数より相当長く用意しないと「一回り」が感覚で掴まれてしまう可能性がありますので。
ところで,std::random_shuffleには,入力と同じ列が返らないという,残念な仕様があります。
そのため,ゲームで使うのはやめた方がよいでしょう。
# or 2度使うとか。入力と同じ列の確率が,他より高くなりますが。
Re:重複しない乱数の作成
Posted: 2008年10月21日(火) 02:59
by Justy
>テトリスのNEXTは使わない方がよいです
えーと、ガイドラインのテトリスの NEXTは
「最初の7個は必ず7種類のテトリミノ全てがランダムな順番で均一に出現し、
次の7個もランダムな順番で均一に出現する」
とのことなので、乱数・シャッフルの質については気にするべきでしょうが、
別段シャッフルでいいかと思いますが、いかがでしょうか。
テトリス - Wikipedia
http://ja.wikipedia.org/wiki/%E3%83%86% ... A%E3%82%B9
>std::random_shuffleには,入力と同じ列が返らないという,残念な仕様があります
>そのため,ゲームで使うのはやめた方がよいでしょう。
厳密なランダムなシャッフルではないことは承知していますが、入力と同じ列って
返っていたような(実装依存?)。
どっちにしても、よほどシビアなところで使うとか、よほど乱数・シャッフルの質が
悪いとかでもない限り、ゲームで問題になることはないと思います。
Re:重複しない乱数の作成
Posted: 2008年10月21日(火) 10:35
by YuO
> えーと、ガイドラインのテトリスの NEXTは
えーと,知りませんでした。
後で確認してみます。
> 厳密なランダムなシャッフルではないことは承知していますが、入力と同じ列って
> 返っていたような(実装依存?)。
実装依存か,私の思い込みのようです。申し訳ないです。
Re:重複しない乱数の作成
Posted: 2008年10月21日(火) 20:15
by YuO
> 後で確認してみます。
とりあえず,DS版のテトリスで,6組分確認しました。
消さずに42個落とすのはしんどかったです……。
XBox360版 (ヘッドセット同梱版) もあるのですが,1回でこりごり……。
Re:重複しない乱数の作成
Posted: 2008年10月21日(火) 23:54
by バグ
ガイドラインが出来る前の作品(ファミコン版とか)は、Z型が5つ連続で落ちてきたりして困る事がありましたからね…(苦笑)
Re:重複しない乱数の作成
Posted: 2008年10月22日(水) 00:51
by Justy
YuOさん
>とりあえず,DS版のテトリスで,6組分確認しました。
>消さずに42個落とすのはしんどかったです……。
乙です。
って、DS/XBox360両方で持ってるんですか。
私はテトリスは持って無いんですよね。
今度買ってこようかなぁ。
バグさん
>Z型が5つ連続で落ちてきたりして困る事がありましたからね
それは・・・ひどい(w
雑談として、お願いします。
Posted: 2008年11月05日(水) 00:25
by ムンバ
こんばんは。お世話になります。<(_ _)>
今、参考書を参考にC言語の勉強をしています。(初心者の参考書と思われる本の2冊目です。)
以前にインクルードのご解説を頂いた時に、管理人さんからご紹介頂いたページ
http://homepage3.nifty.com/mmgames/c_gu ... cense.html
↑このページ、面白いですね~♪ハマってます!
仕事の合間に覗いてます。すごく勉強になってます。
ご紹介ありがとう御座いました。(謝
また長くなっちゃうといけないので、本題です。^^;
今、参考書を勉強してる項目は「式と演算子」です。
参考書では、C言語の開発環境(コンパイラ、リンカ、デバッカなど。となってまして)
Visual C++ 2008 Express Edition を使って、C言語を学ぶ初心者向けの参考書です。
お聞きしたいのは、「補数演算子」の所なのですが
(初めてpreタグ使います。タグが、うまく使えます様にっ!^^;)
#include <stdio.h>
int main()
{
int a = 10;
printf("a = %08X\n~a = %08X\n", a, -a);
return 0;
}
10進数を、16進数とか2進数で表す。とか10は32ビットで表すと~。とか・・・
(この辺は、改めて勉強させて頂きますので)
まだ良く解って無いのですが、このソースを(VC++ 2008で)コンパイルして実行した結果
a = 0000000A
~a = FFFFFFF6
となったのですが、参考書の実行結果を見ると
a = 0000000A
~a = FFFFFFF5
と、なってました。
これは、いかんっ!と思い
1冊目の参考書のコンパイラが、「gcc」というコンパイラを使っての勉強だったので
gccで、このソースをコンパイルした所、結果は
a = 0000000A
~a = FFFFFFF6
こちらにお邪魔する前まで、コンパイラの意味も良く解らない時に使っていた
BCC Developer 1221 でコンパイラした所、結果は
a = 0000000A
~a = FFFFFFF6
でした。ん~む~?
そこで、2冊目の参考書を発刊されている方のページがありましたので(クレームとかでは無いですよ。)
そのページを見た所、VC++ 2006 で、ご解説がされていました。
今、勉強している参考書は、2008年9月3日 第2版 第3刷 発行
となってます。
上記のソースの実行結果(~a = FFFFFFF6)が
VC++ 2006 と VC++ 2008とでは違っているのでしょうか?
細かい事かもしれませんが、悩んでます。
VC++ 2006 でコンパイルして実行すると
a = 0000000A
~a = FFFFFFF5
となるのでしょうか?
パソコンのスペックとかの違いでも、この結果が異なるのでしょうか?^^;
VC++ 2006 をお使いの方がいらっしゃいましたら
お時間ある時にでも、実行された結果を頂けたらと思います。
何とぞ、宜しくお願い致します。
Re:雑談として、お願いします。
Posted: 2008年11月05日(水) 01:07
by 焼き魚定食
こんばんは。はじめまして。
VC++ 2005ですが参考になればと書き込みました。
書かれていたソースをそのままコンパイルしてみたら
a = 0000000A
~a = FFFFFFF6
が出力されました。
多分コンパイラとか、環境は関係ないと思います。
補数演算子というものをはじめて知ったのですが、補数演算子は-ではなく~ だそうです。だから
printf("a = %08X\n~a = %08X\n", a, -a);
ではなく
printf("a = %08X\n~a = %08X\n", a, ~a);
としたら
a = 0000000A
~a = FFFFFFF5
と出力されましたよ。
Re:雑談として、お願いします。
Posted: 2008年11月05日(水) 01:22
by 究極の初心者
焼き魚定食さんも言ってますが、
printf("a = %08X\n~a = %08X\n", a, -a);
//↑ここの部分。
の箇所で「~a」を「ーa」に打ち間違えたか、あるいは、そのコードが本当に参考書に書いてあるならば、参考書の誤りですね。
「~」の演算子は「補数」というらしいです。
私も初めて知りました^^;
おそらく、使用コンパイルで二進数の値などが変わるということはないです。
アドレスなどは、使用状況によって変わりますが・・・・。
参考書が間違えていることも、あまり珍しいことではないですので、確かめてみてください^^;
Re:雑談として、お願いします。
Posted: 2008年11月05日(水) 01:44
by ムンバ
もう本当に、自分が嫌です。すみませんでした。(涙
printf("a = %08X\n~a = %08X\n", a, -a);
の
-a が ~a の間違いだったのですね。
焼き魚定食さん、ご解説ありがとう御座いました。
何度もソースを確認してから、こちらの掲示板に質問させて頂いたつもりだったのですが・・・^^;
~a と修正したら
a = 0000000A
~a = FFFFFFF5
と実行結果が出ました。
まだまだ勉強不足です。
と言いますか、確認不足だったのですね。<(_ _)>
どうもありがとう御座いました。
Re:雑談として、お願いします。
Posted: 2008年11月05日(水) 02:10
by ムンバ
>printf("a = %08X\n~a = %08X\n", a, -a);
//↑ここの部分。
究極の初心者さん、ご解説ありがとう御座います。
焼き魚定食さんへの、ご返信を打ち込んでる間にかぶってしまいました。申し訳御座いません。
老眼鏡が、そろそろ必要になってるのかもしれません。(汗
勉強させて頂いている参考書を、もう一度見直してみたら
~a
と、なっていました。この辺、ちゃんと確認しないといけないですね。すみませんでした。
私の見間違いでした。(泣
>「~」の演算子は「補数」というらしいです。
補数演算子と参考書になってましたので、補数演算子と使わせて頂きました。
今まで聞いた事が無い様な言葉もたくさん出てきて、困惑しているのですが^^;
勉強させて頂きます。
ありがとうございました。
Re:雑談として、お願いします。
Posted: 2008年11月05日(水) 02:19
by ムンバ
↑解決マーク忘れてました。すみません。
ご解説、どうもありがとう御座いました。(謝
Re:雑談として、お願いします。
Posted: 2008年11月05日(水) 07:38
by たかぎ
~aではなく-aとした場合でも、コンパイラによってはFFFFFFF5になり得ます。
具体的には、負の整数値の内部表現に1の補数を使う場合です。
Re:雑談として、お願いします。
Posted: 2008年11月05日(水) 10:48
by ムンバ
おはようございます。
たかぎさん、いつもご解説ありがとうございます。
少し慣れてきたと思ってても、まだまだですね。
しかも、写し違いだったなんて・・・。^^;
お騒がせしました。
今後とも宜しくお願い致します。
ありがとう御座いました。