Est-ce techniquement un algorithme O (1) pour «Hello World»?

117

Serait-ce classé comme un algorithme O (1) pour "Hello, World!" ??

public class Hello1
{
   public static void Main()
   {
      DateTime TwentyYearsLater = new DateTime(2035,01,01);
      while ( DateTime.Now < TwentyYearsLater )
      { 
          System.Console.WriteLine("It's still not time to print the hello ...");
      }
      System.Console.WriteLine("Hello, World!");
   }
}

Je pense utiliser le

DateTime TwentyYearsLater = new DateTime(2035,01,01);
while ( DateTime.Now < TwentyYearsLater )
{ 
   // ... 
}

extrait de code comme une boucle occupée à mettre en blague chaque fois que quelqu'un demande un algorithme d'une certaine complexité. Serait-ce correct?

Développement Web Subpar
la source
15
Ce n'est O(N)pas la complexitéO(1)
Fabjan
19
@SubparWebDev Non, vous ne savez pas combien de fois il passera à travers la boucle, même si vous connaissez la différence de temps exacte entre le moment où vous démarrez le programme et la date spécifiée. Cela dépend de la vitesse de fonctionnement de l'ordinateur, de ce qui s'exécute dessus, de la façon dont le processeur planifie la tâche, etc.
Servy
131
@Fabjan Il n'y a pas de Ndépendance de l'algorithme, donc vous ne pouvez pas dire que c'est un algorithme O (N).
Servy
29
Techniquement, il n'y a pas d'entrée, donc Ncela n'a même pas de sens. Mais vous pouvez envisager DateTime.Nowune entrée qui rend cela encore dépendant du résultat. Si vous pouvez supposer une valeur réaliste pour DateTime.Now, alors oui, le programme boucle un nombre constant de fois.
poke
43
L'énoncé du problème doit définir ce qu'est N.
Yacoub Massad

Réponses:

406

La notation Big O dans ce contexte est utilisée pour décrire une relation entre la taille de l'entrée d'une fonction et le nombre d'opérations qui doivent être effectuées pour calculer le résultat pour cette entrée.

Votre opération n'a pas d'entrée à laquelle la sortie peut être liée, donc l'utilisation de la notation Big O n'a pas de sens. Le temps que prend l'opération est indépendant des entrées de l'opération (qui est ... aucune). Comme il n'y a pas de relation entre l'entrée et le nombre d'opérations effectuées, vous ne pouvez pas utiliser Big O pour décrire cette relation inexistante

Servy
la source
6
Et quoi O(max(1, 2035 - yearTheProgramIsStarted))?
Bergi
19
@Bergi [En fait, non [( stackoverflow.com/questions/34048740/… ), vous ne pouvez pas simplement décrire le nombre d'itérations de la boucle uniquement en fonction de l'heure à laquelle vous exécutez le travail. Et bien sûr, vous combinez cela avec le fait que l'horloge système peut être modifiée à tout moment par l'utilisateur à l'heure de son choix, etc. et vous n'avez toujours pas une entrée bien formée pouvant être liée avec précision à un certain nombre de opérations nécessaires pour produire la sortie. Heck, même la sortie elle-même n'est pas cohérente.
Servy
23
On pourrait soutenir que l'état du système (qui comprend l'horloge) fait partie de l'entrée du programme. En ce sens, vous pouvez utiliser la date comme paramètre d'entrée, bien que non explicite. C'est étrange, cependant.
Connor Clark
9
Pour être plus clair, l'entrée «implicite» est le delta entre le 1er janvier 2035 et aujourd'hui.
Connor Clark
6
@Hoten Mais l'heure système n'est pas une valeur fixe. Cette fonction n'est pas la même chose que d'accepter simplement une DateTimeheure de début comme entrée. Comme je l'ai dit plus tôt, l'horloge système peut changer avec le temps . Et encore une fois, vous ne pouvez pas mapper directement l'entrée quazi que vous décrivez à une sortie fixe. Il n'y a pas un nombre connu d'opérations effectuées pour une heure de début donnée, ni même pour deux programmes qui obtiennent toujours une valeur raisonnable de DateTime.Now, donc vous ne pouvez pas relier les deux à mesure que l'heure change, car vous ne pouvez même pas les relier lorsque le temps ne change pas .
Servy
88

La notation Big-O signifie à peu près «étant donné une opération sur une quantité de travail, N, combien de temps de calcul, proportionnel à N, prend l'algorithme?». Par exemple, le tri d'un tableau de taille N peut prendre N ^ 2, Nlog (N), etc.

Cela n'a aucune quantité de données d'entrée sur lesquelles agir. Donc ce n'est pas le cas O(anything).

Encore pire; ce n'est pas techniquement un algorithme. Un algorithme est une méthode de calcul de la valeur d'une fonction mathématique - les fonctions mathématiques sont un mappage d'une entrée à une sortie. Comme cela ne prend aucune entrée et ne renvoie rien, ce n'est pas une fonction, au sens mathématique. De wikipedia:

Un algorithme est une méthode efficace qui peut être exprimée dans une quantité finie d'espace et de temps et dans un langage formel bien défini pour le calcul d'une fonction. Partant d'un état initial et d'une entrée initiale (peut-être vide), les instructions décrivent un calcul qui, lorsqu'il est exécuté, passe par un nombre fini d'états successifs bien définis, produisant finalement une "sortie" et se terminant à un état final final.

Ce que c'est, techniquement, c'est un système de contrôle. De wikipedia;

Un système de contrôle est un appareil, ou un ensemble d'appareils, qui gère, commande, dirige ou régule le comportement d'autres appareils ou systèmes.

Pour les personnes souhaitant une réponse plus approfondie sur la différence entre les fonctions mathématiques et les algorithmes, et les capacités plus puissantes des ordinateurs à effectuer des opérations secondaires telles que la sortie de la console, l'affichage de graphiques ou le contrôle de robots, lisez cet article sur le Hypothèse forte de Church-Turing

Abstrait

La vision classique du calcul positionne le calcul comme une transformation en boîte fermée d'entrées (nombres rationnels ou chaînes finies) en sorties. Selon la vision interactive de l'informatique, le calcul est un processus interactif continu plutôt qu'une transformation basée sur la fonction d'une entrée en une sortie. Plus précisément, la communication avec le monde extérieur se produit pendant le calcul, pas avant ou après. Cette approche change radicalement notre compréhension de ce qu'est le calcul et de sa modélisation.

L'acceptation de l'interaction en tant que nouveau paradigme est entravée par la Strong Church-Turing Thesis (SCT), la croyance répandue que les machines de Turing (TM) capturent tous les calculs, de sorte que les modèles de calcul plus expressifs que les TM sont impossibles. Dans cet article, nous montrons que SCT réinterprète la thèse originale de Church-Turing (CTT) d'une manière que Turing n'a jamais voulue; son équivalence communément supposée à l'original est un mythe. Nous identifions et analysons les raisons historiques de la croyance largement répandue en SCT. Ce n'est qu'en acceptant que c'est faux que nous pouvons commencer à adopter l'interaction comme paradigme alternatif du calcul

Steve Cooper
la source
Cela n'a pas besoin d'être une séquence. Il ne s'agit que d'une entrée de données, et la notation landau décrit le temps d'exécution par rapport à certaines métriques sur ces données - généralement quelque chose lié à la taille.
Bergi
@Bergi - ouais, voyez votre point! Il suffit de faire une approximation, vraiment, mais oui - si vous pouvez mesurer la quantité de travail à faire et le nombre d'étapes nécessaires pour y parvenir, big-o reflète la relation entre ces deux mesures. Plus proche?
Steve Cooper
@kapep - ce n'est pas une fonction pure car c'est une méthode void, mais si nous comptons la sortie de la console, c'est toujours aléatoire; il pourrait afficher n'importe quel {"Hello, World!", "Il n'est toujours pas temps d'imprimer le bonjour ... \ nHello, World!", "Il n'est toujours pas temps d'imprimer le bonjour ... Il n'est toujours pas temps d'imprimer le bonjour ... \ nBonjour, Monde! ", ...}
Steve Cooper
1
L'impression vers stdout n'est pas une sortie?
rpax
4
@rpax Pas mathématiquement, non. Une fonction est une traduction inchangée des entrées aux sorties; Par exemple, 'square' est la fonction qui renvoie toujours 9 si vous entrez 3. Une méthode c # n'est qu'une fonction mathématique si un appel avec les mêmes paramètres donne toujours la même valeur de retour. Sinon - s'il a des effets secondaires comme l'écriture sur la console, l'affichage de graphiques, l'allocation de mémoire - ce ne sont pas des fonctions mathématiques. (Je vais ajouter un lien vers ma réponse qui y entre dans des détails atroces :))
Steve Cooper
41

Non, votre code a une complexité temporelle de O(2^|<DeltaTime>|),

Pour un codage correct de l'heure actuelle.
S'il vous plaît, laissez-moi d'abord m'excuser pour mon anglais.

Qu'est-ce que et comment Big O fonctionne dans CS

La notation Big O n'est pas utilisée pour lier l'entrée d'un programme à son temps d'exécution .
La notation Big O est, en laissant la rigueur derrière elle, un moyen d'exprimer le rapport asymptotique de deux quantités .

Dans le cas de l'analyse d'algorithme, ces deux grandeurs sont ne pas l'entrée (pour laquelle il faut d'abord avoir une fonction "mesure") et le temps d'exécution.
Ce sont la longueur du codage d'une instance du problème 1 et une métrique d'intérêt.

Les métriques couramment utilisées sont

  1. Le nombre d'étapes nécessaires pour compléter l'algorithme dans un modèle de calcul donné.
  2. L'espace requis, s'il existe un tel concept, par le modèle de calcul.

On suppose implicitement un TM comme modèle de sorte que le premier point se traduit par le nombre d'applications de la transition 2 fonction de , c'est-à-dire «étapes», et le second traduit le nombre de cellules de bande différentes écrites au moins une fois .

Est-il aussi souvent supposé implicitement que nous pouvons utiliser un codage polynomialement lié au lieu de l'original, par exemple une fonction qui recherche un tableau du début à la fin est O(n)complexe malgré le fait qu'un codage d'une instance d'un tel tableau devrait avoir une longueur de n*b+(n-1)b est le nombre (constant) de symboles de chaque élément. En effet, il best considéré comme une constante du modèle de calcul et donc l'expression ci-dessus et nsont asymptotiquement identiques.

Cela explique également pourquoi un algorithme comme la Division de première instance est un algorithme exponentiel bien qu'il soit essentiellement un for(i=2; i<=sqr(N); i++)algorithme similaire 3 .

Voir ça .

Cela signifie également que la grande notation O peut utiliser autant de paramètres dont on peut avoir besoin pour décrire le problème, n'est-il pas inhabituel d'avoir un k paramètre pour certains algorithmes.

Donc ce n'est pas de "l'entrée" ou de "qu'il n'y a pas d'entrée".

Étude de cas maintenant

La notation Big O ne remet pas en question votre algorithme, elle suppose simplement que vous savez ce que vous faites. C'est essentiellement un outil applicable partout, même à un algorithme qui peut être délibérément délicat (comme le vôtre).

Pour résoudre votre problème, vous avez utilisé la date actuelle et une date future, elles doivent donc faire partie du problème d'une manière ou d'une autre; en termes simples: ils font partie de l'instance du problème.

Plus précisément, l'instance est:

<DeltaTime>

Où le <>signifie un codage de choix, non pathologique.

Voir ci-dessous pour des clarifications très importantes .

Donc, votre grand temps de complexité O est juste O(2^|<DeltaTime>|), car vous effectuez un certain nombre d'itérations qui dépendent de la valeur du temps actuel. Il ne sert à rien de mettre d'autres constantes numériques car la notation asymptotique est utile car elle élimine les constantes (donc par exemple l'utilisation deO(10^|<DeltaTime>|*any_time_unit) est inutile).

Où est la partie délicate

Nous avons fait une hypothèse importante ci-dessus: que le modèle de calcul réifie 5 temps, et par temps j'entends le temps physique (réel?). Un tel concept n'existe pas dans le modèle de calcul standard, une MT ne connaît pas le temps, nous associons le temps au nombre d'étapes car c'est ainsi que fonctionne notre réalité 4 .

Dans votre modèle, cependant, le temps fait partie du calcul, vous pouvez utiliser la terminologie des personnes fonctionnelles en disant que Main n'est pas pur mais que le concept est le même.

Pour comprendre cela, il faut noter que rien n'empêche le Framework d'utiliser un faux temps qui s'exécute deux, cinq, dix fois plus vite que le temps physique. De cette façon, votre code fonctionnera dans «la moitié», «un cinquième», «un dixième» du «temps».

Cette réflexion est importante pour le choix du codage de <DeltaTime>, c'est essentiellement une manière condensée d'écrire <(CurrentTime, TimeInFuture)>. Puisque le temps n'existe pas au prieuré, le codage de CurrentTime pourrait très bien être le mot Now (ou tout autre choix) la veille pourrait être codé comme Hier , là en cassant l'hypothèse que la longueur du codage augmente avec le temps physique avance (et celui de DeltaTime diminue)

Nous devons modéliser correctement le temps dans notre modèle de calcul afin de faire quelque chose d'utile.

Le seul choix sûr que nous pouvons faire est d'encoder des horodatages avec des longueurs croissantes (mais toujours sans utiliser unaire) au fur et à mesure que le temps physique avance. C'est la seule vraie propriété du temps dont nous avons besoin et celle dont l'encodage a besoin pour capturer. Ce n'est qu'avec ce type d'encodage que votre algorithme peut avoir une complexité temporelle.

Votre confusion, le cas échéant, provient du fait que le mot temps dans les phrases «Quelle est sa complexité temporelle ? et "Combien de temps cela prendra-t-il?" signifie des choses très très différentes

Hélas, la terminologie utilise les mêmes mots, mais vous pouvez essayer d'utiliser "étapes de complexité" dans votre tête et vous poser à nouveau votre question, j'espère que cela vous aidera à comprendre que la réponse est vraiment ^ _ ^


1 Cela explique aussi la nécessité d'une approche asymptotique que chaque instance a un autre, mais pas arbitraire, longueur.
2 J'espère que j'utilise le terme anglais correct ici.
C'est aussi pourquoi nous trouvons souvent des log(log(n))termes en mathématiques.
4 Id est, un pas doit occuper un intervalle de temps fini, mais non nul, ni non connexe.
5 Cela signifie que le mode de calcul en tant que connaissance du temps physique en elle, qui est peut l' exprimer à ses conditions. Une analogie est le fonctionnement des génériques dans le framework .NET.

Yuni Mj
la source
3
"Donc votre grand temps d'exécution O est juste" .. Je suis sûr que vous vouliez dire "grande complexité O" ?. De plus, nous pouvons toujours simplement appeler «deltaTime» notre «n» droit .. donc votre énonciation O (2 ^ N) quelque chose comme la complexité de l'algorithme de Fibonacci. Comment êtes-vous arrivé au "2 ^"?
Ross
@ Ross, merci pour le point. Je suis venu avec 2 par habitude de travailler avec des nombres binaires. Le fait est que les étapes sont linéaires avec la longueur de la représentation du nombre. La base réelle n'est pas vraiment importante et varie en fonction du codage spécifique. C'est pseudo linéaire .
Yuni Mj
Je suis désolé, mais pourriez-vous expliquer plus en détail dans votre réponse comment vous avez conclu que la complexité est O(2^n)? Ce n'est pas clair pour les débutants.
Arturo Torres Sánchez
2
@YuniMj Bien que votre raisonnement ne soit techniquement pas faux, je pense qu'en insistant pour mesurer la taille de DeltaTimeplutôt que sa valeur , vous ajoutez simplement une confusion supplémentaire. Par exemple, mais ce raisonnement, aucun algorithme de tri optimal n'a une complexité temporelle $ O (n \ cdot log n) $. Pourquoi? Parce que vous n'avez qu'à trier un nombre fini d'objets distinctifs, auquel cas vous pouvez toujours utiliser le tri par compartiment pour trier $ O (n) $. Ou la taille de votre objet est illimitée, auquel cas $ O (n \ cdot log n) $ ne tiendra pas, car une seule comparaison n'aura plus de temps constant ...
fgp
1
FWIW O (2 ^ n)! = O (10 ^ n) stackoverflow.com/questions/19081673/…
Nathan FD
29

Bien qu'il y ait un tas de bonnes réponses ici, permettez-moi de les reformuler un peu.

La notation Big-O existe pour décrire les fonctions . Lorsqu'il est appliqué à l'analyse d'algorithmes, cela nous oblige à définir d'abord certaines caractéristiques de cet algorithme en termes de fonction . Le choix courant consiste à considérer le nombre d'étapes en fonction de la taille d'entrée . Comme indiqué dans d'autres réponses, proposer une telle fonction dans votre cas semble étrange, car il n'y a pas d '«entrée» clairement définie. Nous pouvons toujours essayer de le faire:

  • Nous pouvons considérer votre algorithme comme une fonction constante qui prend toute entrée de n'importe quelle taille, l'ignore, attend un laps de temps fixe et se termine. Dans ce cas, son runtime est f (n) = const , et c'est un algorithme O (1) -time. C'est ce que vous vous attendiez à entendre, non? Oui, c'est, techniquement, un algorithme O (1) .
  • Nous pouvons considérer le TwentyYearsLatercomme le paramètre d'intérêt semblable à la "taille d'entrée". Dans ce cas, le runtime est f (n) = (nx)x est le "moment présent" au moment de l'invocation. Vu de cette façon, il s'agit d'un algorithme à temps O (n). Attendez-vous à ce contre-argument chaque fois que vous montrez votre algorithme techniquement O (1) à d'autres personnes.
  • Oh, mais attendez, si k =TwentyYearsLater est l'entrée, alors sa taille n est, en fait, le nombre de bits nécessaires pour la représenter, c'est-à-dire n = log (k) . La dépendance entre la taille de l'entrée n et le runtime est donc f (n) = 2 ^ n - x . On dirait que votre algorithme est devenu exponentiellement lent! Pouah.
  • Une autre entrée du programme est en fait le flux de réponses donné par le système d'exploitation à la séquence d' DateTime.Nowappels dans la boucle. Nous pouvons en fait imaginer que toute cette séquence est fournie en entrée au moment où nous exécutons le programme. Le runtime peut alors être considéré comme dépendant de la propriété de cette séquence - à savoir sa longueur jusqu'au premier TwentyYearsLaterélément. Dans ce cas, le runtime est à nouveau f (n) = n et l'algorithme est O (n) .

Mais là encore, dans votre question, vous n'avez même pas dit que vous étiez intéressé par le runtime. Et si vous parliez de l'utilisation de la mémoire? En fonction de la manière dont vous modélisez la situation, vous pouvez dire que l'algorithme est O (1) -memory ou, peut-être, O (n) -memory (si l'implémentation de DateTime.Nownécessite de garder une trace de toute la séquence d'invocation, quelque part).

Et si votre objectif était de trouver quelque chose d'absurde, pourquoi ne pas aller à fond et dire que vous vous intéressez à la façon dont la taille du code de l'algorithme en pixels à l'écran dépend du niveau de zoom choisi. Cela pourrait être quelque chose comme f (zoom) = 1 / zoom et vous pouvez déclarer fièrement que votre algorithme est de taille O (1 / n) -pixel!

KT.
la source
+1. Je crois que le "flux de réponses données par le système d'exploitation à la séquence d' DateTime.Nowappels" est la véritable entrée ici. Mais je pense que la conclusion ne devrait pas être que c'est O (n), mais c'est O (k), où k est la longueur jusqu'au premier TwentyYearsLaterélément.
juste la moitié du
7
C'est la meilleure réponse à ce jour - pour que Big O soit significatif, vous devez appliquer des sémantiques / hypothèses mathématiques à l'implémentation physique (définissant essentiellement un modèle mathématique pour le programme avec une définition significative de «entrée»). En ce sens, la complexité du "programme" dépend de la sémantique que vous appliquez - si vous supposez que N est la différence de temps qui évolue linéairement avec le nombre d'opérations, c'est O (n). Si vous supposez un nombre fixe d'opérations à la suite d'une période de temps fixe, c'est O (1).
Ant P
21

Je suis légèrement en désaccord avec Servy. Il y a une entrée dans ce programme, même si ce n'est pas évident, et c'est l'heure du système. C'est peut-être une technicité que vous n'aviez pas prévue, mais votre TwentyYearsFromNowvariable n'est pas dans vingt ans à partir de l' époque du système , elle est attribuée statiquement au 1er janvier 2035.

Donc, si vous prenez ce code et que vous l'exécutez sur une machine dont l'heure système est le 1er janvier 1970, cela prendra 65 ans, quelle que soit la vitesse de l'ordinateur (il peut y avoir des variations si son horloge est défectueuse ). Si vous prenez ce code et que vous l'exécutez sur une machine dont l'heure système est le 2 janvier 2035, il se terminera presque instantanément.

Je dirais que votre entrée, nest January 1st, 2035 - DateTime.Now, et c'est O (n).

Ensuite, il y a aussi la question du nombre d'opérations. Certaines personnes ont noté que des ordinateurs plus rapides atteindraient la boucle plus rapidement, entraînant plus d'opérations, mais ce n'est pas pertinent. Lorsque vous travaillez avec la notation big-O, nous ne considérons pas la vitesse du processeur ou le nombre exact d'opérations. Si vous avez pris cet algorithme et que vous l'exécutez sur un ordinateur, puis que vous l'exécutez à nouveau, mais pendant 10 fois plus longtemps sur le même ordinateur, vous vous attendez à ce que le nombre d'opérations augmente du même facteur de 10x.

Quant à ceci:

Je pense utiliser l'extrait de code [code rédigé] comme une boucle occupée à mettre en plaisanterie chaque fois que quelqu'un demande un algorithme d'une certaine complexité. Serait-ce correct?

Non, pas vraiment. D'autres réponses ont couvert cela, alors je voulais juste le mentionner. Vous ne pouvez généralement pas corréler les années d'exécution à une notation big-O. Par exemple. Il n'y a aucun moyen de dire 20 ans d'exécution = O (n ^ 87) ou quoi que ce soit d'autre d'ailleurs. Même dans l'algorithme que vous avez donné, je pourrais changer l' TwentyYearsFromNowannée 20110, 75699436 ou 123456789 et le big-O est toujours O (n).

Shaz
la source
7
L'heure n'est pas une entrée de la fonction, c'est un état en constante évolution qui est observé tout au long de l'exécution de la méthode. L'horloge système peut même être modifiée pendant l'exécution de la fonction . Pour que Big O soit significatif, vous devez également que chaque entrée corresponde 1-1 à une valeur de sortie, ainsi qu'à un certain nombre d'opérations nécessaires pour la calculer. Pour cette opération, la sortie n'est même pas cohérente pour la même entrée (en fait elle varie énormément ), en plus du nombre d'opérations effectuées qui varie également énormément.
Servy
When working with big-O notation, we don't consider the speed of the processor or the exact number of operations.C'est une fausse déclaration. Pratiquement toute opération sensée dont vous tenteriez de calculer la valeur Big O ne changera pas le nombre d'opérations effectuées en fonction du matériel, mais celle-ci le fait . Big O est juste un moyen de relier le nombre d'opérations à la taille de l'entrée. Pour la plupart des opérations indépendantes du matériel système. Dans ce cas, ce n'est pas le cas .
Servy
If you took this algorithm and ran it on a computer, and then ran it again but for 10x longer on the same computer, you would expect the number of operations to grow by the same factor of 10x.C'est aussi une fausse déclaration. L'environnement ne modifiera pas nécessairement le nombre d'opérations dans la boucle de manière linéaire. Il pourrait, par exemple, y avoir d'autres programmes sur l'ordinateur qui utilisent plus ou moins de temps CPU à différents moments, changeant constamment le temps donné à cette application au fil du temps.
Servy
Je suis avec @Servy sur celui-ci, mais pour une raison légèrement différente. La fonction principale ne prend aucun paramètre et ne renvoie aucune entrée. C'est une fonction de nil => nil, si vous le souhaitez. Peu importe l'heure, il ne renvoie toujours rien.
Steve Cooper
1
Si nous utilisons cette définition - "En mathématiques, une fonction est une relation entre un ensemble d'entrées et un ensemble de sorties autorisées avec la propriété que chaque entrée est liée à exactement une sortie." (wikipedia) - et nous comptons la sortie de la console comme 'la sortie de la fonction', cela varie, s'allongeant sur un ordinateur plus rapide, car il écrira "" Il n'est toujours pas temps d'imprimer le bonjour ... "" plus souvent.
Steve Cooper
13

L'analyse Big-O traite de la quantité de traitement impliquée lorsque la quantité de données traitées augmente sans limite.

Ici, vous n'avez vraiment affaire qu'à un seul objet de taille fixe. En tant que tel, l'application de l'analyse big-O dépend fortement (principalement?) De la façon dont vous définissez vos termes.

Par exemple, vous pourriez vouloir dire l'impression de la sortie en général et imposer une attente si longue que toute quantité raisonnable de données serait / sera imprimée exactement dans la même période de temps. Vous devez également ajouter un peu plus de définitions quelque peu inhabituelles (sinon carrément fausses) pour aller très loin - en particulier, l'analyse big-O est généralement définie en termes du nombre d'opérations fondamentales nécessaires pour effectuer un tâche particulière (mais notez que la complexité peut également être considérée en termes de choses comme l'utilisation de la mémoire, pas seulement l'utilisation du processeur / les opérations effectuées).

Le nombre d'opérations fondamentales se traduit généralement assez étroitement par le temps nécessaire, ce n'est donc pas un énorme problème de traiter les deux comme synonymes. Malheureusement, cependant, nous sommes toujours coincés avec cette autre partie: la quantité de données traitées augmente sans limite. Cela étant, aucun délai fixe que vous pouvez imposer ne fonctionnera vraiment. Pour assimiler O (1) à O (N), vous devez imposer un délai infini afin que toute quantité fixe de données prenne une éternité à s'imprimer, tout comme le ferait une quantité infinie de données.

Jerry Coffin
la source
10

big-O par rapport à quoi?

Vous semblez comprendre que twentyYearsLaterc'est une «entrée». Si en effet vous avez écrit votre fonction comme

void helloWorld(int years) {
   // ...
}

Ce serait O (N) où N = années (ou dites simplement O(years) ).

Je dirais que votre algorithme est O (N) par rapport au nombre que vous écrivez dans la ligne de code commençant par twentyYearsLater =. Mais les gens ne considèrent généralement pas les nombres dans le code source réel comme entrée. Ils peuvent considérer l'entrée de ligne de commande comme entrée, ou l'entrée de signature de fonction comme entrée, mais très probablement pas le code source lui-même. C'est ce que vous contestez avec votre ami - est-ce «l'entrée»? Vous configurez votre code de manière à le faire apparaître intuitivement comme une entrée, et vous pouvez certainement demander son grand temps d'exécution O par rapport au nombre N sur la ligne 6 de votre programme, mais si vous utilisez un tel choix non par défaut comme entrée, vous devez vraiment être explicite à ce sujet.

Mais si vous considérez l'entrée comme quelque chose de plus habituel, comme la ligne de commande ou l'entrée de la fonction, il n'y a pas de sortie du tout et la fonction est O (1). Cela prend vingt ans, mais comme big-O ne change pas jusqu'à un multiple constant, O (1) = O (vingt ans).

Question similaire - quel est le temps d'exécution de:

void sortArrayOfSizeTenMillion(int[] array)

En supposant qu'il fait ce qu'il dit et que l'entrée est valide, et que l'algorithme exploite un tri rapide ou un tri à bulles ou tout ce qui est raisonnable, c'est O (1).

Djechlin
la source
Le codage en dur de l'entrée ne signifie pas que l'entrée disparaît. Le tri rapide et le tri par bulles de complexité temporelle O (1) ne sont en aucun cas non plus. bigocheatsheet.com
Theo Brinkman
@TheoBrinkman Si vous voulez être technique, dans un modèle de machine de Turing, encoder ce que vous pensez de l'entrée, dans la machine de Turing elle-même, en fait, par définition, pas l'entrée. La machine de Turing fonctionnera alors dans un temps constant indépendamment de toute entrée réelle dont elle dispose. Il ne s'agit en quelque sorte pas d'un "tri à bulles" car il ne trie rien mais fonctionne plutôt sur sa propre représentation, mais en termes non techniques bien sûr, vous pouvez décrire l'algorithme comme un tri à bulles.
djechlin
En termes tout aussi «non techniques», vous pourriez décrire l'algorithme en question comme un pont suspendu.
Theo Brinkman
@TheoBrinkman non, vous ne pouvez pas. Cela n'aurait de sens pour personne.
djechlin
Cela a tout autant de sens que de le décrire comme un tri à bulles O (1).
Theo Brinkman
8

Cet "algorithme" est correctement décrit comme O (1) ou temps constant. On a fait valoir qu'il n'y a pas d'entrée dans ce programme, donc il n'y a pas de N à analyser en termes de Big Oh. Je ne suis pas d'accord pour dire qu'il n'y a pas d'entrée. Lorsqu'il est compilé dans un exécutable et appelé, l'utilisateur peut spécifier n'importe quelle entrée de longueur arbitraire. Cette longueur d'entrée est le N.

Le programme ignore simplement l'entrée (quelle que soit sa longueur), donc le temps pris (ou le nombre d'instructions machine exécutées) est le même quelle que soit la longueur de l'entrée (environnement fixe donné = heure de début + matériel), d'où O (1 ).

waldol1
la source
Mais le nombre d'opérations n'est pas nécessairement cohérent, même avec la même heure de début et le même matériel. En plus de cela, pour revendiquer un algorithme O (1), la sortie devrait toujours être constante, et ce n'est pas le cas, elle variera énormément en fonction de l'heure de début et du matériel. Il pourrait aussi très facilement être infini, ce qui n'est certainement pas constant. Il n'y a aucune relation entre l'entrée que vous avez définie et le nombre d'opérations effectuées. Ce n'est pas constant, c'est juste indéfini. Vous ne pouvez pas nommer un nombre fini et savoir qu'il y aura toujours moins d'opérations que cela.
Servy
Le temps réel maximal que cela prendra est de 20 ans. Si nous le démarrons à l'avenir, oui, cela prendra plus de temps. Supposons qu'il y ait une limite inférieure finie sur la durée d'une itération de boucle et que nous fonctionnons sur du matériel série. Ensuite, je peux limiter le nombre de fois que la boucle s'exécutera, ce qui signifie que tout le calcul peut être limité par une fonction constante, quelle que soit la taille de l'entrée ignorée.
waldol1
Let's suppose that there is a finite lower bound on the amount of time a loop iteration takesC'est une fausse hypothèse. Le programme peut fonctionner indéfiniment. Tout ce que j'ai à faire est de régler l'horloge de mon système sur 50 ans à partir de maintenant, de la démarrer et elle ne se terminera jamais. Ou je pourrais continuer à faire reculer l'horloge plus vite qu'elle n'avance, ou la démarrer à un moment indéterminé dans le passé . Vous ne pouvez tout simplement pas supposer qu'il existe une limite inférieure sur la durée d'exécution du programme; il peut fonctionner pour toujours. Mais, même si nous prenons votre (fausse) hypothèse comme vraie, vous ne pouvez toujours pas relier le nombre d'opérations effectuées à l'entrée.
Servy
Une itération à boucle unique prend un temps limité. Il peut être possible qu'il s'exécute un nombre infini de fois, mais chacune doit être à peu près constante. Je ne vois pas de problème avec cette hypothèse.
waldol1
Par cette logique [complètement incorrecte], chaque algorithme est toujours O (1) car chaque opération individuelle est toujours constante. Vous démontrez simplement que vous ne savez même pas ce qu'est Big O. C'est un outil pour (en contexte) décrire la relation entre la taille de l'entrée et le nombre d'opérations pertinentes effectuées. O (1) signifie qu'il y a un nombre constant d'opérations effectuées indépendamment de l'entrée. Ici, il n'y a pas un nombre constant d'opérations effectuées quelle que soit l'entrée, il y a des opérations potentiellement infinies effectuées, infini! = Constant.
Servy
6

Une chose qui m'étonne n'a pas encore été mentionnée: la notation big-O est une borne supérieure!

Le problème que tout le monde a remarqué est qu'il n'y a pas de N décrivant les entrées de l'algorithme, il n'y a donc rien avec quoi faire une analyse big-O. Cependant, ceci est facilement atténué avec quelques astuces de base, telles que l'acceptation int net l'impression des nheures "Hello World" . Cela permettrait de contourner cette plainte et de revenir à la vraie question de savoir comment fonctionne cette DateTimemonstruosité.

Il n'y a aucune garantie réelle que la boucle while se terminera un jour. Nous aimons penser que cela doit à un moment donné, mais considérons que cela DateTime.nowrenvoie la date et l'heure du système . Il n'y a en fait aucune garantie que cela augmente de manière monotone. Il est possible qu'un singe entraîné pathologiquement change constamment la date et l'heure du système au 21 octobre 2015 à 12:00:00 UTC jusqu'à ce que quelqu'un donne au singe des chaussures à ajustement automatique et un hoverboard. Cette boucle peut en fait fonctionner pendant une durée infinie!

Quand vous creusez réellement dans la définition mathématique des notations big-O, ce sont des bornes supérieures. Ils démontrent le pire des cas, aussi improbable soit-il. Le pire des cas * ici est un runtime infini, nous sommes donc obligés de déclarer qu'il n'y a pas de notation big-O pour décrire la complexité d'exécution de cet algorithme. Il n'existe pas, tout comme 1/0 n'existe pas.

* Edit: d'après ma discussion avec KT, il n'est pas toujours valable de supposer que le scénario que nous modélisons avec la notation big-O est le pire des cas. Dans la plupart des cas, si un individu ne spécifie pas le cas que nous utilisons, il a l'intention d'explorer le pire des cas. Cependant, vous pouvez effectuer une analyse de complexité big-O sur le meilleur scénario d'exécution.

Cort Ammon
la source
2
O est, en un sens, une "borne supérieure", en effet, mais cela ne signifie pas que vous ne pouvez parler que de la "complexité du pire des cas" en utilisant la notation O. Complexité attendue, complexité du meilleur cas, toute autre propriété fonctionnelle - toutes peuvent être discutées en fonction de leurs limites O.
KT.
La complexité du meilleur cas @KY est appelée little-o, et la complexité attendue est big-theta. big-o est toujours le pire des cas de complexité, par sa définition mathématique.
Cort Ammon
Non, vous vous trompez ici. Revérifiez les définitions.
KT.
@KT D'accord, je les revérifierai. Vous les revérifiez aussi. en.wikipedia.org/wiki/Big_O_notation Its under Family of Bachmann – Landau notations
Cort Ammon
Je suppose que vous pourriez faire quelque chose de fou comme prendre une fonction fet déclarer que la fonction gest la même que f, mais avec un domaine restreint pour n'inclure que fle meilleur des cas, puis faire big-oh dessus g, mais cela commence à sembler dégénéré lorsque vous le faites cette.
Cort Ammon
5

La complexité est utilisée pour mesurer la «puissance» de calcul en termes de temps / espace. La notation Big O est utilisée pour comparer quels problèmes sont "calculables" ou "non calculables" et aussi pour comparer quelles solutions -algorithmes- sont meilleures que d'autres. En tant que tel, vous pouvez diviser n'importe quel algorithme en deux catégories: ceux qui peuvent être résolus en temps polynomial et ceux qui ne le peuvent pas.

Des problèmes comme le tamis d'Erathostène sont O (n ^ exp) et sont donc résolubles pour de petites valeurs de n. Ils sont calculables, mais pas en temps polynomial (NP) et donc lorsqu'on leur demande si un nombre donné est premier ou non, la réponse dépend de la grandeur de ce nombre. De plus, la complexité ne dépend pas du matériel, donc avoir des ordinateurs plus rapides ne change rien ...

Hello World n'est pas un algorithme et, en tant que tel, il est insensé de tenter de déterminer sa complexité - qui n'en est pas. Un algorithme simple peut être quelque chose comme: étant donné un nombre aléatoire, déterminez s'il est pair ou impair. Maintenant, est-il important que le nombre donné ait 500 chiffres? Non, car il suffit de vérifier si le dernier chiffre est pair ou impair. Un algorithme plus complexe consisterait à déterminer si un nombre donné se divise uniformément par 3. Bien que certains nombres soient "faciles" à calculer, d'autres sont "difficiles" et c'est à cause de leur ampleur: comparez le temps qu'il faut pour déterminer le rappel entre un nombre à un chiffre et un autre à 500 chiffres.

Un cas plus complexe serait de décoder un texte. Vous avez un tableau aléatoire apparent de symboles dont vous savez également qu'ils transmettent un message à ceux qui ont la clé de déchiffrement. Disons que l'expéditeur a utilisé la clé à gauche et que votre Hello World lirait: Gwkki Qieks. La solution "big-hammer, no-brain" produirait toutes les combinaisons pour ces lettres: de Aaaa à Zzzz, puis rechercherait un dictionnaire de mots pour identifier les mots valides et partager les deux lettres communes du chiffrement (i, k) dans la même position. Cette fonction de transformation est ce que Big O mesure!

Turing
la source
4

La plupart des gens semblent manquer deux choses très importantes.

  1. Le programme fait avoir une entrée. Il s'agit de la date / heure codée en dur par rapport à laquelle l'heure du système est comparée. Les entrées sont sous le contrôle de la personne exécutant l'algorithme, et l'heure système ne l'est pas. La seule chose que la personne exécutant ce programme peut contrôler est la date / heure à laquelle elle a été codée en dur dans la comparaison.

  2. Le programme varie en fonction de la valeur d' entrée , mais pas de la taille de l' ensemble d' entrée , ce qui concerne la notation big-O.

Par conséquent, il est indéterminé, et la meilleure notation «big-O» pour ce programme serait probablement O (null), ou éventuellement O (NaN).

Theo Brinkman
la source
1
(2) est complètement faux. Habituellement, la "longueur de l'entrée" est prise en compte. Pour une liste ou un tableau d'objets de taille fixe (comme des entiers), ce sera en effet la taille de l'ensemble. Pour factoriser un nombre comme 1395195191600333, ce sera la longueur de sa représentation binaire (ou décimale, etc.), c'est-à-dire le nombre de chiffres. Comme indiqué, votre définition dans (2) interdit l'utilisation de big-O pour discuter de la complexité de "findPrimeFactors (int num)", à laquelle la plupart des cryptographes s'opposeront.
djechlin
4

Tout le monde a fait remarquer à juste titre que vous ne définissez pas N , mais la réponse est non selon l'interprétation la plus raisonnable. Si N est la longueur de la chaîne que nous imprimons et "bonjour, monde!" est juste un exemple, comme nous pourrions le déduire de la description de ceci comme un algorithme «pour hello, world!», alors l'algorithme est O ( N ), parce que vous pourriez avoir une chaîne de sortie qui prend trente, quarante ou cinquante ans à imprimer, et vous ajouter seulement un temps constant à cela. O ( kN + c ) ∈ O ( N ).

Addenda:

À ma grande surprise, quelqu'un conteste cela. Rappelez-vous les définitions du grand O et du grand Θ. Supposons que nous ayons un algorithme qui attend pendant une durée constante c puis imprime un message de longueur N en temps linéaire. (Ceci est une généralisation de l'exemple de code original.) Disons arbitrairement que nous attendons vingt ans pour commencer à imprimer, et que l'impression d'un billion de caractères prend encore vingt ans. Soit c = 20 et k = 10¹², par exemple, mais tout nombre réel positif fera l'affaire. C'est un taux de d = c / k (dans ce cas 2 × 10⁻¹¹) ans par caractère, donc notre temps d'exécution f ( N ) est asymptotiquementdN + cannées. Chaque fois que N > k , dN = c / k N > c . Par conséquent, dN < dN + c = f ( N ) <2 dN pour tout N > k , et f ( N ) ∈ Θ ( N ). QED

Davislor
la source
Où nous avons N = 13.
djechlin
Mais il n'imprime pas seulement "Hello world", il imprime un nombre inconnu de lignes "Ce n'est toujours pas l'heure". De plus, Big O n'est pas vraiment utilisé pour comparer la taille de l'entrée à la taille de la sortie, il est généralement utilisé pour comparer la taille de l'entrée au nombre d'opérations ou à la quantité de mémoire utilisée.
Servy
@Servy C'est une mémoire constante, mais je limitais implicitement le temps d'exécution. La taille de la sortie est également O ( N ), pour une chaîne arbitraire: la chaîne que nous imprimons quand il est temps pourrait être arbitrairement grande, même en comparaison avec vingt ans de messages please-wait.
Davislor
@Servy J'ai édité pour clarifier que, non, N ici n'est pas la taille de la sortie. Je ne sais pas comment j'ai donné cette impression, mais je vais lever toute ambiguïté.
Davislor
1
Donc, si vous supposez que le programme prend une entrée, quand ce n'est pas le cas, que la sortie peut être arbitrairement grande, quand elle ne le peut pas, que la boucle ne fait rien, quand elle le fait, et que la sortie est liée à l'entrée, quand ce n'est pas le cas, alors oui, le programme est linéaire. Bien sûr, chacune de ces hypothèses est complètement fausse, donc la conclusion que vous en avez tirée ne tient pas. Si vous êtes en mesure de démontrer votre point sans faire de fausses hypothèses, cela signifierait quelque chose.
Servy
4

Je pense que les gens sont rejetés parce que le code ne ressemble pas à un algorithme traditionnel. Voici une traduction du code qui est plus bien formée, mais qui reste fidèle à l'esprit de la question d'OP.

void TrolloWorld(long currentUnixTime, long loopsPerMs){
    long laterUnixTime = 2051222400000;  //unix time of 01/01/2035, 00:00:00
    long numLoops = (laterUnixTime-currentUnixTime)*loopsPerMs;

    for (long i=0; i<numLoops; i++){
        print ("It's still not time to print the hello …");
    }
    print("Hello, World!");
}

Les entrées sont explicites alors qu'avant elles étaient implicitement données par le moment où le code a été lancé et par la vitesse du matériel exécutant le code. Le code est déterministe et a une sortie bien définie pour des entrées données.

En raison des limitations imposées aux entrées que nous pouvons fournir, il existe une limite supérieure au nombre d'opérations qui seront exécutées, donc cet algorithme est en fait O (1).

Nathan FD
la source
2

À ce stade, oui

Cet algorithme a une entrée implicite, à savoir l'heure à laquelle le programme est démarré. Le temps d'exécution variera linéairement 1 selon le moment où il est démarré. Au cours de l'année 2035 et après, la boucle while se termine immédiatement et le programme se termine après des opérations constantes 2 . On pourrait donc dire que le runtime est O(max(2035 - start year, 1))3 . Mais puisque notre année de début a une valeur minimale, l'algorithme ne prendra jamais plus de 20 ans pour s'exécuter (c'est-à-dire une valeur constante).

Vous pouvez rendre votre algorithme plus conforme à votre intention en définissant DateTime TwentyYearsLater = DateTime.Now + new TimeSpan(365*20,0,0,0);4

1 Cela vaut pour le sens plus technique du temps d'exécution mesuré en nombre d'opérations car il y a un nombre maximum d'opérations par unité de temps.
2 En supposant que la récupération DateTime.Nowest une opération constante, ce qui est raisonnable.
3 J'abuse un peu de la grosse notation O ici parce que c'est une fonction décroissante par rapport à start year, mais nous pourrions facilement rectifier cela en l'exprimant en termes de years prior to 2035.
4 L'algorithme ne dépend plus de l'entrée implicite du temps de démarrage, mais c'est sans conséquence.

Nathan FD
la source
1

Je dirais que c'est O (n). en utilisant http://www.cforcoding.com/2009/07/plain-english-explanation-of-big-o.html comme référence.

Qu'est-ce que Big O?

La notation Big O cherche à décrire la complexité relative d'un algorithme en réduisant le taux de croissance aux facteurs clés lorsque le facteur clé tend vers l'infini.

et

Le meilleur exemple de Big-O auquel je puisse penser est celui de l'arithmétique. Les opérations arithmétiques de base que nous avons apprises à l'école étaient:

une addition; soustraction; multiplication; et la division. Chacun de ces éléments est une opération ou un problème. Une méthode pour résoudre ces problèmes est appelée un algorithme.

Pour votre exemple,

étant donné l'entrée de n = 20 (avec unités années).

l'algorithme est une fonction mathématique f (). où f () attend pendant n ans, avec des chaînes de «débogage» entre les deux. Le facteur d'échelle est 1. f () peut être réduit / ou augmenté en modifiant ce facteur d'échelle.

dans ce cas, la sortie est également 20 (la modification de l'entrée change la sortie de manière linéaire).

essentiellement la fonction est

f(n) = n*1 = n
    if  n = 20, then 
f(20) = 20 
Angel Koh
la source