乱数について

　
この記事は[アドカレ](https://qiita.com/advent-calendar/2022/lua)に参加しています。

# 乱数について
c言語のrand関数がボロクソに言われている現場を目撃したことがあるでしょうか。僕はあります。

まだその概要をご存知ない方は以下の記事をお読みください。
[良い乱数・悪い乱数](http://isaku-wada.my.coocan.jp/rand/rand.html)
[C言語による乱数生成](https://omitakahiro.github.io/random/random_variables_generation.html)

…

………

……………

多くは語りません。
aviutlの乱数関数も、かなりまずい状態にあります。
[新鋭のプラグイン](https://scrapbox.io/ePi5131/patch.aul)がその改善に当たりましたが、その成果は芳しくない模様。バグがあるため(?)乱数の改善機能はデフォルトでoffになっています。

## lua用の乱数dllを作る
プラグインで特定の関数をハックするなんて芸当は、僕にはできません。そもそもバイナリ読めません。
　
ですが、事態は深刻です。
仕方がないので、luaからrequireできるdllを書きます。

追記：コメントのご指摘通りに修正しました。@yumetodoさん、ありがとうございます！
```c++

#include <lua.hpp>
#include <new>
#include <random>

int r(lua_State* L) {
    int n = lua_tointeger(L, 1);//個数
    int seed = lua_tointeger(L, 2);//seed値
    double sa = lua_tonumber(L, 3);//下限値
    double fa = lua_tonumber(L, 4);//上限値
    int type = lua_tointeger(L, 5);//type
    lua_newtable(L);//戻り値用
    /*
    type
    0 32bit整数(int)
    1 32bit実数(float)
    2 64bit実数(double)
    */

    if (type == 0 || type == 1) {
        std::mt19937 mt(seed);
        if (type == 0) {
            std::uniform_int_distribution<int> mt2(sa, fa);
            for (int i = 0; i < n; i++) {
                lua_pushinteger(L, mt2(mt));
                lua_rawseti(L, 6, i + 1);
            }
        }
        else {
            std::uniform_real_distribution<float> mt2(sa, fa);
            for (int i = 0; i < n; i++) {
                lua_pushnumber(L, mt2(mt));
                lua_rawseti(L, 6, i + 1);
            }
        }
    }
    else {
        std::mt19937_64 mt(seed);
        std::uniform_real_distribution<double> mt2(sa, fa);
        for (int i = 0; i < n; i++) {
            lua_pushnumber(L, mt2(mt));
            lua_rawseti(L, 6, i + 1);
        }
    }

    return 1;
}

static luaL_Reg functions[] = {
    {"r",r},
    {nullptr,nullptr}
};

extern "C" {
    __declspec(dllexport) int luaopen_M_MersenneTwister_Module(lua_State* L) {
        luaL_register(L, "M_MersenneTwister_Module", functions);
        return 1;
    }
}
```
実装方法は単純で、c++の標準ライブラリを使うだけです。
少し使いかたが特殊ですが、これも乱数のためと思えば。
http://s170199.ppp.asahi-net.or.jp/tech/cpp/random.html
https://qiita.com/Tajitam/items/36edcca3260b4bca226f
https://ez-net.jp/article/D5/27Xc9uFC/CrPFm9AgKLn5/

## 引数と戻り値について
##### M_MersenneTwister_Module.r(n,seed,sa,fa,type)
n-戻り値の要素の数。
seed-シード値。整数値(int型の範囲内)で。
sa-乱数の下限値。
fa-乱数の上限値。(乱数はこの数未満になるらしい？？)
type-以下のようになっている。
    0-32bit整数(int)
    1-32bit実数(float)
    2-64bit実数(double)
戻り値-n個の乱数が入ったテーブル。

## 比較してみる
obj.randと、今回作成したdllとで比較してみます。
画像は白黒とカラーの二つを用意しました。
左がdll、右がobj.randです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005402/6e628f2e-664d-b821-cb1e-c1cb000f709d.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005402/74c22562-73f6-179a-9fe6-50f8ad213027.png)

一応、コードものせときます。
　
白黒
```lua
local ffi=require"ffi"
pcall(ffi.cdef,[[
    typedef struct Pixel_ {
      uint8_t b,g,r,a;
    } Pixel;
  ]])

local data,w,h=obj.getpixeldata()
local cdata=ffi.cast("Pixel*",data)

local d=require("M_MersenneTwister_Module")
local a=d.r(w*h,obj.frame,0,255,0)

for y=0,h-1 do
 for x=0,math.floor(w/2) do
  local ix=y*w+x
  local p=cdata+ix
  p.b=a[ix+1]
  p.g=a[ix+1]
  p.r=a[ix+1]
 end
end
a=nil

for y=0,h-1 do
 for x=math.floor(w/2),w-1 do
  local ix=y*w+x
  local p=cdata+ix
  local s=rand(0,255,ix)
  p.b,p.g,p.r=s,s,s
 end
end

obj.putpixeldata(data)
```
　
カラー
```lua
local ffi=require"ffi"
pcall(ffi.cdef,[[
    typedef struct Pixel_ {
      uint8_t b,g,r,a;
    } Pixel;
  ]])

local data,w,h=obj.getpixeldata()
local cdata=ffi.cast("Pixel*",data)

local d=require("M_MersenneTwister_Module")
local a=d.r(w*h*3,obj.frame,0,255,0)

for y=0,h-1 do
 for x=0,math.floor(w/2) do
  local ix=y*w+x
  local p=cdata+ix
  ix=ix*3
  p.b=a[ix+1]
  p.g=a[ix+2]
  p.r=a[ix+3]
 end
end
a=nil

for y=0,h-1 do
 for x=math.floor(w/2),w-1 do
  local ix=y*w+x
  local p=cdata+ix
  ix=ix*3
  p.b=rand(0,255,ix+1)
  p.g=rand(0,255,ix+2)
  p.r=rand(0,255,ix+3)
 end
end

obj.putpixeldata(data)
```

## さいごに
もしかしたら、未だによろしくない乱数を使用しているものが他にもあるかもしれません。
　
すべての人々に質の良い乱数が行き渡ることを祈るばかりです。