LoginSignup
2
1

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