列表(可能)可以被另一个整除吗?

时间:2017-08-27 15:50:39

标签: python algorithm numbers primes division

问题

假设您有两个整数列表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_i为所有i整除? 例如,如果你有

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

然后答案为True,因为B可以重新排序为B = [3, 6, 4],然后我们会a_1 / b_1 = 2a_2 / b_2 = 2和{ {1}},所有这些都是整数,因此a_3 / b_3 = 2可能被A整除。

作为应输出B的示例,我们可以:

False

这是A = [10, 12, 6, 5, 21, 25] B = [2, 7, 5, 3, 12, 3] 的原因是我们无法重新排序False,因为25和5位于B,但A中唯一的除数是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)

问题

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

非常感谢!

编辑:它可能与大多数人无关,但为了完整起见,我会解释我的动机。在群论中,有一个关于有限单群的猜想是关于是否存在来自群组的不可约的字符和共轭类的双射,使得每个字符度除以相应的类大小。例如,对于 U6(4) here are what A and B would look like.相当大的列表,请注意!

5 个答案:

答案 0 :(得分:69)

构建二分图结构 - 将a[i]b[]的所有除数联系起来。 enter image description here

然后找到maximum matching并检查它是否是完美匹配(匹配中的边数等于对数(如果图是指向的)或加倍数)。

任意选择Kuhn algorithm implementation here

UPD:
@Eric Duminil非常简洁Python implementation here

该方法具有从O(n ^ 2)到O(n ^ 3)的多项式复杂度,这取决于所选择的匹配算法和用于蛮力算法的因子复杂度的边缘(除法对)的数量。

答案 1 :(得分:30)

代码

在@ 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()返回的字典包含映射   左顶点集和右顶点集中的顶点。

这意味着返回的字典应该是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)]

以避免来自AB的节点之间的冲突。还添加了id,以便在重复的情况下保持节点不同。

效率

maximum_matching方法使用Hopcroft-Karp algorithm,在最坏的情况下,O(n**2.5)运行O(n**2)。图生成为O(n**2.5),因此整个方法在O(n!)中运行。它应该适用于大型数组。置换解决方案为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 ,并且无法处理包含20个元素的数组。

使用图表

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

d <- structure(list(Date = structure(c(17349, 17350, 17351, 17352, 
                                       17353, 17354, 17355, 17356, 17357, 17358, 17359, 17360, 17361, 
                                       17362, 17363, 17364, 17365, 17366, 17367, 17368, 17369, 17370, 
                                       17371, 17372, 17373, 17374, 17375, 17376, 17377, 17378, 17379, 
                                       17380, 17381, 17382, 17383), class = "Date"), Ratio = c(67, 50, 
                                                                                               67, 50, 100, 50, 33, 67, 0, 0, 0, 0, 100, 75, 0, 0, 75, 100, 
                                                                                               67, 33, 33, 33, 50, 50, 67, 100, 67, 50, 25, 25, 33, 33, 100, 
                                                                                               33, 0)), .Names = c("Date", "Ratio"), row.names = 183:217, class = "data.frame")

library(xts)
dates = as.Date(d$Date,"%Y-%m-%d")
xs = xts(d$Ratio,dates)

library("forecast")
fixed.nValid <- 6
fixed.nTrain <- length(xs) - fixed.nValid
stepsAhead <- 2
error <- rep(0, fixed.nValid - stepsAhead + 1)
percent.error <- rep(0, fixed.nValid - stepsAhead + 1)
predictions <-rep(0, fixed.nValid - stepsAhead + 1)
for (j in fixed.nTrain:(fixed.nTrain + fixed.nValid - stepsAhead)) {
  train.ts <- window(xs, start = as.Date("2017-07-02"), end = as.Date("2017-07-02") + j)
  valid.ts <- window(xs, start = as.Date("2017-07-02") + j + stepsAhead, end = as.Date("2017-07-02") + j + stepsAhead)
  naive.pred <- naive(train.ts, h = stepsAhead)
  error[j - fixed.nTrain + 1] <- valid.ts - naive.pred$mean[stepsAhead]
  percent.error[j - fixed.nTrain + 1] <- error[j - fixed.nTrain + 1] / valid.ts
}
mean(abs(error))
sqrt(mean(error^2))
mean(abs(percent.error))

以下是相应的图表:

enter image description here enter image description here enter image description here

答案 2 :(得分:17)

由于您对数学很满意,我只想为其他答案添加光泽。要搜索的字词以粗体显示。

问题是排名限制的排列的一个例子,并且可以说很多关于那些的问题。一般情况下,当{且{1}}为1时,可以构造零>>> def rc_to_abc(x, y): return 'ABCDEFGHIJKLOMOPQRSTUVWXYZ'[x] + str(y) >>> rc_to_abc(1, 4) 'B4' >>> rc_to_abc(3, 2) 'D2' >>> rc_to_abc(4, 0) 'E0' 矩阵NxN,当且仅当位置M被允许用于最初位置{{1}的元素时}}。符合所有限制的不同排列的数字则是M[i][j]永久(定义与决定因素相同,但所有术语均为非负数)。

唉 - 与决定因素不同 - 在j中没有已知的通用方法来计算永久性比指数式更快。但是,有多项式时间算法用于确定永久物是否为0。

这就是你得到的答案开始 ;-)这里有一个很好的说明&#34;是永久的0?&#34;通过考虑二分图中的完美匹配来有效地回答问题:

https://cstheory.stackexchange.com/questions/32885/matrix-permanent-is-0

所以,在实践中,你不太可能找到比@Eric Duminil在答案中给出的更快的一般方法。

注意,稍后补充:我应该让最后一部分更清楚。鉴于任何&#34;限制排列&#34;矩阵i,很容易构造整数&#34; divisibilty list&#34;对应它。因此,您的具体问题并不比一般问题容易 - 除非您的列表中可能出现哪些整数有特殊之处。

例如,假设M

N

将行视为代表前4个素数,这也是M中的值:

M

然后第一行&#34;说&#34; 0 1 1 1 1 0 1 1 1 1 0 1 1 1 1 0 无法划分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的相应元素整除。

答案 3 :(得分:3)

这不是最终答案,但我认为这可能是值得的。您可以先列出列表[(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}]。这仍然与我们的列表一致,所以casse是真的。请记住,我们将在每次迭代中查看与列表的一致性,我们可以随时突破。

答案 4 :(得分: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