如何创建一个脚本,为我提供六位代码的所有可能组合

How to create a script that gives me every combination possible of a six digit code

我和一个朋友想创建一个脚本,为我们提供六位代码的所有可能排列,由 36 个字母数字字符(0-9 和 a-z)组成,按字母顺序排列,然后能够看到它们在 .txt 文件中。

我希望它尽可能使用所有 CPU 和 RAM,以便完成任务所需的时间更少。

到目前为止,这是代码:

import random
charset = "0123456789abcdefghijklmnopqrstuvwxyz"
links = []
file = open("codes.txt", "a")

for g in range(0, 36**6):
    key = ""
    base = ""
    print(str(g))
    for i in range(0, 6):
        char = random.choice(charset)
        key += char
    base += key
    file.write(base + "\n")

file.close()

此代码 随机 生成组合并立即将它们写入 .txt 文件,同时打印它已经创建的代码数量,但它不是按字母顺序排列的(必须事后做),时间太长

如何改进代码以获得预期的结果?

第一件事;有更好的方法可以做到这一点,但我想写一些清晰易懂的东西。

伪代码:

base = "";
for(x1=0; x1<charset.length(); x1++)
    for(x2=0; x2<charset.length(); x2++)
        for(x3=0; x3<charset.length(); x3++)
            .
            .
            .
        { base = charset[x1]+charset[x2]+charset[x3]+.....+charset[x6];
          file.write(base + "\n")
        }

对于排列,这可以解决问题:

from itertools import permutations
charset = "0123456789abcdefghijklmnopqrstuvwxyz"
links = []
with open("codes.txt", "w") as f:
    for permutation in permutations(charset, 6):
        f.write(''.join(permutation) + '\n')

仅供参考,它将创建一个 7.8 GB 的文件

对于组合,这可以解决问题:

from itertools import combinations
charset = "0123456789abcdefghijklmnopqrstuvwxyz"
links = []
with open("codes.txt", "w") as f:
    for comb in combinations(charset, 6):
        f.write(''.join(comb)+ '\n')

仅供参考,它会创建一个 10.8 兆字节的文件

随机可能效率很低。你可以试试:

from itertools import permutations
from pandas import Series
charset = list("0123456789abcdefghijklmnopqrstuvwxyz")
links = []
file = open("codes.txt", "a")
comb = permutations(charset,6)
comb = list(comb)
comb = list(map(lambda x:return ''.join(x),comb))
mySeries = Series(comb)
mySeries = mySeries.sort_values()

base = ""
for k in mySeries:
    base += k
file.write(base + "\n")

file.close()

您可以使用默认 itertools 库中的 itertools.permutaions。您还可以指定组合中的字符数。

from itertools import permutations
charset = "0123456789abcdefghijklmnopqrstuvwxyz"

c = permutations(charset, 6)

with open('code.txt', 'w') as f:
    for i in c:
        f.write("".join(i) + '\n')

在我的计算机上运行大约 200 毫秒以创建排列列表,然后花费大量时间写入文件

这是一个组合问题,您试图从长度为 36 的字符集中获得长度为 6 的组合。这将产生大小为 36!/(30!*6!) 的输出。您可以参考 itertools 来解决像您这样的组合问题。可以参考itertools中的Combination函数 Documentation。建议不要使用 Python.

执行此类性能密集型计算

我能想到的最快方法是使用 pypy3 和以下代码:

import functools
import time
from string import digits, ascii_lowercase


@functools.lru_cache(maxsize=128)
def main():
    cl = []
    cs = digits + ascii_lowercase
    for letter in cs:
        cl.append(letter)
    ct = tuple(cl)
    with open("codes.txt", "w") as file:
        for p1 in ct:
            for p2 in ct:
                for p3 in ct:
                    for p4 in ct:
                        for p5 in ct:
                            for p6 in ct:
                                file.write(f"{p1}{p2}{p3}{p4}{p5}{p6}\n")


if __name__ == '__main__':
    start = time.time()
    main()
    print(f"Done!\nTook {time.time() - start} seconds!")

写入速度约为 10-15MB/s。我相信总文件约为 15GB,因此生成需要 990-1500 秒。结果是在具有 1 个 3.4 ghz 服务器内核 CPU 的 unraid 虚拟机上,以及一个旧的 SATA3 SSD。使用 NVME 驱动器和更快的单核 CPU.

可能会获得更好的结果

虽然这个 post 已经有 6 个答案,但我对其中任何一个都不满意,所以我决定贡献一个我自己的解决方案。

首先,请注意许多答案提供字母的 combinationspermutations,但是 post 实际上需要字母表的笛卡尔积本身(重复 N 次, 其中 N=6)。有(此时)两个答案可以做到这一点,但是它们都 write 次数过多,导致性能不佳,并且还在循环的最热部分连接它们的中间结果(也降低了性能).

为了最大限度地优化,我提供了以下代码:

from string import digits, ascii_lowercase
from itertools import chain

ALPHABET = (digits + ascii_lowercase).encode("ascii")

def fast_brute_force():
    # Define some constants to make the following sections more readable
    base_size = 6
    suffix_size = 4
    prefix_size = base_size - suffix_size
    word_size = base_size + 1
    
    # define two containers
    #   word_blob - placeholder words, with hyphens in the unpopulated characters (followed by newline)
    #   sleds - a tuple of repeated bytes, used for substituting a bunch of characters in a batch
    word_blob = bytearray(b"-" * base_size + b"\n")
    sleds = tuple(bytes([char]) for char in ALPHABET)

    # iteratively extend word_blob and sleds, and filling in unpopulated characters using the sleds
    # in doing so, we construct a single "blob" that contains concatenated suffixes of the desired
    # output with placeholders so we can quickly substitute in the prefix, write, repeat, in batches
    for offset in range(prefix_size, base_size)[::-1]:
        word_blob *= len(ALPHABET)
        word_blob[offset::word_size] = chain.from_iterable(sleds)
        sleds = tuple(sled * len(ALPHABET) for sled in sleds)
    
    with open("output.txt", "wb") as f:
        # I've expanded out the logic for substituting in the prefixes into explicit nested for loops
        # to avoid both redundancy (reassigning the same value) and avoiding overhead associated with
        # a recursive implementation
        # I assert this below, so any changes in suffix_size will fail loudly
        assert prefix_size == 2
        for sled1 in sleds:
            word_blob[0::word_size] = sled1
            for sled2 in sleds:
                word_blob[1::word_size] = sled2
                # we write to the raw FileIO since we know we don't need buffering or other fancy
                # bells and whistles, however in practice it doesn't seem that much faster
                f.raw.write(word_blob)

该代码块中发生了很多神奇的事情,但简而言之:

  • 我批​​处理写入,因此我一次写入 36**41679616 个条目,因此上下文切换较少。
  • 我使用字节数组切片/赋值,同时使用新前缀每批更新所有 1679616 个条目。
  • 我对字节进行操作,写入原始文件 IO,扩展前缀分配的循环,以及其他小优化以避免 encoding/buffering/function 调用 overhead/other 性能影响。

请注意,除非您有一个 非常 快速的磁盘和较慢的 CPU,否则您不会从较小的优化中看到太多好处,可能只是写入批处理。

在我的系统上,生成和写入 14880348 文件大约需要 45 秒,这是写入我最慢的磁盘。在我的 NVMe 驱动器上,需要 6.868 秒。