不需要有意义的单词 - 更像是随机密码生成,但关键是 - 它们应该是唯一的。我将用它来生成某种包/产品代码。哪种方法是最好的? :)
通常不可能生成既具有唯一性又具有随机性的序列:显然,为了保持唯一性,算法必须考虑先前生成的序列元素,因此下一个元素实际上并不是真正的随机。
因此,您最好检测碰撞并重试(在您的特定情况下可能非常昂贵)。
如果您只限于7个字符,除了以下方法外,没有太多可以做:
$allowed_chars = 'abcdefghijklmnopqrstuvwxz';
$allowed_count = strlen($allowed_chars);
$password = null;
$password_length = 7;
while($password === null || already_exists($password)) {
$password = '';
for($i = 0; $i < $password_length; ++$i) {
$password .= $allowed_chars{mt_rand(0, $allowed_count - 1)};
}
}
这最终会为您提供一个新的密码。
然而,在我遇到类似情况时,我通常会选择更大的密码长度,这也恰好是流行哈希函数(例如md5
)的十六进制表示大小。这样您就可以更轻松地操作,减少错误:
$password = time(); // even better if you have some other "random" input to use here
do {
$password = md5(time().$password);
}
while (already_exists($password));
这还有一个额外的好处,即序列空间更大,因此碰撞会更少。您可以根据未来生成的密码数量选择哈希函数的大小,以“保证”低碰撞概率,从而减少对可能昂贵的already_exists
函数的调用。
这是一种不需要哈希或循环的方法:
$password = sprintf(
"%04s%03s",
base_convert(mt_rand(0, pow(36, 4) - 1), 10, 36),
base_convert(mt_rand(0, pow(36, 3) - 1), 10, 36)
);
正如其他一些人所提到的,确保唯一性更加复杂,而且应该是不必要的。你可以采用最简单的方法,在每个生成的密码末尾添加额外的字符,并逐个递增。
这里有一些看起来随机的东西,应该是独一无二的,并且在未来的时间里有7个字符:
echo base_convert(intval(microtime(true) * 10000), 10, 36);
或者为了更多的随机性和较少的独特性(每秒之间在1000和10000之间):
echo base_convert(mt_rand(1, 9) . intval(microtime(true) * 1000), 10, 36);
或者(每秒钟在100和10000之间的唯一性)- 这可能是最好的选择:
echo base_convert(mt_rand(10, 99) . intval(microtime(true) * 100), 10, 36);
或者(每秒钟在10和10000之间的唯一性):
echo base_convert(mt_rand(100, 999) . intval(microtime(true) * 10), 10, 36);
你懂的。
intval(microtime(true) * 10000) == -65867797
- 这将给你一个6个字符的输出。 - nickf一个随机的字母数字(基数36 = 0..9 + a..z
)值,它有7个字符必须具有10进制表示在2176782336
和78364164095
之间,以下代码片段证明了这一点:
var_dump(base_convert('1000000', 36, 10)); // 2176782336
var_dump(base_convert('zzzzzzz', 36, 10)); // 78364164095
time()
:var_dump(time()); // 1273508728
var_dump(microtime(true)); // 1273508728.2883
var_dump(base_convert(time() * 2, 10, 36)); // 164ff8w
var_dump(base_convert(time() * 2 + 1, 10, 36)); // 164ff8x
var_dump(base_convert(time() * 2 + 2, 10, 36)); // 164ff8y
var_dump(base_convert(time() * 2 + 3, 10, 36)); // 164ff8z
你会注意到这些代码不是随机的,你也会注意到time()
(1273508728
)小于2176782336
(7个字符代码的最小10进制表示),这就是为什么我要做time() * 2
。
现在让我们进行一些日期数学运算,以增加随机性和增加唯一性因素,同时遵守旧版本PHP(<5.0
?)的整数限制:
var_dump(1 * 60 * 60); // 3600
var_dump(1 * 60 * 60 * 24); // 86400
var_dump(1 * 60 * 60 * 24 * 366); // 31622400
var_dump(1 * 60 * 60 * 24 * 366 * 10); // 316224000
var_dump(1 * 60 * 60 * 24 * 366 * 20); // 632448000
var_dump(1 * 60 * 60 * 24 * 366 * 30); // 948672000
var_dump(1 * 60 * 60 * 24 * 366 * 31); // 980294400
var_dump(PHP_INT_MAX); // 2147483647
PHP_INT_MAX
我不确定最近的 PHP 版本发生了什么变化,因为以下在 PHP 5.3.1 中明显可行,也许有人能够解释一下:var_dump(base_convert(PHP_INT_MAX, 10, 36)); // zik0zj
var_dump(base_convert(PHP_INT_MAX + 1, 10, 36)); // zik0zk
var_dump(base_convert(PHP_INT_MAX + 2, 10, 36)); // zik0zl
var_dump(base_convert(PHP_INT_MAX * 2, 10, 36)); // 1z141z2
var_dump(base_convert(PHP_INT_MAX * 2 + 1, 10, 36)); // 1z141z3
var_dump(base_convert(PHP_INT_MAX * 2 + 2, 10, 36)); // 1z141z4
我在这里有点迷失了,而且我很无聊,所以我会很快完成。我们可以使用几乎整个36进制字符集,并安全地生成连续代码,每秒最少保证1个唯一代码3.16887646年,使用以下方法:
base_convert(mt_rand(22, 782) . substr(time(), 2), 10, 36);
我刚刚意识到,由于mt_rand()
的第一个参数,上述代码有时会返回重复的值,为了产生唯一的结果,我们需要稍微限制一下我们的基本36个字符集:
base_convert(mt_rand(122, 782) . substr(time(), 2), 10, 36);
请记住,上述值仍然是顺序的,为了使它们看起来随机,我们可以使用microtime()
,但我们只能确保每秒10个代码在3.8个月内的唯一性因素:
base_convert(mt_rand(122, 782) . substr(number_format(microtime(true), 1, '', ''), 3), 10, 36);
这比我最初预期的要困难得多,因为有很多限制:
如果我们可以忽略上述任何一个限制,那么这将变得更加容易,我相信这可以进一步优化,但就像我说的那样:这让我感到无聊。也许有人想接手这个项目。=)我饿了!=S
1000000
开始,而不是 0000000
? - sawavar_dump(base_convert('1000000', 36, 10));
证明了它,而不是0000000。我想问为什么?这个问题要求一个字符串。为什么一个字符串不能以'0'开头? - sawa这是我最喜欢的做法。
$pretrimmedrandom = md5(uniqid(mt_rand(),true));
$trimmed = substr($pretrimmedrandom ,0,7);
uniqid 使用当前时间生成一个非常独特的随机字符串。结果看起来像“3f456yg”。
$random = substr(hash('md5',openssl_random_pseudo_bytes(32)),0,7);
Galen的答案只允许在密码中使用每个字符一次。字符串中没有太多信息。不过可以进行简单更改:
$chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
$passwordlength = 7;
for ($x = 1; $x <= $passwordlength; $x++) {
$charlist .= $chars;
}
$temp_pw = substr( str_shuffle( $charlist ), 0, $passwordlength );
substr(str_shuffle(md5(microtime())),rand(0,21),7);
如果安全不是问题,则仅第 1 和第 2 步就足够了。如果安全是一个问题,那么除了你自己之外,没有人应该知道“MasterPassword”的值。
这是我解决这个问题的方法:
考虑到7个字符可以是26个字母(abc..z)或10个数字(01...9)之一,这使得有36种可能的字符。
每次您的应用程序生成新代码时,请将其全局变量递增。您可以使用“Hexatridecimal”转换器将此唯一编号转换为唯一字符串,并添加填充字符以组成其余的字符串。
看看这个链接。我认为这个人和你有同样的问题: http://www.codemaxima.com/2010/04/the-hexatridecimal-numbering-system/
$allowed_chars
字符串,但是删除了所有元音字母以及可能被误解为其他字符的字符:1,l,0
。 - nickf