人力検索はてな
モバイル版を表示しています。PC版はこちら
i-mobile

重複のない10桁の数字をIDとして採番するアルゴリズムを教えて下さい。

但し、下記の条件があります。

- 最低でも1億以上、採番可能なもの
- 時系列や連番など推測されやすいものはNG
- 基本的にデータベースを使用せずアルゴリズム内だけて採番(但しカウントアップ用で使うならOK)
- 数字が一意であると保証されていること

以上になります。

PHPのコードで書かれてると、なお有り難いです、
宜しくお願いします。

●質問者: wankodon
●カテゴリ:コンピュータ ウェブ制作
○ 状態 :終了
└ 回答数 : 4/4件

▽最新の回答へ

1 ● きゃづみぃ
●75ポイント

まず1億分の配列に連番で取得させます。

その後、配列をランダムにします。

あとは採番のときに 順に配列から値を取得すればいいです。


きゃづみぃさんのコメント
上記の配列にセットするのは プログラムに固定で持たせれば よいでしょう。

きゃづみぃさんのコメント
連番にしないで たとえば 123ごとに 採番するようにするとかしたらいかがでしょうか? また それをプラスではなく、マイナスさせていくとかすれば 推測しにくいと思われます。

wankodonさんのコメント
お手軽に実装するにアリかも知れませんが、1億以上も配列を作成するのはちょっと、、 ともあれ回答有り難うございます。

2 ● holoholobird
●75ポイント

idの重複を確実になくすならこれしか方法はないと思います。

<?php
$p=100;
$temp=range(1,$p);
shuffle($temp);
print_r($temp);
?>

たとえば上の例だと$p=100なので、$tempは1?100までの数字を重複なしにランダムに格納した、100の要素の配列になります。

$pの数字は適当にかえてください。

http://codepad.org/KfMrQ2u2
↑こういう結果になります。


wankodonさんのコメント
回答有り難うございます。参考にさせて頂きます。

3 ● だわかき
●75ポイント

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;
}

holoholobirdさんのコメント
shuffle関数を使用しているのになぜ連番が生成されるのか教えてください。 http://megalodon.jp/2013-0826-2035-26/q.hatena.ne.jp/1377468971 にあなたの回答のキャッシュは保存済みです。

tezcelloさんのコメント
質問が「重複のない10桁の数字をIDとして」なので、根本的に外していますよね?

だわかきさんのコメント
重複はしてませんよ。コードをよく読んでください。

だわかきさんのコメント
No.2の回答は、rangeを使っている以上、連番であることが明らかです。shuffleで順番を入れ替えたところで、連番であることには変わりがありません。 10桁(=10億通り)に対して1億以上のIDを発番したいという質問ですから、ランダムに飛び飛びの数字を出す必要があります。 かりに10億個の配列をshuffleするとしたら、どのくらい時間がかかりますか?そもそもメモリが足りてますか?

tezcelloさんのコメント
コードをよく読むと、より怪しくなりませんか? まずuniqid() で1億回ユニークな値が得られたとします。(ここも微妙) md5() でハッシュを取る際に、ユニークな値のハッシュだからと言って衝突が無いという保証はどこに? 更に、hexdec() で10進化しても、その一部しか取り出さないのは衝突の可能性が飛躍的に高まるのでは? 因みに、当方のローカル環境(XAMPP 1.7.7 @Windows7 pro 64bit sp1)では、貴兄のスクリプトはメモリ不足(128MB設定時)、実行時間超過(200秒設定時)で実行出来ませんでした。

だわかきさんのコメント
メモリ上限512MBでエラーは出ませんでした。そもそも、4億個の整数(4バイト長)は380Mバイトですから、メモリ上限128Mというのはナンセンスです。 実行時間がかかるのは、ご指摘の通りです。PHP.exe(5.2) + Windows 7 + Core i7で1時間55分かかりました。

だわかきさんのコメント
一気に1億個のユニークIDを発行するのではなく、その都度発行ということでしたら、以前のIDと重ならないように判断するために、以前のIDをすべてどこかに保存しておく必要があります。 ユーザーIDとして使っているなら、ユーザー管理DBがあるはずですから、それを使うのが妥当だと思います。

wankodonさんのコメント
申し訳ありません。ご指摘通り都度発行でIDを採番したかったのです。 このようなやり方もあるんですね。回答有り難うございました。

4 ● Sampo
●150ポイント ベストアンサー

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回かけていますが、スクランブルするだけならこの奇数にはどんな数字を使ってもかまいません。


だわかきさんのコメント
$v に入る値は32ビット長整数ですから10桁の整数ではありません。なので、下位10桁を切り出すとかして10桁化するのでしょうが、そうなると値が衝突する可能性大です。 また、整数を32ビット固定と判断していますが、Cならともかく、PHPではそうとは限りません。Cの整数演算をそのままコピペしただけではPHPでは動きません。 > PHPを使っている以上Webアプリ そんなことはないでしょう。

Sampoさんのコメント
この関数はカウントアップが約21億に達するとループしますが、この仕様は1億以上採番可能という要件は満たしています。だわかきさんは過去いろいろな方の指摘を受けておられますが、質問文を理解した上での回答を心がけるのがよろしいかと私からもアドバイスさせていただきます。 CからPHPへの移植性をご懸念のようですが、PHPの整数は最低32ビットが保証されていますので、むしろビット数が完全処理系依存のCよりは書きやすくはあります。 ご参考まで。

Lhankor_Mhyさんのコメント
おお、これ面白いですね。 これって、数学的には解析可能なのかという興味が湧きました。 >だわかきさん >32ビット長整数ですから10桁の整数ではありません http://www.wolframalpha.com/input/?i=log10%282%EF%BC%BE32%29 10桁だと思うんですが、いかがでしょうか。 横からコメントすみませんでした。

wankodonさんのコメント
ご指摘頂いた通り、リクエスト毎にIDを採番しておきたかったのです。 他の方には申し訳ないのですが、このような実装を望んでいました! 回答どうも有り難うございます。

Sampoさんのコメント
数学的には 【コンピュータの扱う自然数(2のべき乗を位数とする巡回群)のそれぞれの元は奇数定数をかけると必ず相異なる結果になる】 のを利用しているわけですが、これはもう少し一般的な形で 【位数nの巡回群のそれぞれの元は、定数p(p<n, pとnは互いに素)をかけると必ず相異なる結果になる】 と言い換えましょうか。 このことは 【位数nの巡回群の元p(p<n, pとnは互いに素)には逆数が存在する】 という事実から導けると思います。 逆数が存在することが名前の付いた定理になっていたかどうかは忘れましたが、逆数そのものは拡張ユークリッド互除法という計算方法で機械的に求まります。
関連質問

●質問をもっと探す●



0.人力検索はてなトップ
8.このページを友達に紹介
9.このページの先頭へ
対応機種一覧
お問い合わせ
ヘルプ/お知らせ
ログイン
無料ユーザー登録
はてなトップ