并行化蛮力生成

时间:2013-11-21 21:25:48

标签: python multithreading parallel-processing

我为此搜索了很多,但却找不到能抓住我头脑的东西。我正在寻找的是如何使这个算法并行。它的并行方式无关紧要,例如多线程或多处理甚至分布式计算,但我究竟如何在节点或核心之间拆分工作呢?

def bruteforce(charset, maxlength):
    return (''.join(candidate)
        for candidate in itertools.chain.from_iterable(itertools.product(charset, repeat=i)
        for i in range(1, maxlength + 1)))

2 个答案:

答案 0 :(得分:2)

如果我正确理解了您的生成器表达式,那么您将尝试生成长度为1的所有单词。maxlength给定一定的字母。

我不确切地知道你想如何分解你的问题(你有N工人吗?),但一个显而易见的方法就是将第一个字母的单词列表拆分出去对于各种并行工作者,然后所有人都必须将所有可能的单词组合附加到字母表中的0 .. maxlength - 1个字母。

答案 1 :(得分:1)

你要做的第一件事是对单线疾病不感兴趣;-)也就是说,并行处理增加了它自己的许多复杂性,所以你想让你的代码尽可能简单和透明。这是一种概括@BasSwinckels的建议的方法。它不短!但它非常有效:无论您拥有多少核心,它都会将您的CPU计量器固定在墙上。

CHARSET = "abcdefghijklmnopqrstuvwxyx"
MAX_LENGTH = 6  # generate all strings from CHARSET with length 1 thru MAX_LENGTH
NUM_PROCESSES = None # defaults to all available cores

from itertools import product

# string_gen is what the workers run.  Everything else
# runs in the main program.
def string_gen(prefix, suffix_len, length):
    # Generate all strings of length `length` starting with `prefix`.
    # If length > suffix_len, only the last suffix_len characters
    # need to be generated.
    num_done = 0
    if length <= suffix_len:
        assert prefix == ""
        for t in product(CHARSET, repeat=length):
            result = "".join(t)
            # do something with result
            num_done += 1
    else:
        assert len(prefix) + suffix_len == length
        for t in product(CHARSET, repeat=suffix_len):
            result = prefix + "".join(t)
            # do something with result
            num_done += 1
    return num_done

def record_done(num):
    global num_done
    num_done += num
    print num_done, "done so far"

def do_work(pool, strings_per_chunk=1000000):
    # What's the most chars we can cycle through without
    # exceeding strings_per_chunk?  Could do with this
    # logs, but I'm over-reacting to 1-liner disease ;-)
    N = len(CHARSET)
    suffix_len = 1
    while N**suffix_len <= strings_per_chunk:
        suffix_len += 1
    suffix_len -= 1
    print "workers will cycle through the last", suffix_len, "chars"

    # There's no point to splitting up very short strings.
    max_short_len = min(suffix_len, MAX_LENGTH)
    for length in range(1, max_short_len + 1):
        pool.apply_async(string_gen, args=("", suffix_len, length),
                         callback=record_done)
    # And now the longer strings.
    for length in range(max_short_len + 1, MAX_LENGTH + 1):
        for t in product(CHARSET, repeat=length-suffix_len):
            prefix = "".join(t)
            pool.apply_async(string_gen, args=(prefix, suffix_len, length),
                             callback=record_done)

if __name__ == "__main__":
    import multiprocessing
    pool = multiprocessing.Pool(NUM_PROCESSES)
    num_done = 0
    do_work(pool)
    pool.close()
    pool.join()
    expected = sum(len(CHARSET)**i
                   for i in range(1, MAX_LENGTH + 1))
    assert num_done == expected, (num_done, expected)

这有多个部分,因为你想要的是“块状”:各种尺寸的字符串。平行的噱头通常更容易,问题的结构更加完全统一。但是可以处理冗余 - 它只需要更多的代码。

请注意assert语句以及num_done的“无用”计算。并行代码增加了复杂性的全新维度,因此从一开始就要做好自己的帮助和代码防御。你会尝试许多根本不起作用的事情 - 这种情况发生在每个人身上。

同样要注意,即使没有多个核心,打破单线疾病也可以提供更有效的方法:计算和加入prefix只需一次更长的字符串将在课程中节省数十亿的冗余连接长跑。

玩得开心: - )