numpy 生成所有可能的约束组合

monwx1rj  于 2023-06-23  发布在  其他
关注(0)|答案(3)|浏览(96)

我想创建所有可能的size(N)数组的组合,假设元素可以是[-1,0,1],但是它只允许最多有2个元素[-1,1],而所有其他元素都应该是0。
递归方法可以满足N<1000的要求,但我正在寻找有效的(内存和计算)方法来生成直到N=10000。
对于N=6的递归情况和结果的尝试如下:

def generate_combinations(N):
    elements = [-1, 0, 1]
    combinations = []
    generate_combinations_recursive(elements, N, [], 0, 0, combinations)
    return combinations

def generate_combinations_recursive(elements, repetitions, current_combination, num_nonzero, index, combinations):
    if index == repetitions:
        combinations.append(tuple(current_combination))
        return

    for element in elements:
        if element != 0:
            if num_nonzero < 2:
                generate_combinations_recursive(elements, repetitions, current_combination + [element], num_nonzero + 1,
                                                index + 1, combinations)
        else:
            generate_combinations_recursive(elements, repetitions, current_combination + [element], num_nonzero,
                                            index + 1, combinations)

combinations = generate_combinations(N=6)

结果

[(-1, -1, 0, 0, 0, 0),
 (-1, 0, -1, 0, 0, 0),
 (-1, 0, 0, -1, 0, 0),
 (-1, 0, 0, 0, -1, 0),
 (-1, 0, 0, 0, 0, -1),
 (-1, 0, 0, 0, 0, 0),
 (-1, 0, 0, 0, 0, 1),
 (-1, 0, 0, 0, 1, 0),
 (-1, 0, 0, 1, 0, 0),
 (-1, 0, 1, 0, 0, 0),
 (-1, 1, 0, 0, 0, 0),
 (0, -1, -1, 0, 0, 0),
 (0, -1, 0, -1, 0, 0),
 (0, -1, 0, 0, -1, 0),
 (0, -1, 0, 0, 0, -1),
 (0, -1, 0, 0, 0, 0),
 (0, -1, 0, 0, 0, 1),
 (0, -1, 0, 0, 1, 0),
 (0, -1, 0, 1, 0, 0),
 (0, -1, 1, 0, 0, 0),
 (0, 0, -1, -1, 0, 0),
 (0, 0, -1, 0, -1, 0),
 (0, 0, -1, 0, 0, -1),
 (0, 0, -1, 0, 0, 0),
 (0, 0, -1, 0, 0, 1),
 (0, 0, -1, 0, 1, 0),
 (0, 0, -1, 1, 0, 0),
 (0, 0, 0, -1, -1, 0),
 (0, 0, 0, -1, 0, -1),
 (0, 0, 0, -1, 0, 0),
 (0, 0, 0, -1, 0, 1),
 (0, 0, 0, -1, 1, 0),
 (0, 0, 0, 0, -1, -1),
 (0, 0, 0, 0, -1, 0),
 (0, 0, 0, 0, -1, 1),
 (0, 0, 0, 0, 0, -1),
 (0, 0, 0, 0, 0, 0),
 (0, 0, 0, 0, 0, 1),
 (0, 0, 0, 0, 1, -1),
 (0, 0, 0, 0, 1, 0),
 (0, 0, 0, 0, 1, 1),
 (0, 0, 0, 1, -1, 0),
 (0, 0, 0, 1, 0, -1),
 (0, 0, 0, 1, 0, 0),
 (0, 0, 0, 1, 0, 1),
 (0, 0, 0, 1, 1, 0),
 (0, 0, 1, -1, 0, 0),
 (0, 0, 1, 0, -1, 0),
 (0, 0, 1, 0, 0, -1),
 (0, 0, 1, 0, 0, 0),
 (0, 0, 1, 0, 0, 1),
 (0, 0, 1, 0, 1, 0),
 (0, 0, 1, 1, 0, 0),
 (0, 1, -1, 0, 0, 0),
 (0, 1, 0, -1, 0, 0),
 (0, 1, 0, 0, -1, 0),
 (0, 1, 0, 0, 0, -1),
 (0, 1, 0, 0, 0, 0),
 (0, 1, 0, 0, 0, 1),
 (0, 1, 0, 0, 1, 0),
 (0, 1, 0, 1, 0, 0),
 (0, 1, 1, 0, 0, 0),
 (1, -1, 0, 0, 0, 0),
 (1, 0, -1, 0, 0, 0),
 (1, 0, 0, -1, 0, 0),
 (1, 0, 0, 0, -1, 0),
 (1, 0, 0, 0, 0, -1),
 (1, 0, 0, 0, 0, 0),
 (1, 0, 0, 0, 0, 1),
 (1, 0, 0, 0, 1, 0),
 (1, 0, 0, 1, 0, 0),
 (1, 0, 1, 0, 0, 0),
 (1, 1, 0, 0, 0, 0)]
f45qwnt8

f45qwnt81#

像这样的东西应该可以。只需立即处理输出,数组将在下一次迭代中修改:

def generate_permutations(N):
    if N < 2:
        raise Exception('N must be 2 or greater')
        
    result = [0] * N
    
    # all zeroes
    yield result
    
    # a single 1 or -1
    for i in range(N):
        result[i] = 1
        yield result
        result[i] = -1
        yield result
        result[i] = 0
    
    # 1 and -1
    for i in range(N):
        result[i] = 1
        for j in range(N):
            if i == j:
                continue
            result[j] = -1
            yield result
            result[j] = 0
        result[i] = 0
    
    # 1, 1 or -1, -1
    for i in range(N - 1):
        for j in range(i + 1, N):
            result[i] = 1
            result[j] = 1
            yield result
            result[i] = -1
            result[j] = -1
            yield result
            result[i] = 0
            result[j] = 0

counter = 0
for p in generate_permutations(6):
    counter += 1
    print(p)

print('Found', counter, 'permutations')

似乎总是有2 * N^2 + 1结果。

8xiog9wr

8xiog9wr2#

this answer之后,您可以使用sympy函数sympy.utilities.iterables.multiset_permutations并从中创建生成器函数。

from sympy.utilities.iterables import multiset_permutations

def generate_permutations(N):
    if N < 2:
        raise ValueError("N must be >= 2.")
    
    a = [-1, 1] + [0]*(N-2)
    for p in multiset_permutations(a):
        yield p
        
    a = [-1, -1] + [0]*(N-2)
    for p in multiset_permutations(a):
        yield p
    
    a = [1, 1] + [0]*(N-2)
    for p in multiset_permutations(a):
        yield p
    
    a = [1] + [0]*(N-1)
    for p in multiset_permutations(a):
        yield p
        
    a = [-1] + [0]*(N-1)
    for p in multiset_permutations(a):
        yield p
        
    yield [0]*N

for i, p in enumerate(generate_permutations(6), 1):
    print(p)

print(f"Generated {i} permuations.")
wvmv3b1j

wvmv3b1j3#

不需要图书馆。没有重复。这也可以用于您的问题的许多微小变化。

def subset_combs(n, subset_count):
    if 0 == n:
        yield []
    elif 0 < n:
        for subset in list(subset_count.keys()):
            count = subset_count[subset]
            if 0 < count:
                subset_count[subset] = count - 1
                for comb in subset_combs(n-1, subset_count):
                    for element in subset:
                        comb.append(element)
                        yield comb
                        comb.pop()
                subset_count[subset] = count

subset_count = {
    frozenset([0]): 6,
    frozenset([-1, 1]): 2,
}

for comb in subset_combs(6, subset_count):
    print(comb)

这是可行的,并产生了正确的结果,但你会得到深度递归。
同样的事情没有递归。

def subset_combs(n, subset_count):
    subsets = list(subset_count.keys())
    available = [subset_count[subsets[i]] for i in range(len(subsets))]
    solution = []
    stack = [(0, 0, False)]

    while len(stack):
        (i, j, is_tried) = stack.pop()
        if not is_tried and len(solution) == n:
            # Yield a copy for use.
            yield solution
        elif len(subsets) <= i:
            # Went off the end of subsets, abandon.
            pass
        elif len(subsets[i]) <= j:
            # Went off the end of this subset, try the next.
            stack.append((i+1, 0, False))
        elif is_tried:
            # Undo trying this.
            solution.pop()
            available[i] += 1
            # Try the next thing
            stack.append((i, j+1, False))
            stack.append((i, j+1, False))
        elif 0 < available[i]:
            # Add to the solution.
            solution.append(subsets[i][j])
            available[i] -= 1
            # Mark we tried this, and start over on the next.
            stack.append((i, j, True))
            stack.append((0, 0, False))
        else:
            # Try the next subset because we can't try this one.
            stack.append((i+1, 0, False))

n = 6
subset_count = {
    (0,):     n,
    (-1, 1,): 2,
}
for comb in subset_combs(n, subset_count):
    print(comb)

当我将最后一个修改为只计算解决方案并使用pypy时,它在2分钟内运行了n = 1000。它的运行为10000以及,但这可能需要一天的时间才能完成。

相关问题