【问题标题】:Why does the pattern occurs, while using a set to create a random string?为什么在使用集合创建随机字符串时会出现模式?
【发布时间】:2018-04-10 14:55:08
【问题描述】:

我写了一个小函数,它会创建一个一定长度的随机字符串:

def append_until_length(acceptable, length=45):
    retval = set()
    for _ in range(1000):
        retval.add(random.choice(acceptable))
        if len(retval) == length:
            return ''.join(retval)

这一切正常,所以一切都很好。但是在运行它时,如果你愿意的话,我注意到了一种模式:

>>> for _ in range(10):
...     append_until_length(acceptable)
... 
'!#"%\'(+*-,/.057698=?ADGIHLRUV[]\\`behjmonpryx~'
'"$\')+*,025498:=?ACBGKONQPSY[]\\acdgfhkmruvy{z|'
'#"\'&)+,/03248=<?>ABFHJLOPWYXZ]cbdfhklonqrutz}'
' #"(*-/0328EIJMPSRUWVYX]_^acbegfkmlqpstwvx{}|'
'!#"(,/.032549;=>EDHMLOSYX[]_^acbedjlonprtvxz~'
" %',10346?@CEDFIKNQRVYXZ]\\_abghkjlnqpruw{z}|~"
'! #+,/035469:<@CFIKLSRUVY[Z^cbfijloqsutwvxz}|'
'$&)(+-/5;:?>ABDFIHMLOPSUTYXZa`bdhkjmonprwvx}~'
'!#"&*-/102579:=>@DFKJMLONQSTVYX\\^acimoqpstw}~'
'! &(+-/.2548:=<?A@EGFIKOQPSRTVX\\eihjonprutx}~'
>>> 

如果你看这个,前几个字符是 always 标点符号,接下来的几个是 always 数字,然后是带有一些混合标点符号的大写字母,另一个标点符号,小写字母,最后一个字符是 always 标点符号。

我使用的可接受的字符是list(string.printable)[:-6] 和.append(" ")。这个list的长度是95:

>>> acceptable
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '!', '"', '#', '$', '%', '&', "'", '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~', ' ']
>>> len(acceptable)
95
>>> 

现在我知道set() 不允许字符串中有多个相同的字符,但是,这并不能解释模式总是相同的(不是真的相同,但远程相同)。看看我是否通过list 执行此操作,该函数从来没有模式:

>>> def append_until_length(acceptable, length=45):
...     retval = []
...     for _ in range(length):
...         retval.append(random.choice(acceptable))
...     return ''.join(retval)
... 
>>> for _ in range(10):
...     append_until_length(acceptable)
... 
"] *rZI/<=LwPGU-PzWj)\\jp9tZ}e9T#}4/\\R`4Q^?4)'W"
'%z6wTvuzK;{eS}"^GRf(}a3<"Qqg_*2v?1`y@;=Bn#ycQ'
"t'bqj,*}7:w]:8c;Ddy. 17@^Y0{)>}'25tsl1kf+C%6^"
'RZt)s=?~QrAok+Z\\ei}5K^&1e+w0~*zl{hS2;l]|?p/T;'
'%InO5_fWcJU#v,6_=cPb^cfd1=\\;k{37~$214vd+F&oH&'
'!6Ey#"\'3.,ivG+7\'y[&1`aYNDg-\\j#:! -7(8b#$x)Q1m'
'w}/{mnT\\-IT2?;V_K ZDDy:YzaG+LgGkZWkV8E y@_)Y;'
'e1@71AFDF;|Q.<_fRG0tG*`557z(|}bHDCT+dc}{[QGq8'
"ie~;Iy1O)f!n,Z%%0\\36-!Lke1}cA'uptRS7(2ki|mzgi"
'G=v&#.J1@E$N?NK|~>( E4M/^y[~HK)#Hi$23ez~EY>N '

即使我将list 视为set,输出字符串仍然没有模式:

def append_until_length(acceptable, length=45):
    retval = []
    for _ in range(10000):
        char = random.choice(acceptable)
        if char not in retval:
            retval.append(char)
        if len(retval) == length:
            return ''.join(retval)

8hKO W5"'ERJa/N$vb9^4!)fig:c_n&?@(#}oTC]qePwZ
,b2;Y^VD9|:O!>QilH`4(7/F?8f&5~_B$x#pN{Igahs\n
_z1eDiH$9k&rRt>M/FOqb8SLY.{|0dI4A^:l,3cs7ng][
Y/iu#eOlVMmZ 9S`t?1JX2$<)&|jUz'"~wLIvoqkr}!(H
r~/m{8SLvU?_aVX4A"0%zEgK1I!9#B|snphOZb,@jw\]2
;nX!T20.^b"\eqNExOlrQF'V&#(%iht{Hw+-Sy,Dj]:9[
B@%H[2f&JuwSd1bEnih#}]3jTMLzAW.ZG~,tX|!/N_`D(
usv}KkZgL]&<hY^6Blp\GENTrFC~Xw3#4S8QmRf"PUnM|
?G3Ao[z7gVLve-}S>X]&<+k(DZ*UcsM50r)^1Om`P4K,6
,#&(1-'sj9qy7~dZpuIk!%Q D8haSNrco{xe;=.T[WK0<

所以我的问题是,为什么set 会出现这种模式?大写和小写字符具有不同的ord 数字,因此是不同的字符。即:

>>> ord("c")
99
>>> ord("C")
67
>>> 

所以在我看来,如果字符串是随机生成的,为什么字符串中有模式是没有意义的?根据help(set):

class set(object)
 |  set() -> new empty set object
 |  set(iterable) -> new set object
 |  
 |  Build an unordered collection of unique elements.

【问题讨论】:

    标签: python string random


    【解决方案1】:

    您的问题是sets 并不是真正的无序。它们遵循您无法依赖或预测的实现定义的顺序(这在 python 解释器的 2 次执行之间是不同的),但它就在那里 ('order' of unordered Python sets)。

    在那种情况下,它似乎是自然的字母顺序但不一定:

    Python 3.4.3 (v3.4.3:9b73f1c3e601, Feb 24 2015, 22:44:40) [MSC v.1600 64 bit (AMD64)] on win32
    Type "help", "copyright", "credits" or "license" for more information.
    >>> set("ABCDEFG")
    {'A', 'E', 'F', 'G', 'C', 'B', 'D'}
    >>> set("ABCDEFG")
    {'A', 'E', 'F', 'G', 'C', 'B', 'D'}
    

    2 个不同的集合的顺序相同,不按字母顺序排列。现在让我们再次运行它:

    Python 3.4.3 (v3.4.3:9b73f1c3e601, Feb 24 2015, 22:44:40) [MSC v.1600 64 bit (AM    D64)] on win32
    Type "help", "copyright", "credits" or "license" for more information.
    >>> set("ABCDEFG")
    {'C', 'G', 'D', 'B', 'E', 'A', 'F'}
    >>>
    

    不同的顺序(与哈希种子和python安全特性有关)

    因此,在解释器的同一个实例中,它对不同的字符种类进行相同的排序,从而创建了一个“模式”。

    为了受益于set 的速度(O(1) 平均查找 vs O(n) 用于列表)并保持random 提供的混乱顺序,您可以使用辅助集进行测试,但存储在列表中:

    def append_until_length(acceptable, length=45):
        retval = []
        testset = set()
        for _ in range(10000):
            char = random.choice(acceptable)
            if char not in testset:  # fast lookup (O(1))
                retval.append(char)  # add to the result list
                testset.add(char)    # add to the set
            if len(retval) == length:
                return ''.join(retval)
    

    【讨论】:

    • 根据help(set)class set(object) Build an unordered collection of unique elements.
    • 无序意味着:你不能依赖于顺序。但至少在你的 python 版本/实现中有一个:stackoverflow.com/questions/12165200/…
    • 如果我做了set([1,2,3,4,5,6,7,8,9,10]),它应该以从大到小的顺序显示?
    • 在我的 python 3.4 版本上,我得到了完全相同的排序列表。最小 tp 最大。试试看。整数更容易,因为整数的散列大部分时间都是自己的(对于小的,除了-1)
    • 是的,我试过了。我还尝试了set([1,2,2,2,5,5,5,5,58,58,58]),它显示为set([1, 2, 58, 5])。 setss 真是个怪人哈哈。
    猜你喜欢
    • 1970-01-01
    • 2015-07-14
    • 2020-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多