V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Specs
V2EX  ›  PHP

有哪些能够生成随机不重复字符串的算法或者函数?

  •  
  •   Specs · 2016-05-18 16:22:50 +08:00 · 10422 次点击
    这是一个创建于 2905 天前的主题,其中的信息可能已经有所发展或是发生改变。

    有哪些能够生成随机不重复字符串的算法或者函数?最好是 PHP 中的~~

    第 1 条附言  ·  2016-05-19 09:49:52 +08:00
    长度最好是在十来位以内啊~~
    第 2 条附言  ·  2016-05-20 09:51:50 +08:00
    附加个具体点的条件,应该尽量在 10 来位以内吧,然后最重要的是不重复,随机的只要看起来找不到规律就行。做那种类似优惠券的编码的~
    73 条回复    2017-02-10 13:54:12 +08:00
    ayuanshuai929
        1
    ayuanshuai929  
       2016-05-18 16:36:29 +08:00 via iPhone
    md5(time())算不算,但是不能保证永不重复,只能说重复的概率非常小
    skywalkboy
        2
    skywalkboy  
       2016-05-18 16:45:19 +08:00
    之前看 tomcat 生产 sessionId 的时候用了函数查看是否重复,没有重复才返回
    abelyao
        3
    abelyao  
       2016-05-18 16:49:02 +08:00
    @ayuanshuai929 分布式时候重复的概率还蛮大的
    abelyao
        4
    abelyao  
       2016-05-18 16:49:45 +08:00
    不说长度要求的 都是耍流氓
    walkman660
        5
    walkman660  
       2016-05-18 16:51:28 +08:00
    #!/bin/bash
    echo $RANDOM
    lslqtz
        6
    lslqtz  
       2016-05-18 16:55:50 +08:00
    @ayuanshuai929 md5(time().随机字符.随机数字) 有多长来多长。
    dawniii
        7
    dawniii  
       2016-05-18 16:59:33 +08:00
    @lslqtz md5 好像是多对一的。。 还是会重复
    Sunyanzi
        8
    Sunyanzi  
       2016-05-18 17:01:25 +08:00   ❤️ 10
    严格意义上讲生成一个「随机」且「不重复」的字符串是不可能的 ... 因为随机和唯一是互斥的两个条件 ...

    生成不重复的字符串必须遵循一定规则 ... 这就不随机 ... 反之则一定存在碰撞概率 ... 只是这个概率或大或小而已 ...

    所以对于标题的问题 ... 答案是一个都没有 ...

    但如果你要的是一个「看似随机」且「没有那么容易重复」的方法的话 ... 那倒是有不少 ...

    比如我个人常用的 uniqid( mt_rand( 0, 9999 ), true ) ... 碰撞条件是同一微秒内的万分之一概率 ...

    或者唯一性要求不那么高的时候常用的在毫秒时间戳外面套一层摘要函数的办法 ... 如 md5( microtime() ) ...

    这种方式生成速度比 uniqid 要快 ... 但碰撞条件不明且碰撞概率一定高于 uniqid ...

    顺带一提楼上说的在时间戳后面增加随机字符串的方式除了拖慢执行之外毫无意义 ... 并不会降低碰撞概率 ...
    yeyuexia
        9
    yeyuexia  
       2016-05-18 17:09:49 +08:00   ❤️ 1
    如果是 linux 或者 mac 上 试试在命令行直接敲 uuidgen 吧……
    debiann
        10
    debiann  
       2016-05-18 17:11:03 +08:00 via iPhone
    利用 ascii 码自己写一个很简单的吧,重复概率取决于长度
    yeyuexia
        11
    yeyuexia  
       2016-05-18 17:12:38 +08:00   ❤️ 1
    补上一条 关于 uuid 出现冲突的可能性见 https://en.wikipedia.org/wiki/Universally_unique_identifier
    话说话题是不是可以终结了?
    hgc81538
        12
    hgc81538  
       2016-05-18 17:19:46 +08:00
    <?php

    $length = 32;
    $crypto_strong = true;
    $bytes = openssl_random_pseudo_bytes($length, $crypto_strong);
    $hex = bin2hex($bytes);
    print_r($hex);
    Specs
        13
    Specs  
    OP
       2016-05-18 17:34:30 +08:00
    @skywalkboy 这种不适合存在数据库中啊~每次检查一次,如果数据量比较大的话那。。。。
    Orzzzz
        14
    Orzzzz  
       2016-05-18 17:47:43 +08:00
    @Specs php 调用系统命令 uuidgen 啊,之后随便怎么处理那些字符串

    <?php
    $code = exec('uuidgen');

    echo $code;
    SlipStupig
        15
    SlipStupig  
       2016-05-18 18:39:02 +08:00
    用 100 次鼠标移动位置的坐标生成一个 RSA 1024 的字符串,基本上没什么重复的可能性!
    skywalkboy
        16
    skywalkboy  
       2016-05-18 18:40:10 +08:00
    @Specs 用过的可以放内存中(redis),看你怎么取舍了,一般重复的概率是很小的,但是如果你一定要保证不重复,那就要比较了
    vibbow
        17
    vibbow  
       2016-05-18 18:40:44 +08:00
    为什么...不用...UUID 呢...
    dqh3000
        18
    dqh3000  
       2016-05-18 18:44:41 +08:00
    你想要的是 uuid 算法,去看看 uuid v3 , uuid v5 之类的 RFC 吧
    est
        19
    est  
       2016-05-18 18:44:52 +08:00
    LZ 要的是随机字符和历史产生的不重复,还是字符串里的字符不重复?

    如果是后者,只算字母数字,最大长度 26+10 位。 LZ 我没说错吧。 py 里可以这样

    >>> ''.join(random.choice('abcdefghijklmnopqrstuvwxyz0123456789') for _ in xrange(10))
    'kaz2spd3hy'
    kindjeff
        20
    kindjeff  
       2016-05-18 18:53:31 +08:00
    @est 没看懂代码……请教一下这句代码是什么意思?下划线代表什么,为什么 for 语句可以放在同行,为什么 random.choice('abcdefghijklmnopqrstuvwxyz0123456789') for _ in xrange(10)会报错……
    Specs
        21
    Specs  
    OP
       2016-05-18 18:57:46 +08:00
    @Orzzzz 好像有的服务器是禁用这些函数的吧~
    Specs
        22
    Specs  
    OP
       2016-05-18 18:58:27 +08:00
    @dqh3000 OK~
    Specs
        23
    Specs  
    OP
       2016-05-18 18:58:45 +08:00
    @hgc81538 好的我试试
    misaka19000
        24
    misaka19000  
       2016-05-18 18:59:27 +08:00 via Android
    UUID
    妥妥的
    Specs
        25
    Specs  
    OP
       2016-05-18 18:59:38 +08:00
    @est 和历史的~~~
    lslqtz
        26
    lslqtz  
       2016-05-18 19:35:47 +08:00 via iPhone
    @lslqtz md5 不是唯一的么,随机字符重复+时间戳重复+随机数字重复基本没可能吧
    dphdjy
        27
    dphdjy  
       2016-05-18 20:13:33 +08:00 via Android
    分布式大概用机器编码+特征码+时间

    其实用 UUID 不就解决了~
    Sunyanzi
        28
    Sunyanzi  
       2016-05-18 20:15:01 +08:00   ❤️ 6
    @lslqtz 基本没可能和绝对没可能是天壤之别 ... 至于 md5 唯一什么的 ... 你运行一下下面代码开开眼 ..?

    <?php
    foreach( [ [ '0', '5' ], [ '4', 'd' ] ] as $v )
    echo md5( pack( 'H*',
    '0e306561559aa787d00bc6f70bbdfe3404cf03659e7' . $v[0] .
    '4f8534c00ffb659c4c8740cc942feb2da115a3f415' . $v[1] .
    'cbb8607497386656d7d1f34a42059d78f5a8dd1ef'
    ) ), PHP_EOL;
    zynlnow
        29
    zynlnow  
       2016-05-18 21:00:29 +08:00
    @kindjeff 那是 python 语法
    realpg
        30
    realpg  
       2016-05-18 21:32:59 +08:00
    不说长度?
    $hash="";
    for ($i=1;$i<100;$i++) $hash .= md5(rand(1,999999999));
    zhujinliang
        31
    zhujinliang  
       2016-05-18 21:45:19 +08:00 via iPhone
    @realpg 连续做伪随机并不会提高随机性
    realpg
        32
    realpg  
       2016-05-18 21:47:04 +08:00
    @zhujinliang
    本题要的并不是随机性,有一定的随机度即可,关键是不重复。
    menc
        33
    menc  
       2016-05-18 21:47:05 +08:00
    @abelyao md5(time() + ip + mac)
    loading
        34
    loading  
       2016-05-18 21:56:33 +08:00 via Android
    拼接,简单的随机+时间戳

    第一时间我想到的是 uuid ,上面有人提到,我就不细说了。
    barbery
        35
    barbery  
       2016-05-18 23:35:44 +08:00
    echo bin2hex(random_bytes($length/2));
    lianyue
        36
    lianyue  
       2016-05-18 23:49:52 +08:00
    @ayuanshuai929 md5(time() 高级黑

    正确的姿势 uniqid(mt_rand(), true)
    techme
        37
    techme  
       2016-05-19 08:55:32 +08:00
    Guid().newID()
    notgod
        38
    notgod  
       2016-05-19 09:00:59 +08:00
    查看“ php 短网址生成程序”的算法就好了
    Neveroldmilk
        39
    Neveroldmilk  
       2016-05-19 09:26:05 +08:00
    大部分语言内置的随机生成器,只能保证在一定范围内哈希值不重复,没有绝对随机的生成器。这一点可以参考 Donald Knuth 的 the art of computer programming 第二卷里的内容。
    Specs
        40
    Specs  
    OP
       2016-05-19 09:51:11 +08:00
    @notgod 👌,我查查看看
    lygmqkl
        41
    lygmqkl  
       2016-05-19 11:16:51 +08:00
    其实我倒有个新思路
    大家在上面谈到的都是怎么生成一定概率不重复的随机字符串,这些概率一般都是在单 server 和少量请求的情况下,一旦放到到复杂的架构中,比如 20 台 servers + 10k/s 请求多半都是歇菜

    我想说的是利用一些简单的思路实现 100%不重复的随机字符串,具体如下:

    大家只是把随机字符串妖魔化了,其实 1 , 2 , 3 , 4 , 5 不就是不重复的随机字符串吗?好吧如果你要随机字符串 md5(1) 就可以实现,或者利用拼接的方法因为你有 unique 的部分即 1, 2, 3, 4, 5

    那么实现方法也很简单

    $topic = new Topic();
    //TODO add necessary attributes

    if ($topic->save()) {
    $randString = md5($topic->t_id);

    echo $randString;//c20ad4d76fe97759aa27a0c99bff6710
    }else{
    //save fail, do sth
    }

    demo code 是用 Yii 写的应该能看得懂

    牺牲的是一次数据库写入操作,得到的是一个 unique 的 seed, 这和老实的 order 系统很接近,我唯一做的事情就是把它放到了 RESTful 架构中,真的很好用。

    另外附上我前一阵写的一篇文章,希望对楼主有帮助
    http://yiilib.com/topic/671/php%E7%94%9F%E6%88%90100%25%E4%B8%8D%E9%87%8D%E5%A4%8D%E9%9A%8F%E6%9C%BA%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%AE%9E%E7%8E%B0%E6%96%B9%E6%B3%95
    imydou
        42
    imydou  
       2016-05-19 11:23:42 +08:00
    @lslqtz md5 长度一定了,重复几率差不多吧?
    niuoh
        43
    niuoh  
       2016-05-19 11:29:30 +08:00
    function r($len=5){
    $array=range(chr(33),chr(125));
    shuffle($array);
    for($i=0;$i<$len;$i++)$result.=$array[$i];
    return $result;
    }
    echo r(5);
    xmh51
        44
    xmh51  
       2016-05-19 11:32:29 +08:00
    @imydou md5 码被证实,会有一定机率重复。
    lslqtz
        45
    lslqtz  
       2016-05-19 12:25:57 +08:00 via iPhone
    @xmh51 重复几率小应该就没事吧。
    lhbc
        46
    lhbc  
       2016-05-19 12:43:33 +08:00 via Android
    单机的话,自增 uid+随机字符串
    绝对不重复,有一定随机性
    fyooo
        47
    fyooo  
       2016-05-19 14:18:00 +08:00
    OCaml:

    let random_str length =
    let gen() = match Random.int(26+26+10) with
    n when n < 26 -> int_of_char 'a' + n
    | n when n < 26 + 26 -> int_of_char 'A' + n - 26
    | n -> int_of_char '0' + n - 26 - 26 in
    let gen _ = String.make 1 (char_of_int(gen())) in
    String.concat ~sep:"" (Array.to_list (Array.init length ~f:gen))
    fyooo
        48
    fyooo  
       2016-05-19 14:18:52 +08:00
    忽略我的回复,刚刚才看到是 PHP 节点的
    9hills
        49
    9hills  
       2016-05-19 14:43:37 +08:00
    楼上为啥有这么多重复造轮子的。。而且造的还很烂,根本不符合要求
    现成的 UUID ,各个语言都有对应的库,嫌太长也有 short uuid 。
    glchaos
        50
    glchaos  
       2016-05-19 16:55:44 +08:00
    随机的重要程度和唯一的重要程度哪个更高些?时效有没有要求?
    ty0716
        51
    ty0716  
       2016-05-19 17:12:58 +08:00 via iPhone
    加上用户 id 就 ok 了
    Specs
        52
    Specs  
    OP
       2016-05-19 17:49:07 +08:00
    @glchaos 唯一是最重要的,但是最好别有规律,例如优惠券编码这样的
    innoink
        53
    innoink  
       2016-05-19 18:05:24 +08:00 via Android
    首先如果是无限数量肯定不能同时保证唯一和随机
    数量不多的话我倾向于预先生成
    20131115
        54
    20131115  
       2016-05-19 18:08:42 +08:00
    说 md5 的是没考虑性能。。。生成 10 亿给大家看看
    xujif
        55
    xujif  
       2016-05-19 18:16:45 +08:00
    php 肯定是 uniqid 啊,楼上都在想什么 http://php.net/manual/zh/function.uniqid.php
    cszchen
        56
    cszchen  
       2016-05-19 19:04:49 +08:00 via iPhone
    uniqid 也有概率重复,可以一个进程专门生成随机串,插入到队列,需要的时候从队列取
    paulw54jrn
        57
    paulw54jrn  
       2016-05-19 19:07:44 +08:00
    uuid4
    akagi
        58
    akagi  
       2016-05-19 19:20:13 +08:00
    @ayuanshuai929 高速调用会重复吧
    pagxir
        59
    pagxir  
       2016-05-19 19:58:30 +08:00 via Android
    Des(0) des(1) des(2) ... 保证不重复,并且看似随机。
    AbrahamGreyson
        60
    AbrahamGreyson  
       2016-05-19 20:14:41 +08:00   ❤️ 1
    twitter 有个库,不过根据其算法,只能保证 x 年内不重复,自己搜吧。
    a591826944
        61
    a591826944  
       2016-05-19 20:20:07 +08:00
    @lslqtz 分布式同一时间 同一函数,生成的随机数字 /字符 是相同的 所以 后面 拼多少都没有用
    zhicheng
        62
    zhicheng  
       2016-05-19 20:30:53 +08:00 via Android
    说 MD5 慢的,你咋不上天, 10 亿也是秒级的。
    icylord
        63
    icylord  
       2016-05-19 21:01:02 +08:00
    UUID
    jfcherng
        64
    jfcherng  
       2016-05-19 22:33:58 +08:00
    有限長度與不重複不是明顯互斥嗎
    miaotaizi
        65
    miaotaizi  
       2016-05-20 09:38:34 +08:00
    @Sunyanzi 开眼了, 看了你好多的评论, 你收徒弟么, 请收下我的膝盖, 我不想做那个四年都在做同一件事情的码农.
    Specs
        66
    Specs  
    OP
       2016-05-20 09:49:36 +08:00
    @AbrahamGreyson OK

    @Sunyanzi 还可以这样~

    @jfcherng 在不重复的情况下尽量看起来随机吧~
    juice
        67
    juice  
       2016-05-20 11:31:05 +08:00
    可以参考 twitter 的 Snowflake 算法
    necomancer
        68
    necomancer  
       2016-05-20 12:22:10 +08:00
    UUID 吧, 重复概率极小
    Sunyanzi
        69
    Sunyanzi  
       2016-05-20 23:39:59 +08:00   ❤️ 1
    @miaotaizi 收呀 ... 我一直有琢磨着拉个群讲讲课什么的 ... 但一直没时间来做 ... 过阵子看看吧 ..?

    @juice @AbrahamGreyson Snowflake 算法从本身的设计上并没有避免多线程并发时重复生成的问题 ...

    @Specs 如果最终你会把内容存在数据库的话 ... 那么给相关字段上唯一索引然后生成随机字符串往里插就是 ...

    只要字符串长度足够就可以完全不用管是否会重复 ... 如果插不进去就重新随机一次再插 ... 直到成功为止 ...
    miaotaizi
        70
    miaotaizi  
       2016-05-23 11:03:40 +08:00
    @Sunyanzi 真的可以吗? 期待你开课. (别逗我, 我是认真的)
    mingyun
        71
    mingyun  
       2016-07-24 17:03:08 +08:00
    @barbery 这个得 php7
    Sunyanzi
        72
    Sunyanzi  
       2017-02-09 21:15:57 +08:00
    @miaotaizi 那个啥 ... 我决定尝试性的瞎讲一下啦 ... 具体参考 /t/339287 ...
    miaotaizi
        73
    miaotaizi  
       2017-02-10 13:54:12 +08:00 via iPhone
    @Sunyanzi 真感激,这么久了还记得我。一定来!
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1070 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 23:50 · PVG 07:50 · LAX 16:50 · JFK 19:50
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.