una todas repeticion programa posibles permutaciones permutacion para numeros lista las combinaciones calcular algoritmo algorithm python

algorithm - todas - Comprobando si dos cadenas son permutaciones entre sí en Python



permutaciones python 3 (19)

Estoy comprobando si dos cadenas a y b son permutaciones entre sí, y me pregunto cuál es la forma ideal de hacerlo en Python. Desde el Zen de Python, "debe haber una, y preferiblemente solo una, forma obvia de hacerlo", pero veo que hay al menos dos formas:

sorted(a) == sorted(b)

y

all(a.count(char) == b.count(char) for char in a)

pero el primero es más lento cuando (por ejemplo) el primer carácter de a está en ninguna parte en b , y el segundo es más lento cuando en realidad son permutaciones.

¿Hay alguna forma mejor (ya sea en el sentido de más Pythonic, o en el sentido de más rápido en promedio) para hacerlo? ¿O debería simplemente elegir entre estos dos dependiendo de qué situación espero ser más común?


"pero el primero es más lento cuando (por ejemplo) el primer carácter de a está en ninguna parte en b".

Este tipo de análisis de desempeño de casos degenerados no es una buena idea. Es un agujero de rata del tiempo perdido pensando todo tipo de casos especiales oscuros.

Solo realice el análisis "general" de estilo O.

En general, los géneros son O ( n log ( n )).

El a.count(char) for char in a solución es O ( n 2 ). Cada pase de conteo es un examen completo de la secuencia.

Si un caso especial oscuro resulta ser más rápido o más lento, eso es posiblemente interesante. Pero solo importa cuando conoces la frecuencia de tus oscuros casos especiales. Al analizar los algoritmos de ordenamiento, es importante tener en cuenta que un buen número de tipos involucran datos que ya están en el orden correcto (ya sea por suerte o por un diseño inteligente), por lo que se debe ordenar el rendimiento en asuntos de datos previamente ordenados.

En su oscuro caso especial ("el primer carácter de a está en ninguna parte en b") ¿es esto lo suficientemente frecuente como para importar? Si es solo un caso especial en el que pensaste, déjalo a un lado. Si se trata de un hecho sobre sus datos, entonces considérelo.


Creo que el primero es la forma "obvia". Es más corto, más claro y es probable que sea más rápido en muchos casos porque el género incorporado de Python está altamente optimizado.


Tu segundo ejemplo en realidad no funcionará:

all(a.count(char) == b.count(char) for char in a)

solo funcionará si b no contiene caracteres adicionales que no estén en a. También duplica el trabajo si los caracteres en la cadena se repiten.

Si desea saber si dos cadenas son permutaciones de los mismos caracteres únicos , simplemente haga lo siguiente:

set(a) == set(b)

Para corregir tu segundo ejemplo:

all(str1.count(char) == str2.count(char) for char in set(a) | set(b))

los objetos set () sobrecargan el operador OR bit a bit para que evalúe la unión de ambos conjuntos. Esto asegurará que recorra todos los caracteres de ambas cadenas una sola vez por cada carácter.

Dicho esto, el método sorted () es mucho más simple e intuitivo, y sería lo que usaría.


Vaya con el primero, es mucho más sencillo y fácil de entender. Si en realidad se trata de cadenas increíblemente grandes y el rendimiento es un problema real, entonces no use Python, use algo como C.

En lo que se refiere al Zen de Python, que solo debería haber una forma obvia de hacer las cosas, se refiere a cosas pequeñas y simples. Obviamente, para cualquier tarea suficientemente complicada, siempre habrá tropecientos de pequeñas variaciones en las formas de hacerlo.


heurísticamente, es probable que sea mejor dividirlos según el tamaño de la cuerda.

Pseudocódigo:

returnvalue = false if len(a) == len(b) if len(a) < threshold returnvalue = (sorted(a) == sorted(b)) else returnvalue = naminsmethod(a, b) return returnvalue

Si el rendimiento es crítico, y el tamaño de la cadena puede ser grande o pequeño, esto es lo que haría.

Es bastante común dividir este tipo de cosas en función del tamaño o tipo de entrada. Los algoritmos tienen diferentes fortalezas o debilidades y sería absurdo usar uno donde otro sería mejor ... En este caso, el método de Namin es O (n), pero tiene un factor constante mayor que el método ordenado O (n log n).


Aquí hay una manera que es O (n), asintóticamente mejor que las dos formas que sugiere.

import collections def same_permutation(a, b): d = collections.defaultdict(int) for x in a: d[x] += 1 for x in b: d[x] -= 1 return not any(d.itervalues()) ## same_permutation([1,2,3],[2,3,1]) #. True ## same_permutation([1,2,3],[2,3,1,1]) #. False


Esta es una función PHP que escribí hace una semana y que verifica si dos palabras son anagramas. ¿Cómo se compararía esto (si se implementa de la misma manera en python) con los otros métodos sugeridos? ¿Comentarios?

public function is_anagram($word1, $word2) { $letters1 = str_split($word1); $letters2 = str_split($word2); if (count($letters1) == count($letters2)) { foreach ($letters1 as $letter) { $index = array_search($letter, $letters2); if ($index !== false) { unset($letters2[$index]); } else { return false; } } return true; } return false; }

Aquí hay una traducción literal a Python de la versión de PHP (por JFS):

def is_anagram(word1, word2): letters2 = list(word2) if len(word1) == len(word2): for letter in word1: try: del letters2[letters2.index(letter)] except ValueError: return False return True return False

Comentarios:

1. The algorithm is O(N**2). Compare it to @namin''s version (it is O(N)). 2. The multiple returns in the function look horrible.


Esta versión es más rápida que cualquier ejemplo presentado hasta ahora, excepto que es 20% más lenta que sorted(x) == sorted(y) para cadenas cortas. Depende de los casos de uso, pero generalmente el 20% de ganancia de rendimiento no es suficiente para justificar una complicación del código al usar versiones diferentes para cadenas cortas y largas (como en la respuesta de @patros).

No usa len por lo que acepta cualquier iterable, por lo tanto, funciona incluso para datos que no caben en la memoria, por ejemplo, dado dos grandes archivos de texto con muchas líneas repetidas, responde si los archivos tienen las mismas líneas (las líneas pueden estar en cualquier orden )

def isanagram(iterable1, iterable2): d = {} get = d.get for c in iterable1: d[c] = get(c, 0) + 1 try: for c in iterable2: d[c] -= 1 return not any(d.itervalues()) except KeyError: return False

No está claro por qué esta versión es más rápida que defaultdict (@ namin''s) una para iterable1 grande (probada en 25MB tesauro).

Si reemplazamos get in the loop por try: ... except KeyError entonces se realiza 2 veces más lento para cadenas cortas, es decir, cuando hay pocos duplicados.


Aquí está el código martinus en python. Solo funciona para cadenas ASCII:

def is_permutation(a, b): if len(a) != len(b): return False char_count = [0] * 256 for c in a: char_count[ord(c)] += 1 for c in b: char_count[ord(c)] -= 1 if char_count[ord(c)] < 0: return False return True


Lamento que mi código no esté en Python, nunca lo he usado, pero estoy seguro de que esto se puede traducir fácilmente a python. Creo que esto es más rápido que todos los demás ejemplos ya publicados. También es O (n), pero se detiene lo antes posible:

public boolean isPermutation(String a, String b) { if (a.length() != b.length()) { return false; } int[] charCount = new int[256]; for (int i = 0; i < a.length(); ++i) { ++charCount[a.charAt(i)]; } for (int i = 0; i < b.length(); ++i) { if (--charCount[b.charAt(i)] < 0) { return false; } } return true; }

Primero no uso un diccionario sino una matriz de tamaño 256 para todos los caracteres. El acceso al índice debe ser mucho más rápido. Luego, cuando se repite la segunda cadena, inmediatamente devuelvo falso cuando el conteo llega a estar por debajo de 0. Cuando el segundo ciclo ha finalizado, puede estar seguro de que las cadenas son una permutación, porque las cadenas tienen la misma longitud y ningún carácter se usó con mayor frecuencia en b en comparación con a.


Hice una comparación bastante minuciosa en Java con todas las palabras en un libro que tenía. El método de conteo supera al método de clasificación en todos los sentidos. Los resultados:

Testing against 9227 words. Permutation testing by sorting ... done. 18.582 s Permutation testing by counting ... done. 14.949 s

Si alguien quiere el algoritmo y el conjunto de datos de prueba, coméntelo.


En Python 3.1 / 2.7 puedes simplemente usar collections.Counter(a) == collections.Counter(b) .

Pero sorted(a) == sorted(b) sigue siendo el más obvio de mi humilde opinión. Está hablando de permutaciones, cambiar el orden, por lo que clasificar es la operación obvia para borrar esa diferencia.


Aquí hay algunas ejecuciones cronometradas en cadenas muy pequeñas, usando dos métodos diferentes:
1. clasificación
2. conteo (específicamente el método original por @namin).

a, b, c = ''confused'', ''unfocused'', ''foncused'' sort_method = lambda x,y: sorted(x) == sorted(y) def count_method(a, b): d = {} for x in a: d[x] = d.get(x, 0) + 1 for x in b: d[x] = d.get(x, 0) - 1 for v in d.itervalues(): if v != 0: return False return True

Los tiempos de ejecución promedio de los 2 métodos sobre 100.000 bucles son:

no coincidencia (cadena ayb)

$ python -m timeit -s ''import temp'' ''temp.sort_method(temp.a, temp.b)'' 100000 loops, best of 3: 9.72 usec per loop $ python -m timeit -s ''import temp'' ''temp.count_method(temp.a, temp.b)'' 10000 loops, best of 3: 28.1 usec per loop

partido (cadena ayc)

$ python -m timeit -s ''import temp'' ''temp.sort_method(temp.a, temp.c)'' 100000 loops, best of 3: 9.47 usec per loop $ python -m timeit -s ''import temp'' ''temp.count_method(temp.a, temp.c)'' 100000 loops, best of 3: 24.6 usec per loop

Tenga en cuenta que las cadenas utilizadas son muy pequeñas. La complejidad del tiempo de los métodos es diferente, por lo que verá resultados diferentes con cadenas muy grandes. Elija según sus datos, incluso puede usar una combinación de los dos.


En Swift (u otra implementación de idiomas), puede ver los valores codificados (en este caso, Unicode) y ver si coinciden.

Algo como:

let string1EncodedValues = "Hello".unicodeScalars.map() { //each encoded value $0 //Now add the values }.reduce(0){ total, value in total + value.value } let string2EncodedValues = "oellH".unicodeScalars.map() { $0 }.reduce(0) { total, value in total + value.value } let equalStrings = string1EncodedValues == string2EncodedValues ? true : false

Necesitará manejar espacios y casos según sea necesario.


def matchPermutation(s1, s2): a = [] b = [] if len(s1) != len(s2): print ''length should be the same'' return for i in range(len(s1)): a.append(s1[i]) for i in range(len(s2)): b.append(s2[i]) if set(a) == set(b): print ''Permutation of each other'' else: print ''Not a permutation of each other'' return #matchPermutaion(''rav'', ''var'') #returns True matchPermutaion(''rav'', ''abc'') #returns False


Esta es una solución O(n) en Python que usa hashing con diccionarios. Tenga en cuenta que no uso diccionarios predeterminados porque el código puede detenerse de esta manera si determinamos que las dos cadenas no son permutaciones después de verificar la segunda letra, por ejemplo.

def if_two_words_are_permutations(s1, s2): if len(s1) != len(s2): return False dic = {} for ch in s1: if ch in dic.keys(): dic[ch] += 1 else: dic[ch] = 1 for ch in s2: if not ch in dic.keys(): return False elif dic[ch] == 0: return False else: dic[ch] -= 1 return True


Esto se deriva de la respuesta de @patros .

from collections import Counter def is_anagram(a, b, threshold=1000000): """Returns true if one sequence is a permutation of the other. Ignores whitespace and character case. Compares sorted sequences if the length is below the threshold, otherwise compares dictionaries that contain the frequency of the elements. """ a, b = a.strip().lower(), b.strip().lower() length_a, length_b = len(a), len(b) if length_a != length_b: return False if length_a < threshold: return sorted(a) == sorted(b) return Counter(a) == Counter(b) # Or use @namin''s method if you don''t want to create two dictionaries and don''t mind the extra typing.


Comprobando si dos cadenas son permutaciones entre sí en Python

# First method def permutation(s1,s2): if len(s1) != len(s2):return False; return '' ''.join(sorted(s1)) == '' ''.join(sorted(s2)) # second method def permutation1(s1,s2): if len(s1) != len(s2):return False; array = [0]*128; for c in s1: array[ord(c)] +=1 for c in s2: array[ord(c)] -=1 if (array[ord(c)]) < 0: return False return True


Primero, para resolver tales problemas, por ejemplo, si String 1 y String 2 son exactamente iguales o no, fácilmente, puede usar un "si" ya que es O (1). En segundo lugar, es importante considerar que sean valores numéricos o que también sean palabras en la cadena. Si el último es verdadero (las palabras y los valores numéricos están en la cadena al mismo tiempo), su primera solución no funcionará. Puede mejorarlo utilizando la función "ord ()" para convertirlo en valor numérico ASCII. Sin embargo, al final, estás usando sort; por lo tanto, en el peor de los casos, su complejidad de tiempo será O (NlogN). Esta vez la complejidad no está mal. Pero, puedes hacerlo mejor. Puedes hacerlo O (N). Mi "sugerencia" es usar Array (lista) y configurar al mismo tiempo. Tenga en cuenta que encontrar un valor en Array necesita iteración por lo que la complejidad es O (N), pero buscar un valor en el conjunto (que supongo que está implementado con HashTable en Python, no estoy seguro) tiene O (1) complejidad de tiempo :

def Permutation2(Str1, Str2): ArrStr1 = list(Str1) #convert Str1 to array SetStr2 = set(Str2) #convert Str2 to set ArrExtra = [] if len(Str1) != len(Str2): #check their length return False elif Str1 == Str2: #check their values return True for x in xrange(len(ArrStr1)): ArrExtra.append(ArrStr1[x]) for x in xrange(len(ArrExtra)): #of course len(ArrExtra) == len(ArrStr1) ==len(ArrStr2) if ArrExtra[x] in SetStr2: #checking in set is O(1) continue else: return False return True