2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

HSPでコルーチンを使った乱数ジェネレーターの実装。

Last updated at Posted at 2018-01-24

newlabの使い道を頑張って考えてみる

HSPにもコルーチンがあるということに衝撃を受けたので、線形合同法で簡単に乱数ジェネレーターを作ってみました。
乱数と言ったらジェネレーター、ジェネレーターっと言ったら乱数みたいな。
ちなみにコルーチンを使わない乱数ジェネレーターは以前別に実装しています。XorShiftの実装

実行例
【乱数ジェネレーター】

34
10618
34

251
8804
251

1770
61641
1770

12403
38290
12403

21299
5903
21299

シード値が同一な上段と下段は同じ値が取得できます。

線形合同法

まず、線形合同法とは。

Xn = (A × Xn-1 + C) mod M

の式によって求まる最も簡単な乱数のひとつです。
X0・A・C・Mは初期値としてシード値として与え、
Xnの値は常に変化します。

実装

#runtime "hsp3cl"

#module __RandGen Xn,A,C,M,yieId,value
	#define global RandGen(%1,%2=3,%3=7,%4=13,%5=0xFFFF) \
		dimtype %1,5 :\
		newmod %1,__RandGen,%2,%3,%4,%5
	#modinit int X0,int _A,int _C,int _M
		Xn=X0: A=_A: C=_C: M=_M
		newlab yieId,1: return
		*infLoop
			Xn=(A*Xn+C)\M
			value=Xn
			newlab yieId,1: return
		goto*infLoop
	return

	#modcfunc genNextValue
		gosub yieId
	return value
#global

#module Program
	#deffunc main
		sdim _input
		mes "【乱数ジェネレーター】"
		RandGen g1
		RandGen g2,1515
		RandGen g3
		repeat
			input _input,,1
		    mes genNextValue(g1)
		    mes genNextValue(g2)
		    mes genNextValue(g3)
		loop
		
	return
#global
main

複数のジェネレーターを保持するためにモジュール型変数を使用しています。
細かいコルーチン文法については元記事参照。
なお、コルーチン内のループはreturnを挟むため、cntが破綻してしまうrepeatの使用は避けています。

比較の実装(Node.js)

比較用にJavaScriptでジェネレーターを用いた実装を載せておきます。

function* RandGen(Xn=3,A=7,C=13,M=0xFFFF){
	for(;;){
		yield Xn=(A*Xn+C)%M
	}
}

(async function(){
	console.log("【乱数ジェネレーター】");
	const g1=RandGen();
	const g2=RandGen(1515);
	const g3=RandGen();
	for(;;){
		await new Promise(res=>process.stdin.once("data",res));
		console.log(g1.next().value);
		console.log(g2.next().value);
		console.log(g3.next().value);
	}
})();

おまけ

見た目をjsとかをその他のyieldな言語に近づけてみた。

#runtime "hsp3cl"

#module __RandGen Xn,A,C,M,yieId,value
	#define yieId(%1="__undefined__") value=%1: newlab rezume,1: return
	#modfunc genNext
		gosub rezume: return
	#modcfunc genValue
		return value
	#modcfunc genNextValue
		gosub rezume: return value

	#define global RandGen(%1,%2=3,%3=7,%4=13,%5=0xFFFF) \
		dimtype %1,5 :\
		newmod %1,__RandGen,%2,%3,%4,%5
	#modinit int seed,int _A,int _C,int _M
		Xn=seed: A=_A: C=_C: M=_M
		yieId
		*infLoop
			Xn=(A*Xn+C)\M
			yieId Xn
		goto*infLoop
	return
#global

#module Program
	#deffunc main
		sdim _input
		mes "【乱数ジェネレーター】"
		RandGen g1
		genNext g1
		RandGen g2,1515
		RandGen g3
		repeat
			input _input,,1
		    mes genNextValue(g1)
		    mes genNextValue(g2)
		    mes genNextValue(g3)
		loop
		
	return
#global
main

終わりに

結構普通のコルーチン(ジェネレーター)として使えて満足でした。
モジュールの代わりに特異な変数を埋め込んだdefineでも行けるかな?

2
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?