LoginSignup
1
1

More than 3 years have passed since last update.

VBAで10桁のユニークナンバーを発行する

Posted at

初投稿なので、手探りで書いていきます。

元ネタの質問

「10桁のランダムな数字をそれまでに発生していない数字で」
http://www.excel.studio-kazu.jp/kw/20190522114232.html

質問の内容は10桁の乱数を発行するマクロが欲しいというもの。
それに対する回答は概ねすべて「乱数を作って被ったら作り直す」というもの。
10桁なのでそれでいいかなと思いつつも、
作り直すループをせずとも生成できないものかと思い調べたらすぐ見つかった。

「重複のない10桁の数字をIDとして採番するアルゴリズムを教えて下さい。」
http://q.hatena.ne.jp/1377468971

PHPかぁ。やり方を調べてVBAに作り変えてみよう

コンピュータの扱う自然数(2のべき乗を位数とする巡回群)のそれぞれの元は奇数定数をかけると必ず相異なる結果になる

んん?この話、どこかで聞いたことがある。どこかで…
数学ガールだ!これ数学ガールで見た!!
時計盤の数字を等間隔で線で結ぶと、特定の物は全部の数字を経由するってやつだ。

位数nの巡回群の元p(p<n, pとnは互いに素)には逆数が存在する

互いに素!!知ってる!数学ガールで見た!(しつこい)
フェルマーの最終定理の巻だったかしら。そうかこうやって使うんだ。
本の内容を実際に使えるとなんか嬉しい。

相違なる結果とやら

とりあえずやってみよう。
互いに素ということなので2と5で割り切れなければいいのよね。

Sub RndTest1()
    For i = 1 To 100
        x = (i * 777) Mod 100
        Cells(i, 1) = x
    Next
End Sub

あら、簡単。
なるほど、シャッフルができた。だがこれでは明らかに法則がモロ見えしている。
ここでビット逆順とかいうものをして3回繰り返せばいいわけだ。

ここで問題が2つ。

  • 桁を増やすとすぐオーバーフローする
  • ビット逆順?なにそれ?

オーバーフロー大好きなExcelさん

オーバーフローは10桁くらいどうにかなると思ったが、ちょっとした掛け算ですぐエラーになる。
doubleとか使えばいいかと思ったけど、そもそもmodなんかは 型宣言無視してオーバーフローする
あかん(あかん)

2つに分けてみよう。それで桁が溢れたら上の桁に足そう。

Sub RndTest2()
    For i = 1 To 10
        x1 = Int(i / 1000) + 1
        x2 = i Mod 1000
        For j = 1 To 3
            x3 = (Int(x2 * 1.123) + x1 * 7789) Mod 1000
            x2 = (x2 * 1123) Mod 1000
            x1 = x3
        Next
        Cells(i, 1) = x1 * 1000 + x2
    Next
End Sub

ちょっとややこしくなったけどうまく行った。ちなみに1123と7789は自分の好きな素数だ。

ビット逆順とは

要は2進数にして順番を逆にすればいいわけね。オーケーオーケー。
でもそんなVBA聞いたことがない。
(EXCEL2019にはビットなんちゃらとかいう数式が増えたらしいけど)

小一時間後、よく考えたら2進数にする必要ないね、と気づく。
そうだ!123456が654321になれば逆順なのでは!(暴論)
よし、こうしよう。

  x3 = (Int(x2 * 1.123) + x1 * 7789) Mod 1000
  x2 = (x2 * 1123) Mod 1000
  ↓
  x3 = (x2 * 1123) Mod 1000
  x2 = (Int(x2 * 1.123) + x1 * 7789) Mod 1000

計算するついでに上3桁と下3桁を入れ替えよう。つまり123456を456123にしたわけだ。
こういうのをビット回転というの?言わない?あっそう…
まぁなんかビット逆順を使わなくとも、2種類のシャッフルを何度かワチャワチャすれば
予想しづらい並び順に混ざりそうな気がしなくもなくなくない。

最終

こうなった。

Sub RndTest3()
    For i = 1 To 100
        x1 = Int(i / 1000) + 1
        x2 = i Mod 1000
        For j = 1 To 7
            x3 = 1000 - (x2 * 1123) Mod 1000
            x2 = (Int(x2 * 1.123) + x1 * 7789) Mod 1000
            x1 = x3
        Next
        Cells(i, 1) = x1 * 1000 + x2
    Next
End Sub

jのループ回数は、3では微妙に予想できる混ざり方したので7に増やした。
x3で1000から引いてみたのは、なんか混ざるかなぁと思い気分で追加した。
結局10桁と言いつつ6桁だけど、xを増やせば何桁にでもなると思う。
このへんで満足したので終わりにしようと思う。

枠外コメ

ちなみに枠外のコメに以下のようなものがあったが、こちらはビックリするほどわからなかった。
見なかったことにしよう。

M系列の疑似乱数なら同じ値の繰り返しになるので、適当なシード値とXORマスクを用意して
乱数生成 → 1億未満または11桁以上なら乱数生成に戻る → 乱数取出し → 生成した乱数をシードにして先頭に戻る

1
1
2

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
1
1