ページ 11

引数をランダムに

Posted: 2008年10月26日(日) 02:50
by 久美
ある変数に引数からランダムで数字をいれるという関数をつくりたいのですが
どうすればよいのかわかりません><

void RandNum(int *Num,・,・,・);
Numに・・・の中のどれかがランダムで入る。
・・・の数はその時によって変わる。 

たとえば
RandNum(int &a, 3, 5)とかいたら
aに3か5が入り、
RandNum(int &a, 3, 5, 7)とかいたら
aに3か5か7が入ります。

また調べていたら引数でFormatと書いてある所が
あったのですがどういう意味なのでしょうか?

Re:引数をランダムに

Posted: 2008年10月26日(日) 03:19
by Dixq (管理人)
これでどうでしょう?
int SelectRandNum( int a[/url], int n ){
	return ( a [ GetRand( n-1 ) ] );
}

aにはランダムで返したい数字が入っています。
nはその個数です。

int a[5]={6,1,3,2,8}, ans;
ans = SelectRandNum( a, 5 );

このように呼ぶとaの中の数字のどれかが返ります。
 

Re:引数をランダムに

Posted: 2008年10月26日(日) 03:23
by Dixq (管理人)
あ、すみません、DXライブラリじゃないのならGetRandじゃなく
randで0からnum未満の乱数を取得して下さい。

Re:引数をランダムに

Posted: 2008年10月26日(日) 06:49
by box
質問者さんは可変個の引数を受取る関数を
実装しようとしているようです。
管理人さんの話とはちょっと違うような気がします。

Re:引数をランダムに

Posted: 2008年10月26日(日) 09:12
by たかぎ
これは規格厳密合致プログラムでは実現できそうもありません。
とりあえず、GCCを使って次のようにすれば実現可能です。
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

#define RandNum(var_decl, ...)  const var_decl = RandNumHelper(sizeof((int[/url]){__VA_ARGS__})/sizeof(int), __VA_ARGS__)

int RandNumHelper(size_t n, ...)
{
  n = rand() % n;

  va_list ap;
  va_start(ap, n);

  int result;
  for (size_t i = 0; i <= n; i++)
  {
    result = va_arg(ap, int);
  }

  va_end(ap);
  return result;
}

int main(void)
{
  for (int i = 0; i < 10; i++)
  {
    RandNum(int &a, 1, 2, 3);
    printf("%d\n", a);
  }
}
引数に int &a を渡したときに a に格納しないといけないとのことですので、C++でなければなりません。

Re:引数をランダムに

Posted: 2008年10月26日(日) 09:32
by バグ
たかぎさんの後は緊張しますねぇ(^_^;)
とりあえず、関数部だけ…

C++やC#で書くことが多いのでmalloc関数を使用する機会が少ないので、なんか新鮮でした(^-^)

// int* data = データ格納用バッファへのポインタ
// int num = 可変個引数の個数
// 戻り値 = 数値取得に成功したら1を、エラー時には0を返します
int RandNum(int* data, int num, ...)
{
// 可変個引数の数値を一時的に格納するメモリを確保
int* buf = (int*)malloc(sizeof(int) * num);
if (buf == NULL)
{
// メモリの確保に失敗したので0を返す
return 0;
}

// 可変個引数の取得
va_list list;
va_start(list, num);
for (int i = 0; i < num; ++i) *(buf + i) = va_arg(list, int);
va_end(list);

// *dataにランダムに選択した数値を格納する
*data = *(buf + rand() % num);

// メモリの解放
free(buf);

// 数値取得に成功したので1を返す
return 1;
}

Re:引数をランダムに

Posted: 2008年10月26日(日) 12:21
by たかぎ
バグさん
//形式のコメントや for (int i = 0; i < num; ++i) という書き方から見てC99を想定しているのでしょうが、それならmallocを使わなくても可変長配列で十分な気がします。
そもそも、mallocも可変長配列も不要ですが...

この質問の難しいところは、可変個引数の個数が分からないため、自動的に認識させることと、第1引数に int &a のような宣言を渡すところです。void RandNum(int *Num,・,・,・); という関数原型と矛盾しているような気もしますが...

Re:引数をランダムに

Posted: 2008年10月26日(日) 12:41
by たかぎ
第1実引数が int &a ではなく &a でよいのであれば、規格厳密合致プログラムが可能になります。
#include <stdio.h>
#include <stdlib.h>

#define RandNum(result, ...)  \
  ((void)(*(result) = (int[/url]){__VA_ARGS__}[rand() % (sizeof((int[/url]){__VA_ARGS__})/sizeof(int))]))

int main(void)
{
  for (int i = 0; i < 10; i++)
  {
    int a;
    RandNum(&a, 1, 2, 3);
    printf("%d\n", a);
  }
}
今度はC99で扱えます。ただし、このマクロは標準C++では使えません。

Re:引数をランダムに

Posted: 2008年10月26日(日) 13:04
by 久美
このサイトの方たちは皆さん優しいですね><
えっと、言語はC++です。

Dixq (管理人)さんの

int SelectRandNum( int a[/url], int n ){
return ( a [ GetRand( n-1 ) ] );
}
int a[5]={6,1,3,2,8}, ans;
ans = SelectRandNum( a, 5 );

を使いますね>< と言いますか、これしか理論がわからないw
皆さん天才ですか?
できれば配列を使わずにこれをしたいのですが無理そうですのでこれでいきます。
もし、他にもできそうなのでしたらお願いします。

Re:引数をランダムに

Posted: 2008年10月26日(日) 14:03
by バグ
>>たかぎさん
確かに、わざわざmallocを使う必要無かったですね。ご指摘有り難うございます(^-^)

>>久美さん
C++ならば、vectorに任意の値を格納し、random_shuffleでシャッフルして、先頭の値を返すとかでいいんじゃないでしょうか?

Re:引数をランダムに

Posted: 2008年10月26日(日) 15:41
by Dixq (管理人)
配列を使わずに、かつわかりやすくということでしたら、こういうのはどうでしょう。

int SelectRandNum(int a, int b){
	int *p[2]={&a,&b};
	*p += rand()%2;
	return **p;
}

a,bのアドレスをポインタ配列pにいれます。
最初*pはaをさしています。もし*pに1を足すと、bをさしますよね。
それを利用して0か1どっちかを足せばaかbどっちかをさします。
*pに入っている変数の中身を返す
return **p;
するとaかbどちらかが返る事になります。
効率は悪いですが、これを沢山作るとか・・。

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

int SelectRandNum(int a, int b){
	int *p[2]={&a,&b};
	*p += rand()%2;
	return **p;
}
int SelectRandNum(int a, int b, int c){
	int *p[3]={&a,&b,&c};
	*p += rand()%3;
	return **p;
}
int SelectRandNum(int a, int b, int c, int d){
	int *p[4]={&a,&b,&c,&d};
	*p += rand()%4;
	return **p;
}

int main(void){ 
	printf("%d\n",SelectRandNum(1,2));
	printf("%d\n",SelectRandNum(2,3,4));
	printf("%d\n",SelectRandNum(5,6,7,8));
	return(0);
} 

実行結果

2
4
7
 

Re:引数をランダムに

Posted: 2008年10月26日(日) 16:22
by たかぎ
C++で、実引数の個数の上限が決まっているのであれば、多重定義すれば(実装は手間ですが)簡単に実現できます。

Re:引数をランダムに

Posted: 2008年10月26日(日) 18:45
by tk-xleader
引数40個までで、実装してみました。
#include<cstdio>
#include<cstdlib>
#include<ctime>
using namespace std;
/*↑
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
の代わり(警告対策)*/

class Input{
	int value;
	bool isendmark;
	public:
	Input(int invalue){
		value=invalue;
		isendmark=false;
	}
	Input(){
		value=-1;//適当に代入しておく(警告封じ)。
		isendmark=true;
	}
	bool IsEndMark()const{
		return isendmark;
	}
	int GetValue()const{
		return value;
	}
};

void RandNum(int *Num,
	const Input&arg1=Input(),
	const Input&arg2=Input(),
	const Input&arg3=Input(),
	const Input&arg4=Input(),
	const Input&arg5=Input(),
	const Input&arg6=Input(),
	const Input&arg7=Input(),
	const Input&arg8=Input(),
	const Input&arg9=Input(),
	const Input&arg10=Input(),
	const Input&arg11=Input(),
	const Input&arg12=Input(),
	const Input&arg13=Input(),
	const Input&arg14=Input(),
	const Input&arg15=Input(),
	const Input&arg16=Input(),
	const Input&arg17=Input(),
	const Input&arg18=Input(),
	const Input&arg19=Input(),
	const Input&arg20=Input(),
	const Input&arg21=Input(),
	const Input&arg22=Input(),
	const Input&arg23=Input(),
	const Input&arg24=Input(),
	const Input&arg25=Input(),
	const Input&arg26=Input(),
	const Input&arg27=Input(),
	const Input&arg28=Input(),
	const Input&arg29=Input(),
	const Input&arg30=Input(),
	const Input&arg31=Input(),
	const Input&arg32=Input(),
	const Input&arg33=Input(),
	const Input&arg34=Input(),
	const Input&arg35=Input(),
	const Input&arg36=Input(),
	const Input&arg37=Input(),
	const Input&arg38=Input(),
	const Input&arg39=Input(),
	const Input&arg40=Input()
){
	Input arglist[/url]={
		arg1,
		arg2,
		arg3,
		arg4,
		arg5,
		arg6,
		arg7,
		arg8,
		arg9,
		arg10,
		arg11,
		arg12,
		arg13,
		arg14,
		arg15,
		arg16,
		arg17,
		arg18,
		arg19,
		arg20,
		arg21,
		arg22,
		arg23,
		arg24,
		arg25,
		arg26,
		arg27,
		arg28,
		arg29,
		arg30,
		arg31,
		arg32,
		arg33,
		arg34,
		arg35,
		arg36,
		arg37,
		arg38,
		arg39,
		arg40
	};
	int count;
	for(count=0;count<sizeof(arglist)/sizeof(arglist[0]);count++){
		if(arglist[count].IsEndMark())break;
	}
	if(count<1)return ;
	*Num=arglist[rand()%count].GetValue();
}

int main()
{
	int rnum;
	srand((unsigned)time(0));
	RandNum(&rnum,10,30,65,20,54,42);
	printf("%d\n",rnum);
	return 0;
}
コードがめちゃくちゃですが、一応動作チェックはしています。

Re:引数をランダムに

Posted: 2008年10月29日(水) 01:35
by hoge
雑談になりますが、
No:23076のたかぎさんの発言に反応

> これは規格厳密合致プログラムでは実現できそうもありません。

多分、C99規格に合致していると思われるコードが書けました。
// ヘッダは省略

#define RandNum(var_decl, ...)  RandNum##var_decl, __VA_ARGS__)
#define RandNumint RandNumintHelper(
#define RandNumintHelper(result, ...) \
((void)(*(result) = (int[/url]){__VA_ARGS__}[rand() % (sizeof((int[/url]){__VA_ARGS__})/sizeof(int))]))

int main(void)
{
  for (int i = 0; i < 10; i++)
  {
    int a;
    RandNum(int &a, 1, 2, 3);
    printf("%d\n", a);
  }
}