【问题标题】:Anti-forgery unique serial number generation防伪唯一序列号生成
【发布时间】:2018-06-23 21:17:54
【问题描述】:

我正在尝试生成一个随机序列号贴在全息贴纸上,以便让客户检查所购买的产品是否正品。

前言: 一旦您输入该代码并查询该代码,它将为空,因此下次您再次执行此操作时,您会收到一条消息,指出该产品可能是假的,因为该代码已被使用。

考虑到我应该为年产量不超过 2/3 百万件的工厂制作这个系统,对我来说有点难以理解如何设置所有东西,至少是第一次......

我想到了 4 组 20 位数字的代码(没有字母,因为必须很容易让用户阅读和输入代码)

12345-67890-98765-43210

这是我认为最简单的方法:

function mycheckdigit()
{
...
return $myserial;
}
$mycustomcode="123";
$qty=20000;
$myfile = fopen("./thefile.txt","w")  or die("Houston we got a problem here");
//using a txt file for a test, should be a DB instead...
for($i=0;$i<=$qty;$i++) {
    $txt = date("y").$mycustomcode.str_pad(gettimeofday()['usec'],6,STR_PAD_LEFT).random_int(1000000,9999999). "\n";
    //here the code to make check digits
    mycheckdigit($txt);
    fwrite($myfile,$myserial);
}
fclose($myfile);
  • 第一组标识类似年份的内容:18 和 3 自定义代码
  • 第二组包括microtime (gettimeofday()['usec'])
  • 第三个完全随机
  • 最后一组,包括 3 个随机数和一个用于组 1 的校验位和一个用于组 2 的校验位

简而言之:

Y= year
E= part of the EAN or custom code
M= Microtime generated number (gettimeofday()['usec'])
D= random_int() digits
C= Check Digit

YYEEE-MMMMM-MDDDD-DDDCC

这样,我有一个每年都会变化的前缀,我可以识别产品是什么品牌(所以我可以只使用一个数据库源)并且我仍然有足够的随机数字来 - 也许 - 非常独特,如果我考虑到我将仅从1,000,0009,999,999 中“提取”一部分数字,然后使用上述排序将其拆分

问你一些问题:

  • 考虑到 200 万个代码,您认为我有足够的组合在一年内不会生成相同的代码吗?如果不是真的有必要,我不会在数据库中查找相同的代码,因为这可能会减慢批量生成(在生产过程中批量执行)
  • 是否可以更好地添加一些唯一标识符,例如一年中的某一天 (001-365) 并使 random_int() 缩短 3 位数字?请考虑我将每月而不是每天生成代码(但我认为唯一性没有太大变化)
  • 考虑到 PHP 中的后端,我正在考虑使用 mt_rand() 函数,这可能是一个好方法吗?

更新:在@apokryfos 的建议之后,我阅读了更多关于 UUID 生成和类似的信息,我发现使用 random_int() 是一个很好的折衷方案。 因为我只需要数字,所以 HEX 哈希对我的需求没有用处,让事情变得更复杂

我会避免使用诸如 RSA 密钥之类的复杂加密技术…… 我不需要那种级别的安全性和复杂性,我只需要一种生成唯一序列号的方法,尽可能唯一,如果你不刮标签就不容易被猜到和无效(所以数字创建应该不是从头到尾,而是随机的)

【问题讨论】:

  • 代替YYrrr(随机),您可以使用YYYDDD(月份中的某天,001-365)或YYcwD(CalenderWeek 01-52 和 dayOfWeek (1-7))。您通过固定算法生成的数字越多,重复的概率越低。根据您的(可靠)时间分辨率,您可以下降到分钟、秒甚至毫秒,这可能只剩下几位数字 - 为此,“计数”比随机好。您甚至可以添加一些“伪装”,例如将 543 添加到 DDD、交换位置等。有趣的问题,但遗憾的是,在 SO 上“偏离主题”。
  • @Stephan 感谢您的建议,这是一个简单而有趣的实现,使代码“更干净”。关于“离题”,我认为那是正确的地方,对此感到抱歉。如果您建议我在哪里提出此类问题,我会记住下一个问题。
  • 这样的问题往往会被重定向到SuperUser,但我不确定它是否是主题。 SO 的一个 OnTopic 问题就像“我尝试......,但我无法让以下代码完成我期望的工作”。如果您愿意接受一些反对意见,请保留问题直到它关闭,并希望得到一些有用的反馈。
  • @Stephan 我解决了这个问题,并且在对 SO 进行了一些测试和研究后,我也进行了更新。再次感谢:-)

标签: php mysql batch-file random unique


【解决方案1】:

您每年可以使用 11 个随机数字,因此 11 个数字从 1 到 99999999999(999 亿比 200 万多得多)所以 w.r.t.足够的组合我认为你已经涵盖了。

但是,使用 mt_rand 您可能会遇到冲突。以下是在使用数据库之前规划 200 万个随机数的方法:

<?php
$arr = [];
while (count($arr) < 1000000) {
    $num = mt_rand(1, 99999999999);
    $numStr = str_pad($num,11,0,STR_PAD_LEFT); //Force 11 digits
    if (!isset($arr[$numStr])) {
        $arr[$numStr] = true;
    } 
}
$keys= array_keys($arr);

冲突的数量通常很低(第一次冲突发生在大约 300 000 - 500 000 个生成的数字中,因此非常罕见。

数组$keys 中的每个值都是一个随机且唯一的 11 位数字。

这种方法相对较快,但请注意它需要相当多的内存(超过 128MB)。

话虽如此,更常用的方法是生成一个universally unique identifier (UUID),它更有可能是唯一的,因此不需要检查唯一性。

【讨论】: