一个列表(可能)可以被另一个整除吗?
Posted
技术标签:
【中文标题】一个列表(可能)可以被另一个整除吗?【英文标题】:Is a list (potentially) divisible by another? 【发布时间】:2018-02-05 00:00:18 【问题描述】:问题
假设您有两个整数列表 A = [a_1, a_2, ..., a_n]
和 B = [b_1, b_2, ..., b_n]
。如果B
的排列使得a_i
对于所有i
都可以被b_i
整除,我们就说A
可以被B
整除。那么问题是:是否可以重新排序(即置换)B
,以便所有i
的a_i
可以被b_i
整除?
例如,如果您有
A = [6, 12, 8]
B = [3, 4, 6]
那么答案将是True
,因为B
可以重新排序为B = [3, 6, 4]
,然后我们将得到a_1 / b_1 = 2
、a_2 / b_2 = 2
和a_3 / b_3 = 2
,它们都是整数,所以A
可以被B
整除。
作为一个应该输出False
的例子,我们可以有:
A = [10, 12, 6, 5, 21, 25]
B = [2, 7, 5, 3, 12, 3]
这是False
的原因是我们不能对B
重新排序,因为A
中有25 和5,但B
中唯一的除数是5,所以会省略一个。
方法
显然,直接的方法是获取B
的所有排列,看看是否满足potential-divisibility,类似于:
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)
问题
知道一个列表是否可以被另一个列表整除的最快方法是什么?有什么想法吗?我在想是否有一个聪明的方法可以用 primes 做到这一点,但我想不出一个解决方案。
非常感谢!
编辑:这可能与你们大多数人无关,但为了完整起见,我将解释我的动机。在群论中,有一个关于有限单群的猜想,即群的不可约特征和共轭类是否存在双射,使得每个特征度都划分相应的类大小。例如,对于 U6(4) here are what A
and B
would look like. 相当大的列表,请注意!
【问题讨论】:
有一个(现已删除)答案声称对两个列表进行排序是有效解决方案的一部分。这有什么问题吗? @cᴏʟᴅsᴘᴇᴇᴅ 我不知道为什么答案被删除了。我也不知道排序在这里是否会有所帮助。 @McGuire:a_i 和 b_i 有多大? @Blender 理想的任意大 是否允许在任一列表中重复?例如B
可以是[1,1,1]
?
【参考方案1】:
构建二分图结构 - 将 a[i]
与其来自 b[]
的所有除数连接起来。
然后找到maximum matching并检查它是否是完美匹配(匹配中的边数等于对数(如果图是有向的)或双数)。
任意选择Kuhn algorithm implementation here。
更新: @Eric Duminil 非常简洁 Python implementation here
这种方法的多项式复杂度从 O(n^2) 到 O(n^3),具体取决于所选择的匹配算法和边数(除法对)与蛮力算法的阶乘复杂度。
【讨论】:
@cᴏʟᴅsᴘᴇᴇᴅ:链接只是额外的文档。没有它们,答案仍然有效,因为在***或许多书籍上仍然可以找到提到的图论术语。 要检测完美匹配,使用高斯消元法找到Tutte matrix的行列式,然后检查是否非零。【参考方案2】:代码
在@MBo 的优秀answer 的基础上,这是一个使用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
注意事项
根据documentation:
maximum_matching() 返回的字典包含一个映射 左右顶点集中的顶点。
表示返回的dict应该是A
和B
的两倍。
节点转换自
[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 algorithm,在最坏的情况下以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
下面是对应的图表:
【讨论】:
【参考方案3】:既然您对数学很熟悉,我只想为其他答案增添光彩。要搜索的字词以粗体显示。
问题是位置受限的排列的一个实例,关于这些可以说很多。通常,当且仅当位置j
允许最初位于位置i
的元素时,可以构造一个零-一NxN
矩阵M
,其中M[i][j]
为1。满足所有限制的不同排列的数量 是M
的永久(定义与行列式相同,除了所有项都是非负数)。
唉 - 与行列式不同 - 在N
中,没有已知的通用方法可以比指数计算更快。但是,有多项式时间算法可以确定永久物是否为 0。
这就是您得到答案的地方开始 ;-) 这里很好地说明了“永久 0 是什么?”通过考虑二部图中的完美匹配可以有效地回答问题:
https://cstheory.stackexchange.com/questions/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
的相应元素整除。
【讨论】:
【参考方案4】:这不是最终的答案,但我认为这可能是有价值的。您可以首先列出列表[(1,2,5,10),(1,2,3,6,12),(1,2,3,6),(1,5),(1,3,7,21),(1,5,25)]
中所有元素的因子(包括1 和自身)。我们要查找的列表必须包含其中的一个因素(以均分)。
由于我们正在检查的列表中没有某些因素([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]
。这仍然与我们的列表一致,因此案例是正确的。请记住,我们将在每次可以轻松突破的迭代中查看与列表的一致性。
【讨论】:
【参考方案5】:你可以试试这个:
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
【讨论】:
这与 OP 的方法有何不同? @cᴏʟᴅsᴘᴇᴇᴅ 我同意,我正在寻找另一种方法以上是关于一个列表(可能)可以被另一个整除吗?的主要内容,如果未能解决你的问题,请参考以下文章