你如何洗牌一个以字符串列表作为值的字典,这样没有键是相邻的?

How do you shuffle a dictionary with list of strings as values such that no key is adjacent?

# Create a function to generate a random, 8-character password.
# It should satisfy the following requirements:

# 1) There should be exactly two characters from each of the following categories:
# - Uppercase letters
# - Lowercase letters
# - Numerals 0 - 9
# - Special Characters from the string “!@#$%^&*”

# 2) No two character categories should be adjacent.
# bad example: AXyu74$^

# 3) The categories should be randomly ordered.
# good example: 8b&U6Nz!  NLSUNULS
# */
import random, string
def generate_password():
    store = {}
    for category in 'ULSN':
        store[category] = []
    for i in range(2):
        store['U'].append(random.choice(string.ascii_letters).upper())
        store['L'].append(random.choice(string.ascii_letters).lower())
        store['N'].append(str(random.randint(0,9)))
        store['S'].append("!@#$%^&*"[random.randrange(0,7) + 1])
    print("".join([item for sublist in store.values() for item in sublist]))
    for item in store.items():
        print(item)
     # shuffle and eliminate adjacency
generate_password()

有一本有四个键的字典。每个键映射到不同的类别,每个键都有一个包含 2 个字符的列表作为值。

如何打乱字典以随机顺序构建字符串,这样键就不会相邻?

目标是有效地return一个没有相邻值的 8 个字符长的字符串。

示例和测试用例在问题陈述中

考虑到这个问题,我认为一个解决方案是将其分解为两个部分:模式和映射。

每个有效模式都以两个不同的类别开头;暂且称它们为 A 和 B。这给出了 AB****** 作为起始部分模式。在某些时候,将使用第三个类别 C:

ABC*****
ABAC****
ABABC***

通过完成这些部分模式,您可以生成从 ABCDABCD 到 ABABCDCD 的所有有效模式的列表。这些模式可以硬编码到程序中或从辅助文本文件加载。我粗略算了一下,不到6个! = 720 个有效模式,虽然我不确定少了多少。

选择了一个随机模式后,您需要一个从 ABCD 到 ULNS 的映射。 ULNS 的随机洗牌应该足够了。通读所选模式并使用映射来确定从哪些类别中挑选以及以何种顺序挑选。

例如,带有映射 LSUN 的模式 ABCADCBD 给出 LSULNUSN。将问题分解成独立的部分,模式和映射,更容易处理。

我的回答

import string
import random

categories = "ULNS"
prev = curr = None
passwd = ""
used = {}
for category in categories:
    used[category] = 0

while len(passwd) != 8: 
    while prev == curr:
        curr = random.choice(categories)

    if curr == "U":
        passwd += random.choice(list(string.ascii_uppercase))
    if curr == "L":
        passwd += random.choice(list(string.ascii_lowercase))
    if curr == "N":
        passwd += random.choice([str(i) for i in range(10)])
    if curr == "S":
        passwd += random.choice([s for s in "!@#$%^&*"])
    
    used[curr] += 1
    if used[curr] == 2: 
        used.pop(curr)
        categories = "".join(list(used.keys()))
    prev = curr
        
print(passwd)