前回のコードは、元にしたプログラムがゲーム用に最適化されちゃっていて、もとの論文と比較しにくかったので、乱数生成部分を書き直しました。
WELL512a と WELL1024a, はC版と同じ結果が返ることを確認しています。
それ以外は、たぶん動くけど未検証なので、コメントアウトしてあります。
(誰か検証してください!)
あと、jumpahead()は、未実装にしました。seed()使って下さい。
本来の意味だと、数学的に離れた位置にジャンプするべきなのですが、
やり方がわからないんで、前はseed()を使ってお茶を濁してました。
これは、かえって混乱のもとなのでやめました。
# -*- coding: utf-8 -*- # --------------------------------------------------------------------------- # WELL(512a, 1024a)のPython実装 # # 元の論文との対応付けのし易さを最重視して実装しています。 # 状態の初期化にSHA512を使うのがオリジナルのアイディアです。 # 512より多い状態を初期化する際を考慮して、 # SHA512の繰り返しとXorShiftのXORで状態を初期化しています。 # # パフォーマンスを求める方は、CやC++版を使って下さい。 # # このコードを作成するにあたり、参考にしたのは、以下です # # - http://d.hatena.ne.jp/fgshun/20090724/1248446400 # - http://www.iro.umontreal.ca/~panneton/WELLRNG.html # - http://www3.ocn.ne.jp/~harase/megenerators.html # - http://bitbucket.org/sergiu/random/src/tip/well.hpp # - http://en.wikipedia.org/wiki/Well_Equidistributed_Long-period_Linear # # このコードのライセンスは、MITでお願いします。 # # --------------------------------------------------------------------------- from random import Random as _Random from os import urandom as _urandom from binascii import hexlify as _hexlify from hashlib import sha512 as _sha512 from itertools import cycle as _cycle, izip as _izip # --------------------------------------------------------------------------- # 各WELLのベースクラス定義 # # random_int32()が未実装なので、単独では使用できません。 # init_subclassと_M1~_M6を使用して、random_int32()を初期化して使って下さい。 # --------------------------------------------------------------------------- def _xor128(x=123456789, y=362436069, z=521288629, w=88675123): while True: t = x^((x << 11) & 0xffffffff) x = y y = z z = w w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)) yield w class _WELLBase(_Random): _W = 32 _MASK = 2 ** _W - 1 def __init__(self, seed=None): self.state = self._gen_seed(seed) self._check_zerostate() self.gauss_next = None self.index = 0 def _check_zerostate(self): if self.state == [0] * self._R: self.state = self._gen_seed(self.VERSION) @classmethod def _gen_seed(cls, a=None): if a is None: try: a = int(_hexlify(_urandom(16)), 16) except NotImplementedError: import time a = int(time.time() * 256) if not isinstance(a, basestring): a = str(a) hashed_states = _sha512(a).hexdigest() states = [int(hashed_states[i: i+8], 16) for i in range(0, 128, 8)] return [xor128 ^ sha512 for (i, xor128, sha512) in _izip(range(cls._R), _xor128(), _cycle(states))] def seed(self, a=None): self.__init__(a) def random_int32(self): raise NotImplementedError def random(self): return self.random_int32() * 2.3283064365386963e-10 def random53(self): return self.getrandbits(53) * (1.0 / 9007199254740992.0) def getstate(self): return (self.VERSION, tuple(self.state), self.gauss_next, self.index) def setstate(self, state): version = state[0] if version == self.VERSION: self.state = list(state[1]) self.gauss_next = state[2] self.index = state[3] else: raise ValueError("state with version %s passed to " "setstate() of version %s" % (version, self.VERSION)) def jumpahead(self, n): raise NotImplementedError def getrandbits(self, k=32): ret = self.random_int32() for _ in range((k - 1) // 32): ret = (ret << 32) ^ self.random_int32() return ret >> ((- k) % 32) @classmethod def init_random_int32(cls, T0, T1, T2, T3, T4, T5, T6, T7, m1, m2, m3, me_mask=0): MASKU = cls._MASK >> (cls._W - cls._P) MASKL = cls._MASK << cls._P r = cls._R def random_int32(self): s = self.state newV1 = i = self.index newV0 = i_VRm1 = (i - 1) % r i_VRm2 = (i - 2) % r vm2 = s[(i + m2) % r] z0 = (s[i_VRm1] & MASKL) | (s[i_VRm2] & MASKU); z1 = T0(s[i]) ^ T1(s[(i + m1) % r]) z2 = T2(vm2 ) ^ T3(s[(i + m3) % r]) s[newV1] = z3 = z1 ^ z2 s[newV0] = z4 = T4(z0) ^ T5(z1) ^ T6(z2) ^ T7(z3) self.index = i_VRm1 return z4 ^ (vm2 & me_mask) cls.random_int32 = random_int32 @classmethod def _output_after_tempering(cls, b, c): random_int32 = cls.random_int32 def random_int32_after_tempering(self): y = random_int32(self) y = y ^ ((y << 7) & b) y = y ^ ((y << 15) & c) return y cls.random_int32 = random_int32_after_tempering @classmethod def _shift(cls, k): MASK = cls._MASK if k >= 0: return lambda v: (v >> k) else: k_abs = abs(k) return lambda v: (v << k_abs) & MASK @classmethod def _M0(cls): return lambda v: 0 @classmethod def _M1(cls): return lambda v: v @classmethod def _M2(cls, t): return cls._shift(t) @classmethod def _M3(cls, t): t_shift = cls._shift(t) return lambda v: v ^ t_shift(v) @classmethod def _M4(cls, a): w1 = cls._W - 1 return lambda x: (x >> 1) ^ ((x & 1) * a) @classmethod def _M5(cls, t, b): t_shift = cls._shift(t) return lambda v: v ^ (t_shift(v) & b) @classmethod def _M6(cls, r, a, ds, dt): q_shift = cls._shift( - r) wq_shift = cls._shift(cls._W - r) return lambda x: ((q_shift(x) ^ wq_shift(x)) & ds) ^ (a if (x & dt) != 0 else 0) # ------------------------------------------------------ # 個別のWELLの定義 # ------------------------------------------------------ class WELL512a(_WELLBase): VERSION = u"WELL512a ver0" _K = 512 # not use _R = 16 _P = 0 WELL512a.init_random_int32( m1=13, m2=9, m3=5, T0=WELL512a._M3(-16), T4=WELL512a._M3(-2), T1=WELL512a._M3(-15), T5=WELL512a._M3(-18), T2=WELL512a._M3(11), T6=WELL512a._M2(-28), T3=WELL512a._M0(), T7=WELL512a._M5(-5, 0xda442d24)) class WELL1024a(_WELLBase): VERSION = u"WELL1024a ver0" _K = 1024 # not use _R = 32 _P = 0 WELL1024a.init_random_int32( m1=3, m2=24, m3=10, T0=WELL1024a._M1(), T4=WELL1024a._M3(-11), T1=WELL1024a._M3(8), T5=WELL1024a._M3(-7), T2=WELL1024a._M3(-19), T6=WELL1024a._M3(-13), T3=WELL1024a._M3(-14), T7=WELL1024a._M0()) # 以下、未検証のためコメントアウト # #class WELL1024b(_WELLBase): # VERSION = u"WELL1024b ver0" # _K = 1024 # not use # _R = 32 # _P = 0 # #WELL1024b.init_random_int32( # m1=22, m2=25, m3=26, # T0=WELL1024b._M3(-21), T4=WELL1024b._M3(-14), # T1=WELL1024b._M3(17), T5=WELL1024b._M3(-21), # T2=WELL1024b._M4(0x8bdcb91e), T6=WELL1024b._M1(), # T3=WELL1024b._M3(15), T7=WELL1024b._M0()) # #class WELL19937a(_WELLBase): # VERSION = u"WELL19937a ver0" # _K = 19937 # not use # _R = 624 # _P = 31 # #WELL19937a.init_random_int32( # m1=70, m2=170, m3=449, # T0=WELL19937a._M3(-25), T4=WELL19937a._M1(), # T1=WELL19937a._M3(27), T5=WELL19937a._M3(-9), # T2=WELL19937a._M2(9), T6=WELL19937a._M3(-21), # T3=WELL19937a._M3(1), T7=WELL19937a._M3(21)) # #class WELL19937b(_WELLBase): # VERSION = u"WELL19937b ver0" # _K = 19937 # not use # _R = 624 # _P = 31 # #WELL19937b.init_random_int32( # m1=203, m2=613, m3=123, # T0=WELL19937b._M3(7), T4=WELL19937b._M3(-19), # T1=WELL19937b._M1(), T5=WELL19937b._M2(-11), # T2=WELL19937b._M3(12), T6=WELL19937b._M3(4), # T3=WELL19937b._M3(-10), T7=WELL19937b._M3(-10)) # #class WELL19937c(WELL19937a): # VERSION = u"WELL19937c ver0" # #WELL19937c._output_after_tempering(0xe46e1700, 0x9b868000) # #class WELL44497a(_WELLBase): # VERSION = u"WELL44497a ver0" # _K = 44497 # not use # _R = 1391 # _P = 15 # #WELL44497a.init_random_int32( # m1=23, m2=481, m3=229, # T0=WELL44497a._M3(-24), T4=WELL44497a._M1(), # T1=WELL44497a._M3(30), T5=WELL44497a._M3(20), # T2=WELL44497a._M3(-10), T6=WELL44497a._M6(9, 0xb729fcec, # 0xfbffffff, 0x00020000), # T3=WELL44497a._M2(-26), T7=WELL44497a._M1()) # #class WELL44497b(WELL44497a): # VERSION = u"WELL44497b ver0" # #WELL44497b._output_after_tempering(0x93dd1400, 0xfa118000) # # EOF
イッシーの利用目的だと、WELL512aでおそらく十分ですので、
要望や助力がない限りは、これ以上の実装例は増やさない予定です。
英語読めなくても、論文とソースを見比べていけば、なんとななるはずです。
(イッシーがそうです)
あと、init_random_int32()の、me_maskは、高次最適化の為のオプションです。
これはそのうち対応したいなぁ。
0 件のコメント:
コメントを投稿