虽然这篇文章已经有 6 个答案,但我对其中的任何一个都不满意,所以我决定贡献自己的解决方案。
首先,请注意,许多答案都提供了字母的combinations
或permutations
,但该帖子实际上想要字母表的笛卡尔积本身(重复 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**4
或1679616
条目,因此上下文切换更少。
- 我
1679616
使用 bytearray 切片/分配同时使用新前缀更新每批的所有条目。
- 我对字节进行操作,写入原始 FileIO,扩展前缀分配的循环,以及其他小的优化以避免编码/缓冲/函数调用开销/其他性能损失。
请注意,除非您有一个非常快的磁盘和较慢的 CPU,否则您不会从较小的优化中看到太多好处,可能只是写入批处理。
在我的系统上,产品 + 写入文件大约需要 45 秒14880348
,这是写入我最慢的磁盘。在我的 NVMe 驱动器上,它需要6.868
几秒钟。