J'ai eu cette question sur un test d'algorithmes hier, et je ne peux pas trouver la réponse. Cela me rend complètement fou, car cela valait environ 40 points. Je suppose que la plupart des élèves ne l'ont pas résolu correctement, car je n'ai pas trouvé de solution au cours des dernières 24 heures.
Étant donné une chaîne binaire arbitraire de longueur n, trouvez trois chaînes régulièrement espacées dans la chaîne si elles existent. Écrivez un algorithme qui résout cela en O (n * log (n)) temps.
Ainsi, les chaînes comme celles-ci en ont trois qui sont "régulièrement espacées": 11100000, 0100100100
edit: C'est un nombre aléatoire, il devrait donc pouvoir fonctionner pour n'importe quel nombre. Les exemples que j'ai donnés visaient à illustrer la propriété "régulièrement espacés". Donc 1001011 est un nombre valide. Avec 1, 4 et 7 étant ceux qui sont régulièrement espacés.
Réponses:
Finalement! En suivant les pistes de la réponse de sdcvvc , nous l'avons: l'algorithme O (n log n) pour le problème! C'est simple aussi, une fois que vous l'avez compris. Ceux qui ont deviné FFT avaient raison.
Le problème: on nous donne une chaîne binaire
S
de longueur n , et nous voulons y trouver trois 1 régulièrement espacés. Par exemple,S
peut être110110010
, où n = 9. Il a des 1 régulièrement espacés aux positions 2, 5 et 8.Balayez de
S
gauche à droite et faites une listeL
de positions de 1. Pour ce quiS=110110010
précède, nous avons la liste L = [1, 2, 4, 5, 8]. Cette étape est O (n). Le problème est maintenant de trouver une progression arithmétique de longueur 3 enL
, c'est-à-dire de trouver distincte a, b, c enL
telle que ba = cb , ou de manière équivalente a + c = 2b . Pour l'exemple ci-dessus, nous voulons trouver la progression (2, 5, 8).Faire un polynôme
p
avec des termes x k pour chaque k dansL
. Pour l'exemple ci-dessus, nous faisons le polynôme p (x) = (x + x 2 + x 4 + x 5 + x 8 ) . Cette étape est O (n).Trouvez le polynôme
q
= p 2 , en utilisant la transformation de Fourier rapide . Pour l'exemple ci-dessus, nous obtenons le polynôme q (x) = x 16 + 2x 13 + 2x 12 + 3x 10 + 4x 9 + x 8 + 2x 7 + 4x 6 + 2x 5 + x 4 + 2x 3 + x 2 . Cette étape est O (n log n).Ignorez tous les termes sauf ceux correspondant à x 2k pour certains k in
L
. Pour l'exemple ci-dessus, nous obtenons les termes x 16 , 3x 10 , x 8 , x 4 , x 2 . Cette étape est O (n), si vous choisissez de la faire du tout.Voici le point crucial: le coefficient de tout x 2b pour b dans
L
est précisément le nombre de paires (a, c) enL
sorte que a + c = 2b . [CLRS, Ex. 30.1-7] Une telle paire est toujours (b, b) (donc le coefficient est au moins 1), mais s'il existe une autre paire (a, c) , alors le coefficient est au moins 3, de (a, c ) et (c, a) . Pour l'exemple ci-dessus, nous avons le coefficient de x 10 à 3 précisément à cause de l'AP (2,5,8). (Ces coefficients x 2bseront toujours des nombres impairs, pour les raisons ci-dessus. Et tous les autres coefficients de q seront toujours pairs.)Donc, l'algorithme doit regarder les coefficients de ces termes x 2b , et voir si l'un d'entre eux est supérieur à 1. S'il n'y en a pas, alors il n'y a pas de 1 régulièrement espacés. S'il est un b dans
L
pour laquelle le coefficient de x 2b est supérieur à 1, alors nous savons qu'il ya une paire (a, c) - autre que (b, b) - pour lequel a + c = 2b . Pour trouver la paire réelle, nous essayons simplement chaque a inL
(le c correspondantserait 2b-a ) et voyons s'il y a un 1 à la position 2b-a inS
. Cette étape est O (n).C'est tout, les gars.
On pourrait se demander: devons-nous utiliser FFT? De nombreuses réponses, telles que bêta , flybywire et rsp , suggèrent que l'approche qui vérifie chaque paire de 1 et voit s'il y a un 1 à la «troisième» position, pourrait fonctionner en O (n log n), en fonction de l'intuition que s'il y a trop de 1, nous trouverions facilement un triple, et s'il y a trop peu de 1, vérifier toutes les paires prend peu de temps. Malheureusement, bien que cette intuition soit correcte et que l'approche simple soit meilleure que O (n 2 ), elle n'est pas significativement meilleure. Comme dans la réponse de sdcvvc , nous pouvons prendre le "jeu de type Cantor" de chaînes de longueur n = 3 k, avec des 1 aux positions dont la représentation ternaire ne contient que des 0 et 2 (pas de 1). Une telle chaîne a 2 k = n (log 2) / (log 3) ≈ n 0,63 uns et pas de 1 régulièrement espacés, donc vérifier toutes les paires serait de l'ordre du carré du nombre de 1: c'est 4 k ≈ n 1,26 qui est malheureusement asymptotiquement beaucoup plus grand que (n log n). En fait, le pire des cas est encore pire: Leo Moser en 1953 a construit (effectivement) de telles chaînes qui contiennent n 1-c / √ (log n) 1 mais pas de 1 régulièrement espacées, ce qui signifie que sur de telles chaînes, le simple l'approche prendrait Θ (n 2-2c / √ (log n) )- seulement un tout petit peu mieux que Θ (n 2 ) , étonnamment!
À propos du nombre maximum de 1 dans une chaîne de longueur n sans 3 espacés uniformément (ce que nous avons vu ci-dessus était au moins n 0,63 de la construction facile de type Cantor, et au moins n 1-c / √ (log n) avec Construction de Moser) - il s'agit de l' OEIS A003002 . Il peut également être calculé directement à partir de l' OEIS A065825 comme k tel que A065825 (k) ≤ n <A065825 (k + 1). J'ai écrit un programme pour les trouver, et il s'avère que l'algorithme glouton ne donne pas la plus longue chaîne de ce type. Par exemple, pour n = 9, on peut obtenir 5 1s (110100011) mais le gourmand n'en donne que 4 (110110000), pour n= 26 nous pouvons obtenir 11 1s (11001010001000010110001101) mais le gourmand n'en donne que 8 (11011000011011000000000000), et pour n = 74 nous pouvons obtenir 22 1s (11000010110001000001011010001000000000000000010001011010000010001101000011) mais le gourmand ne donne que 16000000001000001010001100000100000101000110000010000010 Ils sont d'accord à un certain nombre d'endroits jusqu'à 50 (par exemple tous de 38 à 50), cependant. Comme le disent les références OEIS, il semble que Jaroslaw Wroblewski s'intéresse à cette question, et il maintient un site Web sur ces ensembles sans moyenne . Les chiffres exacts ne sont connus que jusqu'à 194.
la source
Votre problème s'appelle MOYENNE en cet article (1999):
Wikipédia :
Cela suffit à résoudre votre problème :).
Ce qui est très important, c'est que O (n log n) est la complexité en termes de nombre de zéros et de uns, et non le nombre de uns (qui pourrait être donné sous forme de tableau, comme [1,5,9,15]). Vérifier si un ensemble a une progression arithmétique, des termes de nombre de 1, est difficile, et selon cet article à partir de 1999, aucun algorithme plus rapide que O (n 2 ) n'est connu, et on suppose qu'il n'existe pas.Quiconque ne tient pas compte de cela tente de résoudre un problème ouvert.
Autres informations intéressantes, pour la plupart non pertinentes:
Borne inférieure:
Une borne inférieure facile est un ensemble de type Cantor (les nombres 1..3 ^ n-1 ne contenant pas 1 dans leur expansion ternaire) - sa densité est n ^ (log_3 2) (environ 0,631). Donc, vérifier si l'ensemble n'est pas trop grand, puis vérifier toutes les paires ne suffit pas pour obtenir O (n log n). Vous devez étudier la séquence plus intelligemment. Une meilleure borne inférieure est citée ici - c'est n 1-c / (log (n)) ^ (1/2) . Cela signifie que le jeu de Cantor n'est pas optimal.
Limite supérieure - mon ancien algorithme:
On sait que pour n grand, un sous-ensemble de {1,2, ..., n} ne contenant pas de progression arithmétique a au plus n / (log n) ^ (1/20) éléments. L'étude Sur les triplets en progression arithmétique prouve davantage: l'ensemble ne peut contenir plus de n * 2 28 * (log log n / log n) 1/2 éléments. Vous pouvez donc vérifier si cette limite est atteinte et sinon, vérifier naïvement les paires. Il s'agit de l' algorithme O (n 2 * log log n / log n), plus rapide que O (n 2 ). Malheureusement, "On triples ..." est sur Springer - mais la première page est disponible, et l'exposition de Ben Green est disponible ici , page 28, théorème 24.
Soit dit en passant, les journaux datent de 1999 - la même année que le premier que j'ai mentionné, c'est probablement pourquoi le premier ne mentionne pas ce résultat.
la source
Ce n'est pas une solution, mais une ligne de pensée similaire à ce que pensait Olexiy
Je jouais avec la création de séquences avec un nombre maximum d'unités, et elles sont toutes assez intéressantes, j'ai obtenu jusqu'à 125 chiffres et voici les 3 premiers nombres trouvés en essayant d'insérer autant de bits '1' que possible:
Remarquez qu'ils sont tous fractales (pas trop surprenant compte tenu des contraintes). Il peut y avoir quelque chose à penser en arrière, peut-être que si la corde n'est pas une fractale avec une caractéristique, alors elle doit avoir un motif répétitif?
Merci à beta pour le meilleur terme pour décrire ces chiffres.
Mise à jour: Hélas, il semble que le modèle se décompose en commençant par une chaîne initiale suffisamment grande, telle que: 10000000000001:
la source
Je soupçonne qu'une approche simple qui ressemble à O (n ^ 2) donnera en fait quelque chose de mieux, comme O (n ln (n)). Les séquences qui prennent le plus de temps à tester (pour un n donné) sont celles qui ne contiennent pas de trios, ce qui impose des restrictions sévères sur le nombre de 1 qui peuvent être dans la séquence.
Je suis venu avec quelques arguments ondulants, mais je n'ai pas été en mesure de trouver une bonne preuve. Je vais essayer dans le noir: la réponse est une idée très intelligente que le professeur connaît depuis si longtemps qu'elle en est venue à paraître évidente, mais c'est beaucoup trop difficile pour les étudiants. (Soit cela, soit vous avez dormi pendant la conférence qui l'a couvert.)
la source
Révision: 17/10/2009 23:00
J'ai exécuté cela sur de grands nombres (comme des chaînes de 20 millions) et je crois maintenant que cet algorithme n'est pas O (n logn). Malgré cela, c'est une implémentation assez cool et contient un certain nombre d'optimisations qui la rendent très rapide. Il évalue tous les arrangements de chaînes binaires de 24 chiffres ou moins en moins de 25 secondes.
J'ai mis à jour le code pour inclure l'
0 <= L < M < U <= X-1
observation du début de la journée.Original
C'est, dans le concept, similaire à une autre question à laquelle j'ai répondu . Ce code a également examiné trois valeurs dans une série et déterminé si un triplet satisfaisait une condition. Voici le code C # adapté de cela:
Les principales différences sont:
Ce code génère un ensemble puissant de données pour trouver l'entrée la plus difficile à résoudre pour cet algorithme.
Le code de la question précédente a généré toutes les solutions à l'aide d'un générateur python. Ce code affiche simplement le plus difficile pour chaque longueur de motif.
Ce code vérifie la distance entre l'élément central et ses bords gauche et droit. Le code python testait si une somme était supérieure ou inférieure à 0.
Le code actuel fonctionne du milieu vers le bord pour trouver un candidat. Le code du problème précédent fonctionnait des bords vers le milieu. Ce dernier changement donne une grande amélioration des performances.
Sur la base des observations à la fin de cet article, le code recherche des paires de nombres pairs de paires de nombres impairs pour trouver L et U, en gardant M fixe. Cela réduit le nombre de recherches en pré-calculant les informations. En conséquence, le code utilise deux niveaux d'indirection dans la boucle principale de FindCandidate et nécessite deux appels à FindCandidate pour chaque élément du milieu: une fois pour les nombres pairs et une fois pour les nombres impairs.
L'idée générale est de travailler sur des index, pas sur la représentation brute des données. Le calcul d'un tableau dans lequel les 1 apparaissent permet à l'algorithme de s'exécuter dans le temps proportionnel au nombre de 1 dans les données plutôt que dans le temps proportionnel à la longueur des données. Il s'agit d'une transformation standard: créez une structure de données qui permet un fonctionnement plus rapide tout en gardant l'équivalent du problème.
Les résultats sont obsolètes: supprimés.
Edit: 16/10/2009 18:48
Sur les données de yx, qui ont une certaine crédibilité dans les autres réponses comme représentatives de données concrètes sur lesquelles calculer, j'obtiens ces résultats ... Je les ai supprimés. Ils ne sont plus à jour.
Je tiens à souligner que ces données ne sont pas les plus difficiles pour mon algorithme, donc je pense que l'hypothèse selon laquelle les fractales de yx sont les plus difficiles à résoudre est erronée. Le pire des cas pour un algorithme particulier, je suppose, dépendra de l'algorithme lui-même et ne sera probablement pas cohérent entre les différents algorithmes.
Edit: 17/10/2009 13:30
Autres observations à ce sujet.
Commencez par convertir la chaîne de 0 et de 1 en un tableau d'index pour chaque position des 1. Disons que la longueur de ce tableau A est X. Alors le but est de trouver
tel que
ou
Puisque A [L] et A [U] sont un nombre pair, ils ne peuvent pas être (pair, impair) ou (impair, pair). La recherche d'une correspondance pourrait être améliorée en divisant A [] en groupes pairs et impairs et en recherchant des correspondances sur A [M] dans les groupes de candidats pairs et impairs à tour de rôle.
Cependant, il s'agit plus d'une optimisation des performances qu'une amélioration algorithmique, je pense. Le nombre de comparaisons devrait baisser, mais l'ordre de l'algorithme devrait être le même.
Edit 2009-10-18 00:45
Une autre optimisation me vient à l'esprit, dans la même veine que la séparation des candidats en pairs et impairs. Étant donné que les trois index doivent s'ajouter à un multiple de 3 (a, a + x, a + 2x - mod 3 est 0, indépendamment de a et x), vous pouvez séparer L, M et U dans leurs valeurs de mod 3 :
En fait, vous pouvez combiner cela avec l'observation paire / impaire et les séparer en leurs valeurs mod 6:
etc. Cela fournirait une optimisation supplémentaire des performances, mais pas une accélération algorithmique.
la source
Je n'ai pas encore pu trouver la solution :(, mais j'ai quelques idées.
Et si nous partions d'un problème inverse: construisons une séquence avec le nombre maximum de 1 et SANS trios régulièrement espacés. Si vous pouvez prouver que le nombre maximum de 1 est o (n), alors vous pouvez améliorer votre estimation en itérant uniquement sur une liste de 1.
la source
Cela peut aider ...
Ce problème se réduit à ce qui suit:
Par exemple, étant donné une séquence de
[ 3, 5, 1, 3, 6, 5, 2, 2, 3, 5, 6, 4 ]
, nous trouverions une sous-séquence de[ 3, 6, 5, 2, 2]
avec un préfixe de[ 3, 6 ]
avec préfixe somme de9
et un suffixe de[ 5, 2, 2 ]
avec suffixe somme de9
.La réduction est la suivante:
Par exemple, étant donné une séquence de
[ 0, 1, 1, 0, 0, 1, 0, 0, 0, 1 0 ]
, nous trouverions la réduction de[ 1, 3, 4]
. À partir de cette réduction, nous calculons la sous-séquence contiguë de[ 1, 3, 4]
, le préfixe de[ 1, 3]
avec somme de4
et le suffixe de[ 4 ]
avec somme de4
.Cette réduction peut être calculée en
O(n)
.Malheureusement, je ne sais pas trop où aller à partir d'ici.
la source
Pour le type de problème simple (c'est-à-dire que vous recherchez trois "1" avec seulement (c'est-à-dire zéro ou plus) "0" entre eux), c'est assez simple: vous pouvez simplement diviser la séquence à chaque "1" et rechercher deux sous-séquences adjacentes ayant la même longueur (la deuxième sous-séquence n'étant pas la dernière, bien sûr). Évidemment, cela peut être fait en temps O (n) .
Pour la version plus complexe (c'est-à-dire que vous recherchez un index i et un intervalle g > 0 tels que
s[i]==s[i+g]==s[i+2*g]=="1"
), je ne suis pas sûr, s'il existe une solution O (n log n) , car il y a éventuellement des triplets O (n²) ayant cette propriété (pensez à une chaîne de tous, il y a environ n² / 2 triplets de ce type). Bien sûr, vous ne recherchez qu'un seul d'entre eux, mais je n'ai actuellement aucune idée, comment le trouver ...la source
Une question amusante, mais une fois que vous vous rendez compte que le modèle réel entre deux '1 n'a pas d'importance, l'algorithme devient:
Dans le code, mode JTest, (notez que ce code n'est pas écrit pour être le plus efficace et j'ai ajouté quelques println pour voir ce qui se passe.)
la source
J'ai pensé à une approche de division et de conquête qui pourrait fonctionner.
Tout d'abord, lors du prétraitement, vous devez insérer tous les nombres inférieurs à la moitié de votre taille d'entrée ( n / 3) dans une liste.
Étant donné une chaîne:
0000010101000100
(notez que cet exemple particulier est valide)Insérez tous les nombres premiers (et 1) de 1 à (16/2) dans une liste: {1, 2, 3, 4, 5, 6, 7}
Puis divisez-le en deux:
100000101 01000100
Continuez à faire cela jusqu'à ce que vous arriviez à des chaînes de taille 1. Pour toutes les chaînes de taille un avec un 1, ajoutez l'index de la chaîne à la liste des possibilités; sinon, retournez -1 en cas d'échec.
Vous devrez également renvoyer une liste des distances d'espacement encore possibles, associées à chaque index de départ. (Commencez par la liste que vous avez faite ci-dessus et supprimez les nombres au fur et à mesure) Ici, une liste vide signifie que vous n'avez affaire qu'à un 1 et que tout espacement est donc possible à ce stade; sinon, la liste comprend des espacements qui doivent être exclus.
Continuez donc avec l'exemple ci-dessus:
1000 0101 0100 0100
10 00 01 01 01 00 01 00
1 0 0 0 0 1 0 1 0 1 0 0 0 1 0 0
Dans la première étape de combinaison, nous avons maintenant huit séries de deux. Dans le premier, on a la possibilité d'un ensemble, mais on apprend que l'espacement de 1 est impossible à cause de la présence de l'autre zéro. Nous retournons donc 0 (pour l'index) et {2,3,4,5,7} pour le fait que l'espacement de 1 est impossible. Dans le second, nous n'avons rien et retournons donc -1. Dans le troisième, nous avons une correspondance sans espacement éliminé dans l'index 5, donc renvoyez 5, {1,2,3,4,5,7}. Dans la quatrième paire, nous retournons 7, {1,2,3,4,5,7}. Dans le cinquième, renvoyez 9, {1,2,3,4,5,7}. Dans le sixième, retournez -1. Dans le septième, retournez 13, {1,2,3,4,5,7}. Au huitième, retournez -1.
En combinant à nouveau en quatre séries de quatre, nous avons:
1000
: Retour (0, {4,5,6,7})0101
: Retour (5, {2,3,4,5,6,7}), (7, {1,2,3,4,5,6 , 7})0100
: Retour (9, {3,4,5,6,7})0100
: Retour (13, {3,4,5,6,7})Combinaison en ensembles de huit:
10000101
: Retour (0, {5,7}), (5, {2,3,4,5,6,7}), (7, {1,2,3,4,5,6,7})01000100
: Retour (9, {4,7}), (13, {3,4,5,6,7})Combinant en un ensemble de seize:
10000101 01000100
Au fur et à mesure que nous progressons, nous continuons à vérifier toutes les possibilités jusqu'à présent. Jusqu'à cette étape, nous avons laissé des éléments qui dépassaient la fin de la chaîne, mais nous pouvons maintenant vérifier toutes les possibilités.
Fondamentalement, nous vérifions le premier 1 avec des espacements de 5 et 7, et constatons qu'ils ne s'alignent pas sur les 1. (Notez que chaque vérification est CONSTANTE et non linéaire) Ensuite, nous vérifions la seconde (index 5) avec des espacements de 2, 3, 4, 5, 6 et 7 - ou nous le ferions, mais nous pouvons nous arrêter à 2 puisque qui correspond en fait.
Phew! C'est un algorithme assez long.
Je ne sais pas à 100% si c'est O (n log n) à cause de la dernière étape, mais tout jusqu'à là est définitivement O (n log n) pour autant que je sache. J'y reviendrai plus tard et j'essaierai d'affiner la dernière étape.
EDIT: J'ai changé ma réponse pour refléter le commentaire de Welbog. Désolé pour l'erreur. J'écrirai aussi un pseudocode plus tard, quand j'aurai un peu plus de temps pour déchiffrer ce que j'ai écrit à nouveau. ;-)
la source
100010001
? Si je comprends bien votre approche, elle ne pourra pas la faire correspondre car la bonne réponse(0,{4})
n'est pas possible à calculer. Étant donné que vous avez besoin de non-nombres premiers dans votre liste, il est facile de trouver des chaînes pathologiques qui gonflent les listes de possibilités que vous devez vérifier au-dessus de O (n log (n)), je pense.Je vais vous donner une estimation approximative ici et laisser ceux qui sont meilleurs avec le calcul de la complexité pour m'aider sur la façon dont mon algorithme se comporte en notation O
Je n'ai aucune idée de comment calculer la complexité pour cela, quelqu'un peut-il m'aider?
edit: ajoutez du code pour illustrer mon idée
edit2: j'ai essayé de compiler mon code et j'ai trouvé des erreurs majeures, corrigé
la source
J'ai trouvé quelque chose comme ça:
Ceci est inspiré par andycjw.
Quant à la complexité, cela pourrait être O (nlogn) car dans chaque récursion, nous divisons par deux.
J'espère que ça aide.
la source
Ok, je vais tenter un autre coup au problème. Je pense que je peux prouver un algorithme O (n log (n)) similaire à ceux déjà discutés en utilisant un arbre binaire équilibré pour stocker les distances entre les 1. Cette approche a été inspirée par l'observation de Justice sur la réduction du problème à une liste de distances entre les 1.
Pourrions-nous balayer la chaîne d'entrée pour construire un arbre binaire équilibré autour de la position des 1 de sorte que chaque nœud stocke la position du 1 et que chaque bord soit étiqueté avec la distance au 1 adjacent pour chaque nœud enfant. Par exemple:
Cela peut être fait en O (n log (n)) puisque, pour une chaîne de taille n, chaque insertion prend O (log (n)) dans le pire des cas.
Ensuite, le problème est de rechercher dans l'arborescence pour découvrir si, à n'importe quel nœud, il existe un chemin de ce nœud à l'enfant gauche qui a la même distance qu'un chemin à travers l'enfant droit. Cela peut être fait de manière récursive sur chaque sous-arbre. Lors de la fusion de deux sous-arbres dans la recherche, nous devons comparer les distances des chemins dans le sous-arbre de gauche avec les distances des chemins dans la droite. Étant donné que le nombre de chemins dans un sous-arbre sera proportionnel à log (n) et que le nombre de nœuds est n, je pense que cela peut être fait en temps O (n log (n)).
Ai-je manqué quelque chose?
la source
Cela semblait être un problème amusant, alors j'ai décidé de m'essayer.
Je suppose que 111000001 trouverait les 3 premiers et réussirait. Essentiellement, le nombre de zéros suivant le 1 est la chose importante, puisque 0111000 équivaut à 111000 selon votre définition. Une fois que vous avez trouvé deux cas de 1, le suivant 1 trouvé complète la trilogie.
Le voici en Python:
C'est un premier essai, donc je suis sûr que cela pourrait être écrit de manière plus propre. Veuillez énumérer les cas où cette méthode échoue ci-dessous.
la source
Je suppose que la raison pour laquelle il s'agit de nlog (n) est due à ce qui suit:
Donc, vous avez n, log (n) et 1 ... O (nlogn)
Edit: Oups, mon mal. Mon cerveau avait établi que n / 2 était connecté ... ce qui n'est évidemment pas le cas (doubler le nombre d'éléments double toujours le nombre d'itérations sur la boucle interne). Ceci est toujours à n ^ 2, ne résolvant pas le problème. Au moins, je dois écrire du code :)
Implémentation en Tcl
la source
Je pense avoir trouvé un moyen de résoudre le problème, mais je ne peux pas construire une preuve formelle. La solution que j'ai faite est écrite en Java et utilise un compteur «n» pour compter le nombre d'accès aux listes / tableaux. Ainsi, n doit être inférieur ou égal à stringLength * log (stringLength) s'il est correct. Je l'ai essayé pour les nombres 0 à 2 ^ 22, et ça marche.
Il commence par itérer sur la chaîne d'entrée et en faisant une liste de tous les index qui en contiennent un. Ceci est juste O (n).
Ensuite, dans la liste des index, il choisit un firstIndex et un secondIndex qui est supérieur au premier. Ces deux index doivent en contenir, car ils sont dans la liste des index. De là, le troisièmeIndex peut être calculé. Si inputString [thirdIndex] est un 1, alors il s'arrête.
}
note supplémentaire: le compteur n n'est pas incrémenté lorsqu'il itère sur la chaîne d'entrée pour construire la liste des index. Cette opération est O (n), donc elle n'aura pas d'effet sur la complexité de l'algorithme de toute façon.
la source
O(n^2)
algorithme.Une des incursions dans le problème consiste à penser aux facteurs et aux changements.
Avec le décalage, vous comparez la chaîne de uns et de zéros avec une version décalée de lui-même. Vous prenez ensuite ceux qui correspondent. Prenons cet exemple décalé de deux:
Les 1 résultants (AND au niveau du bit), doivent représenter tous ces 1 qui sont régulièrement espacés de deux. Le même exemple décalé de trois:
Dans ce cas, il n'y a pas de 1 qui sont régulièrement espacés de trois.
Alors qu'est-ce que cela vous dit? Eh bien, il vous suffit de tester les décalages qui sont des nombres premiers. Par exemple, disons que vous avez deux 1 séparés de six. Il vous suffirait de tester «deux» équipes et «trois» équipes (puisque celles-ci divisent six). Par exemple:
Ainsi, les seuls décalages que vous ayez jamais besoin de vérifier sont 2,3,5,7,11,13 etc. Jusqu'au premier plus proche de la racine carrée de la taille de la chaîne de chiffres.
Presque résolu?
Je pense que je suis plus proche d'une solution. Fondamentalement:
Je pense que le plus gros indice de la réponse est que les algorithmes de tri les plus rapides sont O (n * log (n)).
FAUX
L'étape 1 est fausse comme l'a souligné un collègue. Si nous avons des 1 aux positions 2,12 et 102. Alors en prenant un module de 10, ils auraient tous les mêmes restes, et pourtant ne sont pas également espacés! Désolé.
la source
Voici quelques réflexions qui, malgré tous mes efforts, ne sembleront pas s'envelopper dans un arc. Pourtant, ils pourraient être un point de départ utile pour l'analyse de quelqu'un.
Considérez la solution proposée comme suit, qui est l'approche que plusieurs personnes ont suggérée, y compris moi-même dans une version précédente de cette réponse.
:)
Considérez maintenant les chaînes de chaînes d'entrée comme les suivantes, qui n'auront pas de solution:
En général, il s'agit de la concaténation de k chaînes de la forme j 0 suivies d'un 1 pour j de zéro à k-1.
Notez que les longueurs des sous-chaînes sont 1, 2, 3, etc. Ainsi, la taille du problème n a des sous-chaînes de longueurs 1 à k telles que n = k (k + 1) / 2.
Notez que k suit également le nombre de 1 que nous devons considérer. N'oubliez pas que chaque fois que nous voyons un 1, nous devons considérer tous les 1 vus jusqu'à présent. Ainsi, lorsque nous voyons le deuxième 1, nous ne considérons que le premier, lorsque nous voyons le troisième 1, nous reconsidérons les deux premiers, lorsque nous voyons le quatrième 1, nous devons reconsidérer les trois premiers, et ainsi de suite. À la fin de l'algorithme, nous avons considéré k (k-1) / 2 paires de 1. Appelez ça p.
La relation entre n et p est que n = p + k.
Le processus pour parcourir la chaîne prend un temps O (n). Chaque fois qu'un 1 est rencontré, un maximum de (k-1) comparaisons sont effectuées. Puisque n = k (k + 1) / 2, n> k ** 2, donc sqrt (n)> k. Cela nous donne O (n sqrt (n)) ou O (n ** 3/2). Notez cependant que ce n'est peut-être pas une limite très serrée, car le nombre de comparaisons va de 1 à un maximum de k, ce n'est pas k tout le temps. Mais je ne sais pas comment expliquer cela en mathématiques.
Ce n'est toujours pas O (n log (n)). De plus, je ne peux pas prouver que ces entrées sont les pires cas, même si je soupçonne qu'elles le sont. Je pense qu'un emballage plus dense de 1 à l'avant entraîne un emballage encore plus clairsemé à la fin.
Puisque quelqu'un peut encore le trouver utile, voici mon code pour cette solution en Perl:
la source
Lors de la numérisation des 1, ajoutez leurs positions à une liste. Lors de l'ajout du deuxième 1 et des 1 successifs, comparez-les à chaque position de la liste jusqu'à présent. L'espacement est égal à currentOne (centre) - previousOne (à gauche). Le bit de droite est currentOne + espacement. Si c'est 1, la fin.
La liste de ceux-ci augmente inversement avec l'espace entre eux. En termes simples, si vous avez beaucoup de 0 entre les 1 (comme dans le pire des cas), votre liste de 1 connus augmentera assez lentement.
la source
J'ai pensé ajouter un commentaire avant de publier la 22e solution naïve au problème. Pour la solution naïve, nous n'avons pas besoin de montrer que le nombre de 1 dans la chaîne est au plus O (log (n)), mais plutôt qu'il est au plus O (sqrt (n * log (n)).
Solveur:
C'est fondamentalement un peu similaire à l'idée et à la mise en œuvre de flybywire, mais en regardant vers l'avant plutôt que vers l'arrière.
Générateur de cordes gourmandes:
(Pour ma défense, je suis toujours au stade de compréhension `` apprendre python '')
De plus, sortie potentiellement utile de la construction gourmande de cordes, il y a un saut assez cohérent après avoir atteint une puissance de 2 dans le nombre de 1 ... ce que je n'étais pas prêt à attendre pour assister à 2096.
la source
Je vais essayer de présenter une approche mathématique. C'est plus un début qu'une fin, donc toute aide, commentaire ou même contradiction - sera profondément apprécié. Cependant, si cette approche est prouvée, l'algorithme est une recherche directe dans la chaîne.
Étant donné un nombre fixe d'espaces
k
et une chaîneS
, la recherche d'un triplet d'espacement k prendO(n)
- Nous testons simplement pour chaque0<=i<=(n-2k)
siS[i]==S[i+k]==S[i+2k]
. Le test prendO(1)
et nous le faisonsn-k
fois oùk
est une constante, donc il fautO(n-k)=O(n)
.Supposons qu'il existe une proportion inverse entre le nombre de
1
's et le maximum d'espaces que nous devons rechercher. Autrement dit, s'il y en a beaucoup1
, il doit y avoir un triplet et il doit être assez dense; S'il n'y en a que peu1
, le triplet (le cas échéant) peut être assez clairsemé. En d'autres termes, je peux prouver que si j'en ai assez1
, un tel triplet doit exister - et plus1
j'en ai, un triplet plus dense doit être trouvé. Cela peut être expliqué par le principe de Pigeonhole - J'espère développer cela plus tard.Disons avoir une limite supérieure
k
sur le nombre possible d'espaces que je dois rechercher. Maintenant, pour chaque1
situé dansS[i]
nous devons vérifier1
dansS[i-1]
etS[i+1]
,S[i-2]
etS[i+2]
, ...S[i-k]
etS[i+k]
. Cela prendO((k^2-k)/2)=O(k^2)
pour chaque1
enS
- en raison de Gauss Series Formula Summation . Notez que cela diffère de la section 1 - j'aik
comme limite supérieure pour le nombre d'espaces, pas comme un espace constant.Nous devons prouver
O(n*log(n))
. Autrement dit, nous devons montrer quek*(number of 1's)
c'est proportionnel àlog(n)
.Si nous pouvons faire cela, l'algorithme est trivial - pour chacun
1
dansS
dont l'index esti
, cherchez simplement1
de chaque côté jusqu'à la distancek
. Si deux ont été trouvés à la même distance, retournezi
etk
. Encore une fois, la partie délicate serait de trouverk
et de prouver l'exactitude.J'apprécierais vraiment vos commentaires ici - j'ai essayé de trouver la relation entre
k
et le nombre de1
's sur mon tableau blanc, jusqu'à présent sans succès.la source
Supposition:
Tout simplement faux, parler du nombre log (n) de la limite supérieure de uns
ÉDITER:
Maintenant, j'ai trouvé qu'en utilisant les nombres de Cantor (si corrects), la densité sur le plateau est (2/3) ^ Log_3 (n) (quelle fonction étrange) et je suis d'accord, la densité log (n) / n est trop forte.
S'il s'agit d'une limite supérieure, il existe un algorithme qui résout ce problème en au moins O (n * (3/2) ^ (log (n) / log (3))) complexité de temps et O ((3/2) ^ ( complexité de l'espace log (n) / log (3))). (Vérifiez la réponse de Justice pour l'algorhitm)
C'est toujours de loin meilleur que O (n ^ 2)
Cette fonction ((3/2) ^ (log (n) / log (3))) ressemble vraiment à n * log (n) à première vue.
Comment ai-je obtenu cette formule?
Appliquant le nombre de Cantors sur la chaîne.
Supposons que la longueur de la chaîne est de 3 ^ p == n
A chaque étape de la génération de la chaîne Cantor, vous gardez 2/3 du nombre précédent de chaînes. Appliquez ce p fois.
Cela signifie (n * ((2/3) ^ p)) -> (((3 ^ p)) * ((2/3) ^ p)) restants et après simplification 2 ^ p. Cela signifie 2 ^ p unités dans 3 ^ p chaîne -> (3/2) ^ p unités. Remplacez p = log (n) / log (3) et obtenez
((3/2) ^ (log (n) / log (3)))
la source
Que diriez-vous d'une simple solution O (n), avec un espace O (n ^ 2)? (Utilise l'hypothèse que tous les opérateurs au niveau du bit fonctionnent dans O (1).)
L'algorithme fonctionne essentiellement en quatre étapes:
Étape 1: Pour chaque bit de votre numéro d'origine, déterminez à quelle distance se trouvent ceux-ci, mais ne considérez qu'une seule direction. (J'ai considéré tous les bits dans le sens du bit le moins significatif.)
Étape 2: Inversez l'ordre des bits dans l'entrée;
Étape 3: Relancez l'étape 1 sur l'entrée inversée.
Étape 4: Comparez les résultats des étapes 1 et 3. Si des bits sont également espacés au-dessus ET au-dessous, nous devons avoir un hit.
Gardez à l'esprit qu'aucune étape de l'algorithme ci-dessus ne prend plus de O (n). ^ _ ^
Comme avantage supplémentaire, cet algorithme trouvera TOUS les numéros également espacés de CHAQUE numéro. Ainsi, par exemple, si vous obtenez un résultat de "0x0005", il y en a des espacés de manière égale à 1 et 3 unités.
Je n'ai pas vraiment essayé d'optimiser le code ci-dessous, mais c'est du code C # compilable qui semble fonctionner.
Quelqu'un dira probablement que pour un nombre suffisamment grand, les opérations au niveau du bit ne peuvent pas être effectuées dans O (1). Vous auriez raison. Cependant, je suppose que chaque solution qui utilise l'addition, la soustraction, la multiplication ou la division (ce qui ne peut pas être fait par décalage) aurait également ce problème.
la source
Voici une solution. Il peut y avoir quelques petites erreurs ici et là, mais l'idée est bonne.
Edit: Ce n'est pas n * log (n)
CODE PSEUDO:
Code C #:
Comment ça fonctionne:
la source
De toute évidence, nous devons au moins vérifier des groupes de triplés en même temps, nous devons donc compresser les contrôles d'une manière ou d'une autre. J'ai un algorithme candidat, mais l'analyse de la complexité temporelle dépasse mon seuil de capacité * temps.
Construisez une arborescence où chaque nœud a trois enfants et chaque nœud contient le nombre total de 1 à ses feuilles. Créez également une liste chaînée sur les 1. Attribuez à chaque nœud un coût autorisé proportionnel à la plage qu'il couvre. Tant que le temps que nous passons à chaque nœud est dans les limites du budget, nous aurons un algorithme O (n lg n).
-
Commencez à la racine. Si le carré du nombre total de 1 en dessous est inférieur à son coût autorisé, appliquez l'algorithme naïf. Sinon, récurer ses enfants.
Maintenant, soit nous sommes revenus dans les limites du budget, soit nous savons qu'il n'y a pas de triplés valides entièrement contenus dans l'un des enfants. Il faut donc vérifier les triplets inter-nœuds.
Maintenant, les choses deviennent incroyablement compliquées. Nous voulons essentiellement revenir sur les groupes potentiels d'enfants tout en limitant la gamme. Dès que la plage est suffisamment limitée pour que l'algorithme naïf s'exécute sous le budget, vous le faites. Profitez de la mise en œuvre de cela, car je vous garantis que ce sera fastidieux. Il y a comme une douzaine de cas.
-
La raison pour laquelle je pense que cet algorithme fonctionnera est que les séquences sans triplets valides semblent alterner entre des paquets de 1 et beaucoup de 0. Il divise efficacement l'espace de recherche à proximité et l'arborescence émule ce fractionnement.
Le temps d'exécution de l'algorithme n'est pas du tout évident. Il repose sur les propriétés non triviales de la séquence. Si les 1 sont vraiment rares, l'algorithme naïf fonctionnera sous le budget. Si les 1 sont denses, une correspondance doit être trouvée immédiatement. Mais si la densité est «juste bonne» (par exemple près de ~ n ^ 0,63, ce que vous pouvez obtenir en définissant tous les bits à des positions sans chiffre «2» en base 3), je ne sais pas si cela fonctionnera. Il faudrait prouver que l'effet de division est suffisamment fort.
la source
Aucune réponse théorique ici, mais j'ai écrit un programme Java rapide pour explorer le comportement au moment de l'exécution en fonction de k et n, où n est la longueur totale en bits et k le nombre de 1. Je suis avec quelques-uns des répondants qui disent que l'algorithme "régulier" qui vérifie toutes les paires de positions de bits et recherche le 3ème bit, même s'il nécessiterait O (k ^ 2) dans le pire des cas, en réalité parce que le pire des cas a besoin de chaînes binaires éparses, est O (n ln n).
Bref, voici le programme ci-dessous. C'est un programme de style Monte-Carlo qui exécute un grand nombre d'essais NTRIALS pour la constante n, et génère aléatoirement des ensembles de bits pour une plage de valeurs k en utilisant des processus de Bernoulli avec une densité de un contrainte entre des limites pouvant être spécifiées, et enregistre le temps d'exécution de trouver ou de ne pas trouver un triplet de triples espacés uniformément, temps mesuré en pas PAS en temps CPU. Je l'ai couru pendant n = 64, 256, 1024, 4096, 16384 * (toujours en cours d'exécution), d'abord un test avec 500000 essais pour voir quelles valeurs k prennent le temps de fonctionnement le plus long, puis un autre test avec 5000000 essais avec des essais réduits- focus sur la densité pour voir à quoi ressemblent ces valeurs. Les temps de fonctionnement les plus longs se produisent avec une densité très faible (par exemple pour n = 4096, les pics de temps de fonctionnement sont dans la plage k = 16-64, avec un pic doux pour le temps de fonctionnement moyen à 4212 étapes @ k = 31, la durée de fonctionnement maximale a culminé à 5101 étapes @ k = 58). Il semble qu'il faudrait des valeurs extrêmement élevées de N pour que le pas O (k ^ 2) du cas le plus défavorable devienne plus grand que le pas O (n) où vous parcourez la chaîne de bits pour trouver les indices de position de 1.
la source
J'ai des problèmes avec les pires scénarios avec des millions de chiffres. Fuzzing
/dev/urandom
vous donne essentiellement O (n), mais je sais que le pire des cas est pire que cela. Je ne peux pas dire à quel point c'est pire. Pour les petitsn
, il est trivial de trouver des intrants aux alentours3*n*log(n)
, mais il est étonnamment difficile de les différencier d'un autre ordre de croissance pour ce problème particulier.Est-ce que quelqu'un qui travaillait sur des entrées du pire des cas peut générer une chaîne avec une longueur supérieure, disons, cent mille?
la source
Une adaptation de l'algorithme de Rabin-Karp pourrait vous être possible. Sa complexité est de 0 (n) donc cela pourrait vous aider.
Jetez un œil à http://en.wikipedia.org/wiki/Rabin-Karp_string_search_algorithm
la source
Serait-ce une solution? Je ne sais pas si c'est O (nlogn) mais à mon avis c'est mieux que O (n²) car le seul moyen de ne pas trouver un triplet serait une distribution de nombres premiers.
Il y a place à amélioration, le deuxième trouvé 1 pourrait être le suivant premier 1. Aussi pas de vérification d'erreur.
la source
Je pense que cet algorithme a une complexité O (n log n) (C ++, DevStudio 2k5). Maintenant, je ne connais pas les détails sur la façon d'analyser un algorithme pour déterminer sa complexité, j'ai donc ajouté des informations de collecte de métriques au code. Le code compte le nombre de tests effectués sur la séquence de 1 et de 0 pour une entrée donnée (j'espère que je n'ai pas fait une boule de l'algorithme). Nous pouvons comparer le nombre réel de tests à la valeur O et voir s'il existe une corrélation.
Ce programme génère le nombre de tests pour chaque longueur de chaîne jusqu'à 32 caractères. Voici les résultats:
J'ai également ajouté les valeurs «n log n». Tracez-les à l'aide de l'outil graphique de votre choix pour voir une corrélation entre les deux résultats. Cette analyse s'étend-elle à toutes les valeurs de n? Je ne sais pas.
la source