Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何加速代码以解决位删除难题_Python_Performance_Algorithm_Math - Fatal编程技术网

Python 如何加速代码以解决位删除难题

Python 如何加速代码以解决位删除难题,python,performance,algorithm,math,Python,Performance,Algorithm,Math,[这与以下方面有关] 我想用计算机为小尺寸的n解决以下难题。考虑长度为n的所有2 ^ n个二进制向量。对于每一个,您精确地删除n/3位,留下一个二进制向量长度2n/3(假设n是3的整数倍)。目标是选择删除的位,以便将保留在末尾的长度为2n/3的不同二进制向量的数量降至最低 例如,对于n=3,最佳答案是2个不同的向量11和00。对于n=6,它是4;对于n=9,它是6;对于n=12,它是10 我以前曾尝试将此问题作为以下类型的最小集合覆盖问题来解决。所有列表仅包含1和0 我说,如果您可以通过插入精确

[这与以下方面有关]

我想用计算机为小尺寸的n解决以下难题。考虑长度为n的所有2 ^ n个二进制向量。对于每一个,您精确地删除n/3位,留下一个二进制向量长度2n/3(假设n是3的整数倍)。目标是选择删除的位,以便将保留在末尾的长度为2n/3的不同二进制向量的数量降至最低

例如,对于n=3,最佳答案是2个不同的向量11和00。对于n=6,它是4;对于n=9,它是6;对于n=12,它是10

我以前曾尝试将此问题作为以下类型的最小集合覆盖问题来解决。所有列表仅包含1和0

我说,如果您可以通过插入精确的
x
符号从
a
生成
B
,则列表
a
涵盖了列表
B

考虑长度为
n
的1和0的所有2^n列表,并设置
x=n/3
。我想计算一组最小长度为
2n/3
的列表,这些列表涵盖了所有这些内容。David Eisenstat提供了将这个最小集合覆盖问题转换为混合整数编程问题的代码,该问题可以输入CPLEX(或者是开源的)


问题是,如果设置n=15,那么它输出的实例对于我能找到的任何解算器来说都太大。是否有更有效的方法来解决这个问题,所以我可以解决n=15,甚至n=18?

< p>首先考虑如果你有6位。你可以扔掉2块。因此,6-0、5-1或4-2的任何模式余额都可以转换为0000或1111。在3-3零一余额的情况下,任何模式都可以转换为四种情况之一:1000、0001、0111或1110。因此,6位的一个可能最小设置为:

0000
0001
0111
1110
1000
1111
现在考虑9个比特,3个扔掉。您有以下14个主模式集:

000000
100000
000001
010000
000010
110000
000011
001111
111100
101111
111101
011111
111110
111111
换句话说,每个模式集的中心都有1/0,每端都有n/3-1位的排列。例如,如果有24位,那么中间有17位,末端有7位。由于2^7=128,您将有4 x 128-2=510个可能的模式


要找到正确的删除,有各种算法。一种方法是找到当前位集和每个主模式之间的编辑距离。具有最小编辑距离的图案是要转换的图案。该方法使用动态规划。另一种方法是使用一组规则在模式中进行树搜索,以找到匹配的模式。

这并不能解决您的问题(嗯,还不够快),但您的想法不多,其他人可能会在这里找到一些有用的东西

这是一个简短的纯Python 3程序,使用回溯搜索和一些贪婪的排序启发式。它可以非常快速地求解N=3、6和9个实例。它也很快找到了一个大小为10的N=12的封面,但显然需要更长的时间来耗尽搜索空间(我没有时间了,它还在运行)。对于N=15,初始化时间已经很慢

这里,位字符串由普通的N位整数表示,因此占用的存储空间很少。这是为了用更快的语言轻松地重新编码。它确实大量使用整数集,但没有其他“高级”数据结构

希望这对别人有帮助!但很明显,随着N的增加,可能性的组合爆炸确保了如果不深入研究问题的数学,任何事情都不会“足够快”

def dump(cover):
    for s in sorted(cover):
        print("    {:0{width}b}".format(s, width=I))

def new_best(cover):
    global best_cover, best_size
    assert len(cover) < best_size
    best_size = len(cover)
    best_cover = cover.copy()
    print("N =", N, "new best cover, size", best_size)
    dump(best_cover)

def initialize(N, X, I):
    from itertools import combinations
    # Map a "wide" (length N) bitstring to the set of all
    # "narrow" (length I) bitstrings that generate it.
    w2n = [set() for _ in range(2**N)]
    # Map a narrow bitstring to all the wide bitstrings
    # it generates.
    n2w = [set() for _ in range(2**I)]
    for wide, wset in enumerate(w2n):
        for t in combinations(range(N), X):
            narrow = wide
            for i in reversed(t):  # largest i to smallest
                hi, lo = divmod(narrow, 1 << i)
                narrow = ((hi >> 1) << i) | lo
            wset.add(narrow)
            n2w[narrow].add(wide)
    return w2n, n2w

def solve(needed, cover):
    if len(cover) >= best_size:
        return
    if not needed:
        new_best(cover)
        return
    # Find something needed with minimal generating set.
    _, winner = min((len(w2n[g]), g) for g in needed)
    # And order its generators by how much reduction they make
    # to `needed`.
    for g in sorted(w2n[winner],
                    key=lambda g: len(needed & n2w[g]),
                    reverse=True):
        cover.add(g)
        solve(needed - n2w[g], cover)
        cover.remove(g)

N = 9  # CHANGE THIS TO WHAT YOU WANT

assert N % 3 == 0
X = N // 3 # number of bits to exclude
I = N - X  # number of bits to include

print("initializing")
w2n, n2w = initialize(N, X, I)
best_cover = None
best_size = 2**I + 1  # "infinity"
print("solving")
solve(set(range(2**N)), set())
改善效果追踪 对于N=12,这最终完成,确认最小覆盖集包含10个元素(它在开始时很快找到)。我没有计时,但至少花了5个小时

为什么?因为它接近脑死亡;-)完全简单的搜索将尝试256个8位短字符串的所有子集。有2**256个这样的子集,大约1.2e77-它不会在宇宙的预期寿命内完成;-)

这里的排序技巧首先检测到“所有0”和“所有1”短字符串必须在任何覆盖集中,所以选择它们。这就让我们只看剩下的254个短字符串。然后贪婪的“选择覆盖最多的元素”策略很快找到一个包含11个元素的覆盖集,然后很快找到一个包含10个元素的覆盖集。这恰好是最优的,但需要很长时间才能用尽所有其他可能性

此时,覆盖集达到10个元素的任何尝试都将被中止(它不可能小于10个元素!)。如果这完全是天真的,那么它需要尝试添加(所有0和“所有1”字符串)剩余254的所有8元素子集,254-choose-8约为3.8e14。比1.2e77小很多,但仍然太大,不实用。这是一个有趣的练习,可以理解代码是如何做得更好的。提示:这与问题中的数据有很大关系

工业强度解算器的复杂程度和复杂性无与伦比。我对这个简单的小程序在较小的问题实例上的表现感到惊喜!幸运的是

但对于N=15,这种简单的方法是没有希望的。它很快就找到了一个包含18个元素的封面,但在至少几个小时内没有任何明显的进展。在内部,它仍在使用包含数百(甚至数千)个元素的
needed
集合,这使得
solve()
的主体相当昂贵。它仍然有2×10 - 2 = 1022短字符串考虑,1022选择16是约6E34。即使这段代码的速度提高了一百万倍,我也不认为它会有明显的帮助

尽管如此,尝试还是很有趣的:-)

和一个小重写 这个版本在一次完整的N=12运行中运行速度至少快6倍,只需提前一个级别切断无用的搜索。还加快了初始化速度,减少了changin的内存使用
def dump(cover):
    for s in sorted(cover):
        print("    {:0{width}b}".format(s, width=I))

def new_best(cover):
    global best_cover, best_size
    assert len(cover) < best_size
    best_size = len(cover)
    best_cover = cover.copy()
    print("N =", N, "new best cover, size", best_size)
    dump(best_cover)

def initialize(N, X, I):
    from itertools import combinations
    # Map a "wide" (length N) bitstring to the set of all
    # "narrow" (length I) bitstrings that generate it.
    w2n = [set() for _ in range(2**N)]
    # Map a narrow bitstring to all the wide bitstrings
    # it generates.
    n2w = [set() for _ in range(2**I)]
    for wide, wset in enumerate(w2n):
        for t in combinations(range(N), X):
            narrow = wide
            for i in reversed(t):  # largest i to smallest
                hi, lo = divmod(narrow, 1 << i)
                narrow = ((hi >> 1) << i) | lo
            wset.add(narrow)
            n2w[narrow].add(wide)
    return w2n, n2w

def solve(needed, cover):
    if len(cover) >= best_size:
        return
    if not needed:
        new_best(cover)
        return
    # Find something needed with minimal generating set.
    _, winner = min((len(w2n[g]), g) for g in needed)
    # And order its generators by how much reduction they make
    # to `needed`.
    for g in sorted(w2n[winner],
                    key=lambda g: len(needed & n2w[g]),
                    reverse=True):
        cover.add(g)
        solve(needed - n2w[g], cover)
        cover.remove(g)

N = 9  # CHANGE THIS TO WHAT YOU WANT

assert N % 3 == 0
X = N // 3 # number of bits to exclude
I = N - X  # number of bits to include

print("initializing")
w2n, n2w = initialize(N, X, I)
best_cover = None
best_size = 2**I + 1  # "infinity"
print("solving")
solve(set(range(2**N)), set())
initializing
solving
N = 9 new best cover, size 6
    000000
    000111
    001100
    110011
    111000
    111111
def dump(cover):
    for s in sorted(cover):
        print("    {:0{width}b}".format(s, width=I))

def new_best(cover):
    global best_cover, best_size
    assert len(cover) < best_size
    best_size = len(cover)
    best_cover = cover.copy()
    print("N =", N, "new best cover, size", best_size)
    dump(best_cover)

def initialize(N, X, I):
    from itertools import combinations
    # Map a "wide" (length N) bitstring to the set of all
    # "narrow" (length I) bitstrings that generate it.
    w2n = [set() for _ in range(2**N)]
    # Map a narrow bitstring to all the wide bitstrings
    # it generates.
    n2w = [set() for _ in range(2**I)]
    # mask[i] is a string of i 1-bits
    mask = [2**i - 1 for i in range(N)]
    for t in combinations(range(N), X):
        t = t[::-1]  # largest i to smallest
        for wide, wset in enumerate(w2n):
            narrow = wide
            for i in t:  # delete bit 2**i
                narrow = ((narrow >> (i+1)) << i) | (narrow & mask[i])
            wset.add(narrow)
            n2w[narrow].add(wide)
    # release some space
    for i, s in enumerate(w2n):
        w2n[i] = list(s)
    return w2n, n2w

def solve(needed, cover):
    if not needed:
        if len(cover) < best_size:
            new_best(cover)
        return
    if len(cover) >= best_size - 1:
        # can't possibly be extended to a cover < best_size
        return
    # Find something needed with minimal generating set.
    _, winner = min((len(w2n[g]), g) for g in needed)
    # And order its generators by how much reduction they make
    # to `needed`.
    for g in sorted(w2n[winner],
                    key=lambda g: len(needed & n2w[g]),
                    reverse=True):
        cover.add(g)
        solve(needed - n2w[g], cover)
        cover.remove(g)

N = 9  # CHANGE THIS TO WHAT YOU WANT

assert N % 3 == 0
X = N // 3 # number of bits to exclude
I = N - X  # number of bits to include

print("initializing")
w2n, n2w = initialize(N, X, I)

best_cover = None
best_size = 2**I + 1  # "infinity"
print("solving")
solve(set(range(2**N)), set())

print("best for N =", N, "has size", best_size)
dump(best_cover)