列表是否可以被另一个整除?


68

问题

假设您有两个列表A = [a_1, a_2, ..., a_n]B = [b_1, b_2, ..., b_n]整数。我们说A潜在的,可分割B,如果有一个置换B,使得a_i整除b_i所有i。问题是:是否可以重新排序(即置换)B以便所有人都a_i可以整除?例如,如果您有b_ii

A = [6, 12, 8]
B = [3, 4, 6]

那么答案是True,因为B可以重新排序是B = [3, 6, 4],然后我们就会有a_1 / b_1 = 2a_2 / b_2 = 2a_3 / b_3 = 2,所有这一切都是整数,因此A是潜在的,整除B

作为应该输出的示例False,我们可以有:

A = [10, 12, 6, 5, 21, 25]
B = [2, 7, 5, 3, 12, 3]

这是False因为我们不能重新排序,B因为in中有25和5 A,但是in中的唯一除数B是5,因此将被忽略。

方法

显然,最直接的方法是获取的所有排列,B然后看是否满足分频比,这类似于:

import itertools
def is_potentially_divisible(A, B):
  perms = itertools.permutations(B)
  divisible = lambda ls: all( x % y == 0 for x, y in zip(A, ls))
  return any(divisible(perm) for perm in perms)

知道一个列表是否可以被另一个列表整除的最快方法是什么?有什么想法吗?我在想是否有一个聪明的方法可以用素数来做到这一点,但我无法提出解决方案。

非常感激!


编辑:这可能与大多数人无关,但是为了完整起见,我将解释我的动机。在群论中,有一个关于有限简单群的猜想,即是否存在不可约性和群的共轭类的双射,从而每个字符的度数都划分了相应的类大小。例如,对于U6(4) 这里是什么AB会是什么样子。相当大的清单,请注意!


1
有一个(现在已删除)答案,声称对两个列表进行排序是有效解决方案的一部分。这有问题吗?
cs95

@cᴏʟᴅsᴘᴇᴇᴅ我不知道为什么删除答案。我也不知道排序是否对您有帮助。
McGuire

@McGuire:a_ib_i有多大?
Blender

@Blender理想地任意大
McGuire

是否允许两个列表中的重复项?例如可能B[1,1,1]
MSeifert

Answers:


72

建立二部图结构-a[i]与它的所有除数连接b[]在此处输入图片说明

然后找到最大匹配并检查它是否是完美匹配匹配中的边数等于对数(如果有向图)或为双倍数)。

这里任意选择Kuhn算法的实现

Upd:
@Eric Duminil在这里做了非常简洁的Python实现

该方法具有多项式复杂度,从O(n ^ 2)到O(n ^ 3),具体取决于所选的匹配算法和边缘(分区对)的数量,而不是蛮力算法的阶乘复杂度。


20
@cᴏʟᴅsᴘᴇᴇᴅ:这些链接只是额外的文档。没有它们,答案仍然是有效的,因为提到的图论术语仍然可以在Wikipedia或许多书籍中找到。
埃里克·杜米尼尔

1
要检测完美匹配,请使用高斯消去法找到Tutte矩阵的行列式,然后检查非零值。
Vadim Ponomarenko

31

基于@MBo的出色答案,这是使用networkx进行二部图匹配的实现。

import networkx as nx

def is_potentially_divisible(multiples, divisors):
    if len(multiples) != len(divisors):
        return False

    g = nx.Graph()
    g.add_nodes_from([('A', a, i) for i, a in enumerate(multiples)], bipartite=0)
    g.add_nodes_from([('B', b, j) for j, b in enumerate(divisors)], bipartite=1)

    edges = [(('A', a, i), ('B', b, j)) for i, a in enumerate(multiples)
             for j, b in enumerate(divisors) if a % b == 0]
    g.add_edges_from(edges)
    m = nx.bipartite.maximum_matching(g)
    return len(m) // 2 == len(multiples)

print(is_potentially_divisible([6, 12, 8], [3, 4, 6]))
# True
print(is_potentially_divisible([6, 12, 8], [3, 4, 3]))
# True
print(is_potentially_divisible([10, 12, 6, 5, 21, 25], [2, 7, 5, 3, 12, 3]))
# False

笔记

根据文档

maximum_matching()返回的字典包含左顶点集和右顶点集中顶点的映射。

这意味着,返回的字典应该是两倍大AB

节点从

[10, 12, 6, 5, 21, 25]

至:

[('A', 10, 0), ('A', 12, 1), ('A', 6, 2), ('A', 5, 3), ('A', 21, 4), ('A', 25, 5)]

为了避免A和之间的节点之间发生冲突B。还添加了id,以使节点在重复的情况下保持不同。

效率

maximum_matching方法使用Hopcroft-Karp算法,该算法O(n**2.5)在最坏的情况下运行。图的生成是O(n**2),因此整个方法都在中运行O(n**2.5)。它在大型阵列上应该可以正常工作。排列解决方案是O(n!)并且将无法处理具有20个元素的数组。

有图

如果您对显示最佳匹配的图表感兴趣,可以混合使用matplotlib和networkx:

import networkx as nx
import matplotlib.pyplot as plt

def is_potentially_divisible(multiples, divisors):
    if len(multiples) != len(divisors):
        return False

    g = nx.Graph()

    l = [('l', a, i) for i, a in enumerate(multiples)]
    r = [('r', b, j) for j, b in enumerate(divisors)]

    g.add_nodes_from(l, bipartite=0)
    g.add_nodes_from(r, bipartite=1)

    edges = [(a,b) for a in l for b in r if a[1] % b[1]== 0]
    g.add_edges_from(edges)

    pos = {}
    pos.update((node, (1, index)) for index, node in enumerate(l))
    pos.update((node, (2, index)) for index, node in enumerate(r))

    m = nx.bipartite.maximum_matching(g)
    colors = ['blue' if m.get(a) == b else 'gray' for a,b in edges]

    nx.draw_networkx(g, pos=pos, arrows=False, labels = {n:n[1] for n in g.nodes()}, edge_color=colors)
    plt.axis('off')
    plt.show()

    return len(m) // 2 == len(multiples)

print(is_potentially_divisible([6, 12, 8], [3, 4, 6]))
# True
print(is_potentially_divisible([6, 12, 8], [3, 4, 3]))
# True
print(is_potentially_divisible([10, 12, 6, 5, 21, 25], [2, 7, 5, 3, 12, 3]))
# False

这是相应的图:

在此处输入图片说明 在此处输入图片说明 在此处输入图片说明


18

既然您对数学很满意,我只想在其他答案中加一个亮点。要搜索的术语以粗体显示

问题是位置受限排列排列实例,关于这些排列可以说很多。通常,当且仅当元素原本位于position的位置允许时,才能构造零一NxN矩阵M,其中等于M[i][j]1 。的数目满足所有的限制不同的排列是,则永久的(限定的方式与行列式相同,不同之处在于所有术语非负)。jiM

las-与行列式不同-没有已知的通用方法可以比指数更快地计算永久物N。但是,存在多项式时间算法来确定永久变量是否为0。

这就是您开始获得答案的地方;-)这很好地说明了“永久0是多少?” 通过考虑二部图中的完美匹配,可以有效地回答问题:

/cstheory/32885/matrix-permanent-is-0

因此,在实践中,您几乎不可能找到比@Eric Duminil在其答案中给出的方法更快的一般方法。

注意,稍后添加:我应该使最后一部分更清楚。给定任何“受限置换”矩阵M,构造与之相对应的整数“可区分性列表”很容易。因此,您的特定问题并不比一般问题更容易-除非也许有一些关于整数可能出现在列表中的特殊问题。

例如,假设M

0 1 1 1
1 0 1 1
1 1 0 1
1 1 1 0

查看代表前4个质数的行,这些行中的值也是B

B = [2, 3, 5, 7]

第一行,然后说:“”是B[0] (= 2)不能分割A[0]的,但要分A[1]A[2]A[3]。等等。通过施工,

A = [3*5*7, 2*5*7, 2*3*7, 2*3*5]
B = [2,     3,     5,     7]

对应于M。并且有一些permanent(M) = 9置换的方法,B使得的每个元素A都可以被置换的对应元素整除B


3

这不是最终的答案,但我认为这可能是有价值的。您可以首先列出list中所有元素的factor(包括其自身在内)[(1,2,5,10),(1,2,3,6,12),(1,2,3,6),(1,5),(1,3,7,21),(1,5,25)]。我们要查找的列表中必须包含因素之一(以平均划分)。由于列表中没有某些因素,因此我们针对([2,7,5,3,12,3])进行检查。该列表可以进一步过滤为:

[(2,5),(2,3,12),(2,3),(5),(3,7),(5)]

在这里,两个地方都需要5(我们根本没有任何选择),但是我们只有5,所以我们可以在这里停下来说这里的情况是错误的。

假设我们[2,7,5,3,5,3]改为:

然后我们将有这样的选择:

[(2,5),(2,3),(2,3),(5),(3,7),(5)]

由于在两个地方需要5:

[(2),(2,3),(2,3),{5},(3,7),{5}]{}表示保证位置。

同时确保2:

[{2},(2,3),(2,3),{5},(3,7),{5}] 现在,由于采用了2,所以确保了3的两个位置:

[{2},{3},{3},{5},(3,7),{5}] 现在当然采用3并确保7:

[{2},{3},{3},{5},{7},{5}]。这仍然与我们的清单一致,所以凯斯是真的。记住,在每次迭代中我们都可以轻松地查看清单中的一致性。


2

您可以尝试以下方法:

import itertools

def potentially_divisible(A, B):
    A = itertools.permutations(A, len(A))
   return len([i for i in A if all(c%d == 0 for c, d in zip(i, B))]) > 0

l1 = [6, 12, 8]
l2 = [3, 4, 6]

print(potentially_divisible(l1, l2))

输出:

True

另一个例子:

l1 = [10, 12, 6, 5, 21, 25]
l2 = [2, 7, 5, 3, 12, 3]

print(potentially_divisible(l1, l2))

输出:

False

1
这与OP的方法有何不同?
cs95

@cᴏʟᴅsᴘᴇᴇᴅ我同意,我正在寻找另一种方法
McGuire
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.