Par exemple, j'ai des listes:
a[0] = [1, 1, 1, 0, 0]
a[1] = [1, 1, 0, 0, 1]
a[2] = [0, 1, 1, 1, 0]
# and so on
Ils semblent être différents, mais si l'on suppose que le début et la fin sont connectés, alors ils sont circulairement identiques.
Le problème est que chaque liste que j'ai a une longueur de 55 et ne contient que trois uns et 52 zéros. Sans condition circulaire, il y a 26 235 (55 choix 3) listes. Cependant, si la condition "circulaire" existe, il existe un grand nombre de listes circulairement identiques
Actuellement, je vérifie l'identité circulaire en suivant:
def is_dup(a, b):
for i in range(len(a)):
if a == list(numpy.roll(b, i)): # shift b circularly by i
return True
return False
Cette fonction nécessite 55 opérations de décalage cyclique dans le pire des cas. Et il y a 26 235 listes à comparer. En bref, j'ai besoin de 55 * 26,235 * (26,235 - 1)/2 = 18,926,847,225 calculs. C'est environ 20 Giga!
Existe-t-il un bon moyen de le faire avec moins de calculs? Ou tout type de données qui prend en charge circulaire?
Tout d'abord, cela peut être fait dans O(n)
en termes de longueur de la liste Vous pouvez remarquer que si vous dupliquez votre liste 2 fois ([1, 2, 3]
) Sera [1, 2, 3, 1, 2, 3]
alors votre nouvelle liste contiendra certainement toutes les listes cycliques possibles.
Il vous suffit donc de vérifier si la liste que vous recherchez se trouve dans les 2 fois de votre liste de départ. Dans python vous pouvez y parvenir de la manière suivante (en supposant que les longueurs sont les mêmes).
list1 = [1, 1, 1, 0, 0]
list2 = [1, 1, 0, 0, 1]
print ' '.join(map(str, list2)) in ' '.join(map(str, list1 * 2))
Quelques explications sur mon oneliner: list * 2
Combinera une liste avec lui-même, map(str, [1, 2])
convertira tous les nombres en chaîne et ' '.join()
convertira le tableau ['1', '2', '111']
En un chaîne '1 2 111'
.
Comme l'ont souligné certaines personnes dans les commentaires, oneliner peut potentiellement donner des faux positifs, afin de couvrir tous les cas possibles d'Edge:
def isCircular(arr1, arr2):
if len(arr1) != len(arr2):
return False
str1 = ' '.join(map(str, arr1))
str2 = ' '.join(map(str, arr2))
if len(str1) != len(str2):
return False
return str1 in str2 + ' ' + str2
PS1 en parlant de complexité temporelle, il convient de noter que O(n)
sera atteint si une sous-chaîne peut être trouvée dans O(n)
temps. Il n'en est pas toujours ainsi et dépend de l'implémentation dans votre langue ( bien que cela puisse potentiellement se faire en linéaire temps KMP par exemple).
P.S.2 pour les personnes qui ont peur du fonctionnement des cordes et de ce fait pensent que la réponse n'est pas bonne. Ce qui est important, c'est la complexité et la vitesse. Cet algorithme s'exécute potentiellement dans O(n)
time et O(n)
space ce qui le rend bien meilleur que tout dans le domaine O(n^2)
. Pour voir cela par vous-même, vous pouvez exécuter un petit benchmark (crée une liste aléatoire pop le premier élément et l'ajoute à la fin créant ainsi une liste cyclique. Vous êtes libre de faire vos propres manipulations)
from random import random
bigList = [int(1000 * random()) for i in xrange(10**6)]
bigList2 = bigList[:]
bigList2.append(bigList2.pop(0))
# then test how much time will it take to come up with an answer
from datetime import datetime
startTime = datetime.now()
print isCircular(bigList, bigList2)
print datetime.now() - startTime # please fill free to use timeit, but it will give similar results
0,3 seconde sur ma machine. Pas vraiment longtemps. Essayez maintenant de comparer cela avec les solutions O(n^2)
. Pendant qu'il le compare, vous pouvez voyager des États-Unis en Australie (très probablement en bateau de croisière)
Pas assez informé en Python pour répondre à cela dans la langue demandée, mais en C/C++, étant donné les paramètres de votre question, je convertirais les zéros et les uns en bits et les pousserais sur le bits les moins significatifs d'un uint64_t. Cela vous permettra de comparer les 55 bits en un seul coup - 1 horloge.
Extrêmement rapide, et le tout tiendra dans des caches sur puce (209 880 octets). La prise en charge matérielle pour déplacer simultanément les 55 membres de la liste vers la droite n'est disponible que dans les registres d'un processeur. Il en va de même pour comparer les 55 membres simultanément. Cela permet un mappage 1 pour 1 du problème avec une solution logicielle. (et en utilisant les registres SIMD/SSE 256 bits, jusqu'à 256 membres si nécessaire) En conséquence, le code est immédiatement évident pour le lecteur.
Vous pourriez être en mesure d'implémenter cela en Python, je ne le connais pas assez bien pour savoir si c'est possible ou quelles pourraient être les performances.
Après avoir dormi dessus, certaines choses sont devenues évidentes, et pour le mieux.
1.) Il est si facile de faire tourner la liste liée de manière circulaire en utilisant des bits que l'astuce très intelligente de Dali n'est pas nécessaire. À l'intérieur d'un registre 64 bits, le décalage de bits standard accomplira la rotation très simplement et dans une tentative de rendre tout cela plus Python convivial, en utilisant l'arithmétique au lieu des opérations de bits.
2.) Le décalage de bits peut être accompli facilement en utilisant la division par 2.
3.) La vérification de la fin de la liste pour 0 ou 1 peut être facilement effectuée par modulo 2.
4.) "Déplacer" un 0 vers la tête de la liste à partir de la queue peut être fait en divisant par 2. Cela parce que si le zéro était réellement déplacé, le 55ème bit serait faux, ce qu'il est déjà en ne faisant absolument rien.
5.) "Déplacer" un 1 vers la tête de la liste à partir de la queue peut être fait en divisant par 2 et en ajoutant 18 014 398 509 481 984 - qui est la valeur créée en marquant le 55e bit comme vrai et tout le reste faux.
6.) Si une comparaison de l'ancre et de uint64_t composé est VRAIE après une rotation donnée, rompez et retournez VRAI.
Je voudrais convertir tout le tableau de listes en un tableau de uint64_ts dès le départ pour éviter d'avoir à faire la conversion à plusieurs reprises.
Après avoir passé quelques heures à essayer d'optimiser le code, à étudier le langage d'assemblage, j'ai pu réduire de 20% le temps d'exécution. Je dois ajouter que le compilateur O/S et MSVC a également été mis à jour à la mi-journée hier. Pour quelque raison que ce soit, la qualité du code produit par le compilateur C s'est considérablement améliorée après la mise à jour (15/11/2014). Le temps d'exécution est maintenant ~ 70 horloges, 17 nanosecondes pour composer et comparer un anneau d'ancrage avec les 55 tours d'un anneau de test et NxN de tous les anneaux contre tous les autres se fait en 12,5 secondes.
Ce code est tellement serré, mais 4 registres sont assis à ne rien faire 99% du temps. Le langage d'assemblage correspond au code C presque ligne pour ligne. Très facile à lire et à comprendre. Un grand projet d'assemblage si quelqu'un apprenait cela par lui-même.
Le matériel est Hazwell i7, MSVC 64 bits, optimisations complètes.
#include "stdafx.h"
#include "stdafx.h"
#include <string>
#include <memory>
#include <stdio.h>
#include <time.h>
const uint8_t LIST_LENGTH = 55; // uint_8 supports full witdth of SIMD and AVX2
// max left shifts is 32, so must use right shifts to create head_bit
const uint64_t head_bit = (0x8000000000000000 >> (64 - LIST_LENGTH));
const uint64_t CPU_FREQ = 3840000000; // turbo-mode clock freq of my i7 chip
const uint64_t LOOP_KNT = 688275225; // 26235^2 // 1000000000;
// ----------------------------------------------------------------------------
__inline uint8_t is_circular_identical(const uint64_t anchor_ring, uint64_t test_ring)
{
// By trial and error, try to synch 2 circular lists by holding one constant
// and turning the other 0 to LIST_LENGTH positions. Return compare count.
// Return the number of tries which aligned the circularly identical rings,
// where any non-zero value is treated as a bool TRUE. Return a zero/FALSE,
// if all tries failed to find a sequence match.
// If anchor_ring and test_ring are equal to start with, return one.
for (uint8_t i = LIST_LENGTH; i; i--)
{
// This function could be made bool, returning TRUE or FALSE, but
// as a debugging tool, knowing the try_knt that got a match is Nice.
if (anchor_ring == test_ring) { // test all 55 list members simultaneously
return (LIST_LENGTH +1) - i;
}
if (test_ring % 2) { // ring's tail is 1 ?
test_ring /= 2; // right-shift 1 bit
// if the ring tail was 1, set head to 1 to simulate wrapping
test_ring += head_bit;
} else { // ring's tail must be 0
test_ring /= 2; // right-shift 1 bit
// if the ring tail was 0, doing nothing leaves head a 0
}
}
// if we got here, they can't be circularly identical
return 0;
}
// ----------------------------------------------------------------------------
int main(void) {
time_t start = clock();
uint64_t anchor, test_ring, i, milliseconds;
uint8_t try_knt;
anchor = 31525197391593472; // bits 55,54,53 set true, all others false
// Anchor right-shifted LIST_LENGTH/2 represents the average search turns
test_ring = anchor >> (1 + (LIST_LENGTH / 2)); // 117440512;
printf("\n\nRunning benchmarks for %llu loops.", LOOP_KNT);
start = clock();
for (i = LOOP_KNT; i; i--) {
try_knt = is_circular_identical(anchor, test_ring);
// The shifting of test_ring below is a test fixture to prevent the
// optimizer from optimizing the loop away and returning instantly
if (i % 2) {
test_ring /= 2;
} else {
test_ring *= 2;
}
}
milliseconds = (uint64_t)(clock() - start);
printf("\nET for is_circular_identical was %f milliseconds."
"\n\tLast try_knt was %u for test_ring list %llu",
(double)milliseconds, try_knt, test_ring);
printf("\nConsuming %7.1f clocks per list.\n",
(double)((milliseconds * (CPU_FREQ / 1000)) / (uint64_t)LOOP_KNT));
getchar();
return 0;
}
En lisant entre les lignes, on dirait que vous essayez d'énumérer un représentant de chaque classe d'équivalence circulaire de chaînes avec 3 uns et 52 zéros. Passons d'une représentation dense à une représentation clairsemée (ensemble de trois nombres dans range(55)
). Dans cette représentation, le décalage circulaire de s
par k
est donné par la compréhension set((i + k) % 55 for i in s)
. Le représentant lexicographique minimum dans une classe contient toujours la position 0. Étant donné un ensemble de la forme {0, i, j}
avec 0 < i < j
, les autres candidats au minimum dans la classe sont {0, j - i, 55 - i}
et {0, 55 - j, 55 + i - j}
. Par conséquent, nous avons besoin de (i, j) <= min((j - i, 55 - i), (55 - j, 55 + i - j))
pour que l'original soit minimal. Voici un code d'énumération.
def makereps():
reps = []
for i in range(1, 55 - 1):
for j in range(i + 1, 55):
if (i, j) <= min((j - i, 55 - i), (55 - j, 55 + i - j)):
reps.append('1' + '0' * (i - 1) + '1' + '0' * (j - i - 1) + '1' + '0' * (55 - j - 1))
return reps
Répétez le premier tableau, puis utilisez algorithme Z (heure O (n)) pour trouver le deuxième tableau à l'intérieur du premier.
(Remarque: vous n'avez pas à copier physiquement le premier tableau. Vous pouvez simplement boucler pendant la correspondance.)
La bonne chose à propos de l'algorithme Z est qu'il est très simple par rapport à KMP, BM, etc.
Cependant, si vous vous sentez ambitieux, vous pouvez faire une correspondance de chaîne en temps linéaire et constant espace - strstr
, par exemple, fait cela. Sa mise en œuvre serait cependant plus douloureuse.
Dans le prolongement de la solution très intelligente de Salvador Dali, la meilleure façon de la gérer est de s'assurer que tous les éléments sont de la même longueur, ainsi que les deux listes sont de la même longueur.
def is_circular_equal(lst1, lst2):
if len(lst1) != len(lst2):
return False
lst1, lst2 = map(str, lst1), map(str, lst2)
len_longest_element = max(map(len, lst1))
template = "{{:{}}}".format(len_longest_element)
circ_lst = " ".join([template.format(el) for el in lst1]) * 2
return " ".join([template.format(el) for el in lst2]) in circ_lst
Aucune idée si c'est plus rapide ou plus lent que la solution regex recommandée par AshwiniChaudhary dans la réponse de Salvador Dali, qui se lit comme suit:
import re
def is_circular_equal(lst1, lst2):
if len(lst2) != len(lst2):
return False
return bool(re.search(r"\b{}\b".format(' '.join(map(str, lst2))),
' '.join(map(str, lst1)) * 2))
Vous pouvez rouler une liste comme celle-ci:
list1, list2 = [0,1,1,1,0,0,1,0], [1,0,0,1,0,0,1,1]
str_list1="".join(map(str,list1))
str_list2="".join(map(str,list2))
def rotate(string_to_rotate, result=[]):
result.append(string_to_rotate)
for i in xrange(1,len(string_to_rotate)):
result.append(result[-1][1:]+result[-1][0])
return result
for x in rotate(str_list1):
if cmp(x,str_list2)==0:
print "lists are rotationally identical"
break
Convertissez d'abord chacun de vos éléments de liste (dans une copie si nécessaire) en que version pivotée qui est lexicalement la plus grande.
Triez ensuite la liste de listes résultante (en conservant un index dans la position de liste d'origine) et unifiez la liste triée, en marquant tous les doublons de la liste d'origine selon vos besoins.
Étant donné que vous devez faire autant de comparaisons, cela vaut-il la peine de faire un premier passage dans vos listes pour les convertir en une sorte de forme canonique qui peut être facilement comparée?
Essayez-vous d'obtenir un ensemble de listes circulaires uniques? Si c'est le cas, vous pouvez les jeter dans un ensemble après la conversion en tuples.
def normalise(lst):
# Pick the 'maximum' out of all cyclic options
return max([lst[i:]+lst[:i] for i in range(len(lst))])
a_normalised = map(normalise,a)
a_tuples = map(Tuple,a_normalised)
a_unique = set(a_tuples)
Toutes mes excuses à David Eisenstat pour ne pas avoir repéré sa réponse similaire.
S'inspirant de l'observation de @ SalvadorDali sur la recherche de correspondances de a dans n'importe quelle tranche de taille a en b + b, voici une solution utilisant simplement des opérations de liste.
def rollmatch(a,b):
bb=b*2
return any(not any(ax^bbx for ax,bbx in Zip(a,bb[i:])) for i in range(len(a)))
l1 = [1,0,0,1]
l2 = [1,1,0,0]
l3 = [1,0,1,0]
rollmatch(l1,l2) # True
rollmatch(l1,l3) # False
2ème approche: [supprimé]
Pas une réponse complète et autonome, mais sur le sujet de l'optimisation en réduisant les comparaisons, je pensais moi aussi aux représentations normalisées.
À savoir, si votre alphabet d'entrée est {0, 1}, vous pouvez réduire considérablement le nombre de permutations autorisées. Faites pivoter la première liste vers une forme (pseudo-) normalisée (compte tenu de la distribution dans votre question, je choisirais celle où l'un des 1 bits est à l'extrême gauche et l'un des 0 bits à l'extrême droite). Maintenant, avant chaque comparaison, tournez successivement l'autre liste à travers les positions possibles avec le même motif d'alignement.
Par exemple, si vous avez un total de quatre 1 bits, il peut y avoir au plus 4 permutations avec cet alignement, et si vous avez des clusters de 1 bits adjacents, chaque bit supplémentaire dans un tel cluster réduit la quantité de positions.
List 1 1 1 1 0 1 0
List 2 1 0 1 1 1 0 1st permutation
1 1 1 0 1 0 2nd permutation, final permutation, match, done
Cela se généralise aux alphabets plus grands et aux différents modèles d'alignement; le principal défi est de trouver une bonne normalisation avec seulement quelques représentations possibles. Idéalement, ce serait une normalisation appropriée, avec une seule représentation unique, mais étant donné le problème, je ne pense pas que ce soit possible.
Vous pouvez vérifier si une liste A est égale à un décalage cyclique de la liste B dans le temps O(N) attendu assez facilement.
J'utiliserais une fonction de hachage polynomiale pour calculer le hachage de la liste A, et chaque décalage cyclique de la liste B. Lorsqu'un décalage de la liste B a le même hachage que la liste A, je comparerais les éléments réels pour voir s'ils sont égaux .
La raison pour laquelle cela est rapide est qu'avec les fonctions de hachage polynomiales (qui sont extrêmement courantes!), Vous pouvez calculer le hachage de chaque décalage cyclique à partir du précédent en temps constant, afin de pouvoir calculer les hachages pour tous les décalages cycliques dans O(N) heure.
Cela fonctionne comme ceci:
Disons que B a N éléments, alors le hachage de B utilisant P premier est:
Hb=0;
for (i=0; i<N ; i++)
{
Hb = Hb*P + B[i];
}
C'est une façon optimisée d'évaluer un polynôme dans P, et équivaut à:
Hb=0;
for (i=0; i<N ; i++)
{
Hb += B[i] * P^(N-1-i); //^ is exponentiation, not XOR
}
Remarquez comment chaque B [i] est multiplié par P ^ (N-1-i). Si nous décalons B vers la gauche de 1, alors chaque B [i] sera multiplié par un P supplémentaire, sauf le premier. Étant donné que la multiplication se répartit sur l'addition, nous pouvons multiplier tous les composants à la fois simplement en multipliant le hachage entier, puis fixer le facteur pour le premier élément.
Le hachage du décalage gauche de B est juste
Hb1 = Hb*P + B[0]*(1-(P^N))
Le deuxième décalage à gauche:
Hb2 = Hb1*P + B[1]*(1-(P^N))
etc...
REMARQUE: toutes les mathématiques ci-dessus sont effectuées modulo une certaine taille de mot machine, et vous ne devez calculer P ^ N qu'une seule fois.
Simplifier le problème
(0,1)
1
S consécutifs dans un nombre0
consécutifs dans un nombre négatifExemple
A = [ 1, 1, 1, 0, 0, 1, 1, 0 ]
B = [ 1, 1, 0, 1, 1, 1, 0, 0 ]
~
A = [ +3, -2, +2, -1 ]
B = [ +2, -1, +3, -2 ]
Processus de vérification
La poignée
lookup
et look-ahead
Pseudo-code
FUNCTION IS_DUPLICATE (LIST L1, LIST L2) : BOOLEAN
LIST A = MAP_LIST(L1)
LIST B = MAP_LIST(L2)
LIST ALPHA = LOOKUP_INDEX(B, A[0])
IF A.SIZE != B.SIZE
OR COUNT_CHAR(A, 0) != COUNT_CHAR(B, ALPHA[0]) THEN
RETURN FALSE
END IF
FOR EACH INDEX IN ALPHA
IF ALPHA_NGRAM(A, B, INDEX, 1) THEN
IF IS_DUPLICATE(A, B, INDEX) THEN
RETURN TRUE
END IF
END IF
END FOR
RETURN FALSE
END FUNCTION
FUNCTION IS_DUPLICATE (LIST L1, LIST L2, INTEGER INDEX) : BOOLEAN
INTEGER I = 0
WHILE I < L1.SIZE DO
IF L1[I] != L2[(INDEX+I)%L2.SIZE] THEN
RETURN FALSE
END IF
I = I + 1
END WHILE
RETURN TRUE
END FUNCTION
Fonctions
MAP_LIST(LIST A):LIST
CARTE ÉLÉMENTS CONSQUETIFS COMME COMPTE DANS UNE NOUVELLE LISTE
LOOKUP_INDEX(LIST A, INTEGER E):LIST
RETOURNER LA LISTE DES INDICES O THE L'ÉLÉMENT E
EXISTENT DANS LA LISTE A
COUNT_CHAR(LIST A , INTEGER E):INTEGER
COMPTEZ COMBIEN DE FOIS UN ÉLÉMENT E
SE PRODUIT DANS UNE LISTE A
ALPHA_NGRAM(LIST A,LIST B,INTEGER I,INTEGER N):BOOLEAN
VÉRIFIEZ SI B[I]
IS ÉQUIVALENT À A[0]
N-GRAM
DANS LES DEUX DIRECTIONS
Enfin
Si la taille de la liste va être assez énorme ou si l'élément dont nous commençons à vérifier le cycle est souvent élevé, alors nous pouvons faire ce qui suit:
Recherchez l'élément le moins fréquent dans la première liste pour commencer
augmenter le paramètre n-gramme N pour réduire la probabilité de passer par une vérification linéaire
Une "forme canonique" efficace et rapide à calculer pour les listes en question peut être dérivée comme suit:
a
) doit être compris entre 18
Et 52
(Inclus). Ré-encodez-le entre 0
Et 34
.b
) doit être compris entre 0
Et 26
, Mais peu importe.52 - (a + b)
et n'ajoute aucune informationLa forme canonique est l'entier b * 35 + a
, Qui se situe entre 0
Et 936
(Inclus), qui est assez compact (il y a 477
Des listes circulairement uniques au total).
Comme d'autres l'ont mentionné, une fois que vous avez trouvé la rotation normalisée d'une liste, vous pouvez les comparer.
Voici un code de travail qui fait cela, la méthode de base consiste à trouver une rotation normalisée pour chaque liste et à comparer:
Notez que cette méthode ne dépend pas des nombres, vous pouvez passer des listes de chaînes (toutes les valeurs qui peuvent être comparées).
Au lieu de faire une recherche liste par liste, nous savons que nous voulons que la liste commence par la valeur minimale - afin que nous puissions parcourir les valeurs minimales, en recherchant jusqu'à ce que nous trouvions celle qui a les valeurs successives les plus faibles, en la stockant pour d'autres comparaisons jusqu'à ce que nous ayons le meilleur.
Il existe de nombreuses possibilités de sortie anticipée lors du calcul de l'indice, des détails sur certaines optimisations.
Notez que dans Python une recherche liste par liste pourrait bien être plus rapide, mais j'étais intéressé à trouver un algorithme efficace - qui pourrait également être utilisé dans d'autres langues. En outre, il y a un avantage pour éviter de créer de nouvelles listes.
def normalize_rotation_index(ls, v_min_other=None):
""" Return the index or -1 (when the minimum is above `v_min_other`) """
if len(ls) <= 1:
return 0
def compare_rotations(i_a, i_b):
""" Return True when i_a is smaller.
Note: unless there are large duplicate sections of identical values,
this loop will exit early on.
"""
for offset in range(1, len(ls)):
v_a = ls[(i_a + offset) % len(ls)]
v_b = ls[(i_b + offset) % len(ls)]
if v_a < v_b:
return True
Elif v_a > v_b:
return False
return False
v_min = ls[0]
i_best_first = 0
i_best_last = 0
i_best_total = 1
for i in range(1, len(ls)):
v = ls[i]
if v_min > v:
v_min = v
i_best_first = i
i_best_last = i
i_best_total = 1
Elif v_min == v:
i_best_last = i
i_best_total += 1
# all values match
if i_best_total == len(ls):
return 0
# exit early if we're not matching another lists minimum
if v_min_other is not None:
if v_min != v_min_other:
return -1
# simple case, only one minimum
if i_best_first == i_best_last:
return i_best_first
# otherwise find the minimum with the lowest values compared to all others.
# start looking after the first we've found
i_best = i_best_first
for i in range(i_best_first + 1, i_best_last + 1):
if (ls[i] == v_min) and (ls[i - 1] != v_min):
if compare_rotations(i, i_best):
i_best = i
return i_best
def compare_circular_lists(ls_a, ls_b):
# sanity checks
if len(ls_a) != len(ls_b):
return False
if len(ls_a) <= 1:
return (ls_a == ls_b)
index_a = normalize_rotation_index(ls_a)
index_b = normalize_rotation_index(ls_b, ls_a[index_a])
if index_b == -1:
return False
if index_a == index_b:
return (ls_a == ls_b)
# cancel out 'index_a'
index_b = (index_b - index_a)
if index_b < 0:
index_b += len(ls_a)
index_a = 0 # ignore it
# compare rotated lists
for i in range(len(ls_a)):
if ls_a[i] != ls_b[(index_b + i) % len(ls_b)]:
return False
return True
assert(compare_circular_lists([0, 9, -1, 2, -1], [-1, 2, -1, 0, 9]) == True)
assert(compare_circular_lists([2, 9, -1, 0, -1], [-1, 2, -1, 0, 9]) == False)
assert(compare_circular_lists(["Hello" "Circular", "World"], ["World", "Hello" "Circular"]) == True)
assert(compare_circular_lists(["Hello" "Circular", "World"], ["Circular", "Hello" "World"]) == False)
Voir: cet extrait pour d'autres tests/exemples.
C'est la même idée de Salvador Dali mais n'a pas besoin de la conversion de chaîne. Derrière se trouve la même idée de récupération KMP pour éviter une inspection de quart de travail impossible. Ils appellent uniquement KMPModified (list1, list2 + list2).
public class KmpModified
{
public int[] CalculatePhi(int[] pattern)
{
var phi = new int[pattern.Length + 1];
phi[0] = -1;
phi[1] = 0;
int pos = 1, cnd = 0;
while (pos < pattern.Length)
if (pattern[pos] == pattern[cnd])
{
cnd++;
phi[pos + 1] = cnd;
pos++;
}
else if (cnd > 0)
cnd = phi[cnd];
else
{
phi[pos + 1] = 0;
pos++;
}
return phi;
}
public IEnumerable<int> Search(int[] pattern, int[] list)
{
var phi = CalculatePhi(pattern);
int m = 0, i = 0;
while (m < list.Length)
if (pattern[i] == list[m])
{
i++;
if (i == pattern.Length)
{
yield return m - i + 1;
i = phi[i];
}
m++;
}
else if (i > 0)
{
i = phi[i];
}
else
{
i = 0;
m++;
}
}
[Fact]
public void BasicTest()
{
var pattern = new[] { 1, 1, 10 };
var list = new[] {2, 4, 1, 1, 1, 10, 1, 5, 1, 1, 10, 9};
var matches = Search(pattern, list).ToList();
Assert.Equal(new[] {3, 8}, matches);
}
[Fact]
public void SolveProblem()
{
var random = new Random();
var list = new int[10];
for (var k = 0; k < list.Length; k++)
list[k]= random.Next();
var rotation = new int[list.Length];
for (var k = 1; k < list.Length; k++)
rotation[k - 1] = list[k];
rotation[rotation.Length - 1] = list[0];
Assert.True(Search(list, rotation.Concat(rotation).ToArray()).Any());
}
}
J'espère que cette aide!
J'ai écrit une solution simple qui compare les deux listes et augmente simplement (et enveloppe) l'index de la valeur comparée pour chaque itération.
Je ne sais pas python bien donc je l'ai écrit en Java, mais c'est vraiment simple donc ça devrait être facile de l'adapter à n'importe quel autre langage.
Par cela, vous pouvez également comparer des listes d'autres types.
public class Main {
public static void main(String[] args){
int[] a = {0,1,1,1,0};
int[] b = {1,1,0,0,1};
System.out.println(isCircularIdentical(a, b));
}
public static boolean isCircularIdentical(int[] a, int[]b){
if(a.length != b.length){
return false;
}
//The outer loop is for the increase of the index of the second list
outer:
for(int i = 0; i < a.length; i++){
//Loop trough the list and compare each value to the according value of the second list
for(int k = 0; k < a.length; k++){
// I use modulo length to wrap around the index
if(a[k] != b[(k + i) % a.length]){
//If the values do not match I continue and shift the index one further
continue outer;
}
}
return true;
}
return false;
}
}
S'appuyant sur la réponse de RocketRoy: convertissez toutes vos listes à l'avance en nombres 64 bits non signés. Pour chaque liste, faites pivoter ces 55 bits pour trouver la plus petite valeur numérique.
Il vous reste maintenant une seule valeur 64 bits non signée pour chaque liste que vous pouvez comparer directement avec la valeur des autres listes. La fonction is_circular_identical () n'est plus requise.
(En substance, vous créez une valeur d'identité pour vos listes qui n'est pas affectée par la rotation des éléments des listes) Cela fonctionnerait même si vous en avez un nombre arbitraire dans vos listes.