但し、下記の条件があります。
- 最低でも1億以上、採番可能なもの
- 時系列や連番など推測されやすいものはNG
- 基本的にデータベースを使用せずアルゴリズム内だけて採番(但しカウントアップ用で使うならOK)
- 数字が一意であると保証されていること
以上になります。
PHPのコードで書かれてると、なお有り難いです、
宜しくお願いします。
Lhankor_Mhyさんがコメントでご指摘の通り、どういう使い方をするスクランブル値なのかを考慮しないと的外れになりますよね。
質問文に使い方が書いていないので推測せざるを得ないのですがPHPを使っている以上Webアプリであり、「採番」というからには一度のアクセスで一気に1億個のユニークナンバーがほしいのではなくアクセスかなにかリクエストのたびにユニークナンバーを発生させたいことと推測します。
さて、カウントアップが利用できるならその連番数をスクランブルすることで予測の困難なユニーク値を生成できます。
以前書いたブログ記事ですが
http://cs.hatenablog.jp/entry/2013/06/19/135527
これはC言語版なのでPHP的に書き直すと
{ // 奇数その1の乗算 $v *= 0x1ca7bc5b; $v &= 0x7FFFFFFF; // 下位31ビットだけ残して正の数であることを保つ // ビット上下逆転 $v = ($v >> 15) | (($v & 0x7FFF) << 16); // 奇数その2(奇数その1の逆数)の乗算 $v *= 0x6b5f13d3; $v &= 0x7FFFFFFF; // 下位31ビットだけ残して正の数であることを保つ // ビット上下逆転 $v = ($v >> 15) | (($v & 0x7FFF) << 16); // 奇数その1の乗算 $v *= 0x1ca7bc5b; $v &= 0x7FFFFFFF; // 下位31ビットだけ残して正の数であることを保つ return $v; }
ビット逆順の代わりにビット上下逆転を使っています。
奇数を3回かけていますが、スクランブルするだけならこの奇数にはどんな数字を使ってもかまいません。
まず1億分の配列に連番で取得させます。
その後、配列をランダムにします。
あとは採番のときに 順に配列から値を取得すればいいです。
連番にしないで たとえば 123ごとに 採番するようにするとかしたらいかがでしょうか?
また それをプラスではなく、マイナスさせていくとかすれば 推測しにくいと思われます。
お手軽に実装するにアリかも知れませんが、1億以上も配列を作成するのはちょっと、、
ともあれ回答有り難うございます。
idの重複を確実になくすならこれしか方法はないと思います。
<?php $p=100; $temp=range(1,$p); shuffle($temp); print_r($temp); ?>
たとえば上の例だと$p=100なので、$tempは1~100までの数字を重複なしにランダムに格納した、100の要素の配列になります。
$pの数字は適当にかえてください。
http://codepad.org/KfMrQ2u2
↑こういう結果になります。
回答有り難うございます。参考にさせて頂きます。
No.2の回答では連番生成されるので、簡単にIDが推測されてしまいます。
推測がしにくいユニークIDを発生させる方法としては、uniqid関数とmd5関数の組み合わせが考えられます。
以下のスクリプトは、ユニークIDを10進数10桁に変換し、配列$arrの添え字として1億個を発生させるものです。
$arr = array(); while (count($arr) <= 100000000) { $id = md5(uniqid(rand(),1)); $id = substr(hexdec($id), 0, 10); $arr[$id] = TRUE; }
一気に1億個のユニークIDを発行するのではなく、その都度発行ということでしたら、以前のIDと重ならないように判断するために、以前のIDをすべてどこかに保存しておく必要があります。
ユーザーIDとして使っているなら、ユーザー管理DBがあるはずですから、それを使うのが妥当だと思います。
申し訳ありません。ご指摘通り都度発行でIDを採番したかったのです。
このようなやり方もあるんですね。回答有り難うございました。
Lhankor_Mhyさんがコメントでご指摘の通り、どういう使い方をするスクランブル値なのかを考慮しないと的外れになりますよね。
質問文に使い方が書いていないので推測せざるを得ないのですがPHPを使っている以上Webアプリであり、「採番」というからには一度のアクセスで一気に1億個のユニークナンバーがほしいのではなくアクセスかなにかリクエストのたびにユニークナンバーを発生させたいことと推測します。
さて、カウントアップが利用できるならその連番数をスクランブルすることで予測の困難なユニーク値を生成できます。
以前書いたブログ記事ですが
http://cs.hatenablog.jp/entry/2013/06/19/135527
これはC言語版なのでPHP的に書き直すと
{ // 奇数その1の乗算 $v *= 0x1ca7bc5b; $v &= 0x7FFFFFFF; // 下位31ビットだけ残して正の数であることを保つ // ビット上下逆転 $v = ($v >> 15) | (($v & 0x7FFF) << 16); // 奇数その2(奇数その1の逆数)の乗算 $v *= 0x6b5f13d3; $v &= 0x7FFFFFFF; // 下位31ビットだけ残して正の数であることを保つ // ビット上下逆転 $v = ($v >> 15) | (($v & 0x7FFF) << 16); // 奇数その1の乗算 $v *= 0x1ca7bc5b; $v &= 0x7FFFFFFF; // 下位31ビットだけ残して正の数であることを保つ return $v; }
ビット逆順の代わりにビット上下逆転を使っています。
奇数を3回かけていますが、スクランブルするだけならこの奇数にはどんな数字を使ってもかまいません。
ご指摘頂いた通り、リクエスト毎にIDを採番しておきたかったのです。
他の方には申し訳ないのですが、このような実装を望んでいました!
回答どうも有り難うございます。
数学的には
【コンピュータの扱う自然数(2のべき乗を位数とする巡回群)のそれぞれの元は奇数定数をかけると必ず相異なる結果になる】
のを利用しているわけですが、これはもう少し一般的な形で
【位数nの巡回群のそれぞれの元は、定数p(p<n, pとnは互いに素)をかけると必ず相異なる結果になる】
と言い換えましょうか。
このことは
【位数nの巡回群の元p(p<n, pとnは互いに素)には逆数が存在する】
という事実から導けると思います。
逆数が存在することが名前の付いた定理になっていたかどうかは忘れましたが、逆数そのものは拡張ユークリッド互除法という計算方法で機械的に求まります。
ご指摘頂いた通り、リクエスト毎にIDを採番しておきたかったのです。
2013/08/27 10:52:24他の方には申し訳ないのですが、このような実装を望んでいました!
回答どうも有り難うございます。
数学的には
2013/08/27 11:48:12【コンピュータの扱う自然数(2のべき乗を位数とする巡回群)のそれぞれの元は奇数定数をかけると必ず相異なる結果になる】
のを利用しているわけですが、これはもう少し一般的な形で
【位数nの巡回群のそれぞれの元は、定数p(p<n, pとnは互いに素)をかけると必ず相異なる結果になる】
と言い換えましょうか。
このことは
【位数nの巡回群の元p(p<n, pとnは互いに素)には逆数が存在する】
という事実から導けると思います。
逆数が存在することが名前の付いた定理になっていたかどうかは忘れましたが、逆数そのものは拡張ユークリッド互除法という計算方法で機械的に求まります。