web-dev-qa-db-fra.com

Exactitude de l'algorithme de Sakamoto pour trouver le jour de la semaine

J'utilise l'algorithme de Sakamoto pour connaître le jour de la semaine à partir d'une date donnée. Quelqu'un peut-il me dire l'exactitude de cet algorithme? Je veux juste cela de 2000 à 2099.

L'algorithme de Wikipedia est donné à titre de référence.

int dow(int y, int m, int d)
{
   static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
   y -= m < 3;
   return (y + y/4 - y/100 + y/400 + t[m-1] + d) % 7;
}
57
Harikrishnan

Eh bien, vous pouvez voir juste en le regardant que c'est correct ... En supposant que le t[] le tableau est correct, ce que vous pouvez vérifier avec seulement 12 vérifications ponctuelles (une pour chaque mois en utilisant n'importe quel jour/année).

Le y -= m < 3 est une bonne astuce. Il crée une "année virtuelle" qui commence le 1er mars et se termine le 28 février (ou 29), mettant le jour supplémentaire (le cas échéant) à la fin de l'année; ou plutôt à la fin de l'année précédente . Ainsi, par exemple, l'année virtuelle 2011 a commencé le 1er mars et se terminera le 29 février, tandis que l'année virtuelle 2012 commencera le 1er mars et se terminera le 28 février suivant.

En plaçant le jour supplémentaire pour les années bissextiles à la fin de l'année virtuelle, le reste de l'expression est massivement simplifié.

Regardons la somme:

(y + y/4 - y/100 + y/400 + t[m-1] + d) % 7

Il y a 365 jours dans une année normale. C'est 52 semaines plus 1 jour. Ainsi, le jour de la semaine se déplace d'un jour par an, en général. C'est ce que le terme y apporte; il en ajoute un à la journée pour chaque année.

Mais tous les quatre ans est une année bissextile. Ceux-ci contribuent une journée supplémentaire tous les quatre ans. Grâce à l'utilisation des années virtuelles, nous pouvons simplement ajouter y/4 à la somme pour compter combien de jours bissextiles se produisent en y années. (Notez que cette formule suppose des arrondis de division entiers vers le bas .)

Mais ce n'est pas tout à fait vrai, car tous les 100 ans n'est pas une année bissextile. Nous devons donc soustraire y/100.

Sauf que tous les 400 ans est à nouveau une année bissextile. Nous devons donc ajouter y/400.

Enfin, nous ajoutons simplement le jour du mois d et un décalage à partir d'un tableau qui dépend du mois (car les limites du mois dans l'année sont assez arbitraires).

Ensuite, prenez tout le mod 7 car c'est la durée d'une semaine.

(Si les semaines étaient de huit jours, par exemple, qu'est-ce qui changerait dans cette formule? Eh bien, ce serait le mod 8, évidemment. De plus, le y devrait être 5*y, car 365% 8 == 5. Le tableau des mois t[] devrait être ajusté. C'est ça.)

Soit dit en passant, la déclaration de Wikipédia selon laquelle le calendrier est "bon jusqu'en 9999" est totalement arbitraire. Cette formule est bonne aussi longtemps que nous nous en tenons au calendrier grégorien , que ce soit 10 ans, 100 ans, 1000 ans ou 1 million d'années.

[Éditer]

L'argument ci-dessus est essentiellement une preuve par induction. Autrement dit, en supposant que la formule fonctionne pour un particulier (y, m, d), vous prouvez pour lequel cela fonctionne (y + 1, m, d) et (y, m, d + 1). (Où y est une "année virtuelle" à partir du 1er mars.) La question clé est donc la suivante: la somme change-t-elle selon le montant correct lorsque vous passez d'une année à l'autre? Avec la connaissance des règles de l'année bissextile, et avec "l'année virtuelle" ayant le jour supplémentaire à la fin de l'année, c'est trivial.

119
Nemo

Récemment, j'ai écrit un article de blog sur cet algorithme ici .

L'idée de base derrière l'algorithme est de compter février et janvier pour compter le jour de la semaine du 31 décembre de l'année précédente . Pour tous les autres mois, nous compterons le jour de la semaine à partir de l'année actuelle le 31 décembre. Nous le faisons en deux étapes d'abord, nous calculons le jour de la semaine du dernier jour du mois précédant le mois en cours m alors nous ajoutons simplement d modulo seven.

31 décembre 1 BC est dimanche qui est codé comme 0, lundi est 1 etc. Nous avons donc: 0 + y + y/4 - y/100 + y/400 ceci avec y -= m < 3 calcule le jour de la semaine du 31 décembre de l'année en cours ou de l'année précédente (selon le mois). Remarque: 365 % 7 == 1 cela explique pourquoi nous avons écrit y au lieu de 365*y. Le dernier composant d est évident puisque nous commençons à compter le jour de la semaine du mois précédent le jour dernier.

La dernière partie qui doit être expliquée est les valeurs dans le tableau, pour les deux premières valeurs, il s'agit du nombre de jours écoulés depuis le 31 décembre de l'année dernière jusqu'au début du mois % 7. Pour le reste des mois, ils sont annulés modulo sept nombre de jours de la fin du mois précédent au 31 décembre de l'année en cours. En d'autres termes, nous soustrayons les jours en ajoutant modulo 7, par ex. (a-b)%7 = (a+(7-b%7))%7.

Vous trouverez plus d'explications dans mon article de blog.

3
csharpfolk