program prime number code calculate python algorithm numbers primes division

python - prime - ¿Es una lista(potencialmente) divisible por otra?



python get prime numbers (5)

Problema

Supongamos que tiene dos listas A = [a_1, a_2, ..., a_n] y B = [b_1, b_2, ..., b_n] de enteros. Decimos que A es potencialmente divisible por B si hay una permutación de B que hace que a_i sea ​​divisible por b_i para todo i . El problema es entonces: ¿es posible reordenar (es decir, permutar) B para que a_i sea ​​divisible por b_i para todo i ? Por ejemplo, si tienes

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

Entonces la respuesta sería True , ya que B puede reordenarse para que sea B = [3, 6, 4] y entonces tendríamos que a_1 / b_1 = 2 , a_2 / b_2 = 2 , y a_3 / b_3 = 2 , todos que son enteros, por lo que A es potencialmente divisible por B

Como ejemplo que debería mostrar False , podríamos tener:

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

La razón por la que esto es False es que no podemos reordenar B ya que 25 y 5 están en A , pero el único divisor en B sería 5, por lo que uno quedaría fuera.

Enfoque

Obviamente, el enfoque directo sería obtener todas las permutaciones de B y ver si uno satisfaría la divisibilidad potencial , algo en la línea de:

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)

Pregunta

¿Cuál es la forma más rápida de saber si una lista es potencialmente divisible por otra lista? ¿Alguna idea? Estaba pensando si hay una manera inteligente de hacer esto con los números primos , pero no pude encontrar una solución.

¡Muy apreciado!

Editar : Probablemente sea irrelevante para la mayoría de ustedes, pero en aras de la exhaustividad, explicaré mi motivación. En la teoría de grupos, existe una conjetura sobre grupos simples finitos sobre si existe o no una biyección de los caracteres irreducibles y las clases de conjugación del grupo, de modo que cada grado de carácter divide el tamaño de clase correspondiente. Por ejemplo, para U6 (4), aquí se muestran cómo se verían A y B Bastante grandes listas, ¡cuidado!


Código

Sobre la base de la excelente answer @ MBo, aquí hay una implementación de la coincidencia de gráficos bipartitos usando 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

Notas

De acuerdo con la documentation :

El diccionario devuelto por maximum_matching () incluye una asignación para vértices en los conjuntos de vértices izquierdo y derecho.

Significa que el dict devuelto debe ser el doble de grande que A y B

Los nodos se convierten de

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

a:

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

para evitar colisiones entre nodos de A y B La identificación también se agrega para mantener los nodos distintos en caso de duplicados.

Eficiencia

El método maximum_matching utiliza el algoritmo Hopcroft-Karp , que se ejecuta en O(n**2.5) en el peor de los casos. La generación del gráfico es O(n**2) , por lo que todo el método se ejecuta en O(n**2.5) . Debería funcionar bien con matrices grandes. La solución de permutación es O(n!) Y no podrá procesar matrices con 20 elementos.

Con diagramas

Si está interesado en un diagrama que muestre la mejor coincidencia, puede mezclar matplotlib y 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

Aquí están los diagramas correspondientes:


Como te sientes cómodo con las matemáticas, solo quiero agregar un brillo a las otras respuestas. Los términos para buscar se muestran en negrita .

El problema es una instancia de permutaciones con posiciones restringidas , y hay mucho que decir sobre ellas. En general, se puede construir una matriz NxN cero M donde M[i][j] es 1 si y solo si se permite la posición j para el elemento originalmente en la posición i . El número de permutaciones distintas que cumplen todas las restricciones es entonces el permanente de M (definido de la misma manera que el determinante, excepto que todos los términos son no negativos).

Por desgracia, a diferencia del determinante, no hay formas generales conocidas de calcular el permanente más rápido que el exponencial en N Sin embargo, existen algoritmos de tiempo polinomiales para determinar si el permanente es o no 0.

Y ahí es donde comienzan las respuestas que obtuviste ;-) Aquí hay una buena cuenta de cómo el "¿es el 0 permanente?" La pregunta se responde de manera eficiente al considerar coincidencias perfectas en gráficos bipartitos:

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

Entonces, en la práctica, es poco probable que encuentres un enfoque general más rápido que el que @Eric Duminil dio en su respuesta.

Nota, agregada más tarde: debería aclarar esa última parte. Dada cualquier matriz de "permutación restringida" M , es fácil construir "listas de divisibilidad" enteras que le correspondan. Por lo tanto, su problema específico no es más fácil que el problema general, a menos que tal vez haya algo especial sobre qué números enteros pueden aparecer en sus listas.

Por ejemplo, supongamos que M es

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

Vea las filas como representando los primeros 4 primos, que también son los valores en B :

B = [2, 3, 5, 7]

La primera fila "dice" que B[0] (= 2) no puede dividir A[0] , pero debe dividir A[1] , A[2] y A[3] . Y así. Por construcción,

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

corresponde a M Y hay permanent(M) = 9 formas de permutar B tal manera que cada elemento de A es divisible por el elemento correspondiente de B permutado.


Construir estructura de gráfico bipartito: conecte a[i] con todos sus divisores de b[] .

Luego encuentre la coincidencia máxima y verifique si es una coincidencia perfecta (el número de aristas en la coincidencia es igual al número de pares (si se dirige el gráfico) o al número duplicado).

Implementación arbitraria del algoritmo Kuhn elegido aquí .

Upd:
@Eric Duminil hizo una gran implementación concisa de Python aquí

Este enfoque tiene una complejidad polinómica de O (n ^ 2) a O (n ^ 3) dependiendo del algoritmo de coincidencia elegido y el número de aristas (pares de división) contra la complejidad factorial para el algoritmo de fuerza bruta.


Esta no es la respuesta definitiva, pero creo que podría ser algo valioso. Primero puede enumerar los factores (1 y él incluido) de todos los elementos en la lista [(1,2,5,10),(1,2,3,6,12),(1,2,3,6),(1,5),(1,3,7,21),(1,5,25)] . La lista que estamos buscando debe tener uno de los factores (para dividir equitativamente). Dado que no tenemos algunos factores en la lista que estamos comprobando ( [2,7,5,3,12,3] ) Esta lista se puede filtrar como:

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

Aquí, se necesitan 5 dos lugares (donde no tenemos ninguna opción), pero solo tenemos un 5, por lo que podemos detenernos aquí y decir que el caso es falso aquí.

Digamos que teníamos [2,7,5,3,5,3] lugar:

Entonces tendríamos la opción como tal:

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

Como se necesita 5 en dos lugares:

[(2),(2,3),(2,3),{5},(3,7),{5}] Donde {} significa posición asegurada.

También 2 está asegurado:

[{2},(2,3),(2,3),{5},(3,7),{5}] Ahora, dado que se toma 2, se aseguran los dos lugares de 3:

[{2},{3},{3},{5},(3,7),{5}] Ahora, por supuesto, se toman 3 y se garantiza 7:

[{2},{3},{3},{5},{7},{5}] . que todavía es consistente con nuestra lista, por lo que el caso es cierto. Recuerde que estaremos observando las consistencias con nuestra lista en cada iteración donde podamos salir fácilmente.


Puedes probar esto:

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))

Salida:

True

Otro ejemplo:

l1 = [10, 12, 6, 5, 21, 25] l2 = [2, 7, 5, 3, 12, 3] print(potentially_divisible(l1, l2))

Salida:

False