Проблема

Скажем, у вас есть два списка целых чисел 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 = 2, a_2 / b_2 = 2 и {{X5} }, все из которых являются целыми числами, поэтому A потенциально делится на B.

В качестве примера, который должен вывести False, мы могли бы иметь:

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

Причина в том, что False заключается в том, что мы не можем переупорядочить B, поскольку 25 и 5 находятся в 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)

Вопрос

Какой самый быстрый способ узнать, является ли список потенциально делимым другим списком? есть идеи? Я думал, есть ли умный способ сделать это с помощью простых чисел , но я не смог найти решение.

Очень признателен!


Изменить . Вероятно, это не имеет отношения к большинству из вас, но для полноты изложения я объясню свою мотивацию. В теории групп существует гипотеза о конечных простых группах о том, существует ли биекция из неприводимых символов и классов сопряженности группы так, что каждая степень символа делит соответствующий размер класса. Например, для U6 (4) вот что A и { {X1}} будет выглядеть так. Заметьте, довольно большие списки!

67
McGuire 27 Авг 2017 в 18:50

5 ответов

Лучший ответ

Построить структуру двудольного графа - соединить a[i] со всеми его делителями из b[]. введите описание изображения здесь

Затем найдите максимальное соответствие и проверьте, является ли идеальное соответствие (количество ребер в совпадении равно количеству пар (если граф направлен) или удвоенному числу).

Произвольно выбранная реализация алгоритма Куна здесь.

Upd :
@Eric Duminil сделал отличный лаконичный здесь реализация Python

Этот подход имеет полиномиальную сложность от O (n ^ 2) до O (n ^ 3) в зависимости от выбранного алгоритма сопоставления и количества ребер (пар деления) от факторной сложности для алгоритма грубой силы.

69
MBo 28 Авг 2017 в 05:20

Вы можете попробовать это:

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
2
Ajax1234 27 Авг 2017 в 16: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 (), включает отображение для вершин как в левом, так и в правом наборе вершин.

Это означает, что возвращаемый 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. Идентификатор также добавляется для сохранения отличия узлов в случае дублирования.

КПД

В методе maximum_matching используется алгоритм Хопкрофта-Карпа, который выполняется в 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

Вот соответствующие диаграммы:

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

30
Eric Duminil 1 Сен 2017 в 09:33

Это не окончательный ответ, но я думаю, что это может быть что-то стоящее. Сначала вы можете перечислить факторы (включая 1 и себя) всех элементов в списке [(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}] . что по-прежнему соответствует нашему списку, так что это правда. Помните, что мы будем смотреть на согласованность с нашим списком на каждой итерации, где мы можем легко выйти.

3
officialaimm 27 Авг 2017 в 16:40

Так как с математикой у вас все в порядке, я просто хочу добавить глянец к другим ответам. Термины для поиска показаны полужирным шрифтом .

Проблема - это случай перестановок с ограниченными позициями , и о них можно многое сказать. В общем случае матрицу NxN M, равную нулю, можно построить, где M[i][j] равен 1, если и только если позиция j разрешена для элемента, изначально находящегося в позиции {{X4} } } . число различных перестановок, отвечающих всем ограничениям, является тогда постоянным из M (определенным так же, как определитель, за исключением того, что все термины неотрицательны ) .

Увы, в отличие от определителя, не существует известных общих способов вычислить постоянное быстрее, чем экспоненциальное в N. Тем не менее, существуют алгоритмы полиномиального времени для определения, является ли перманент 0.

И вот где вы получили ответы start ;-) Вот хороший отчет о том, как "это постоянный 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.

17
Tim Peters 27 Авг 2017 в 23:59