
خرج هذا المقال من سؤال طرحته أمس.
"هل هناك سنة لا يبدأ فيها شهر واحد يوم الاثنين؟"
للوهلة الأولى ، نعم. يمكن أن تبدأ السنة من أي يوم من أيام الأسبوع ، كما تبدأ الشهور في كل مرة في أيام مختلفة من الأسبوع. هناك العديد من الخيارات ، على الأرجح ، سيكون هناك أكثر من سنة واحدة.
لذلك فكرت في الدقيقة الأولى بعد أن تساءلت. هذا يجب أن يثبت. تمر عبر كل السنوات ، على سبيل المثال. طريقة بسيطة وسريعة ، ولكن ليست مثيرة للاهتمام. كان إثبات الرياضيات فكرة أكثر إغراءً ، لكنني لم أفهم تمامًا كيفية التعامل مع هذا. لذلك ، بدأت للتو في كتابة مدة كل شهر على الورق.
تجدر الإشارة هنا إلى أننا سنتحدث أكثر عن
التقويم الغريغوري ، الذي نعيش فيه منذ عام 1918. ومع ذلك ، فإن جزء من المنطق سيكون صحيحا
لجوليان .
في الواقع ، مثل هذا العام غير موجود. دعونا معرفة السبب.
الجزء 1. أشهر
أولاً ، تذكر عدد الأيام في كل شهر:
الآن ، لنرى عدد الأيام في كل شهر لأكثر من أربعة أسابيع.
عند هذه النقطة ، تنشأ الفكرة التالية. إذا أضفت 7 أيام إلى التاريخ ، فلن يتغير يوم الأسبوع. الأعمال الحسابية المعيارية. من هنا ، من السهل أن نفهم أنه إذا كان هناك يومان في الشهر أكثر من أربعة أسابيع ، فسيتحول اليوم الأول من الشهر التالي لمدة يومين من الأسبوع بالنسبة إلى اليوم الأول من الشهر الحالي. وبالفعل،
إذا كان هناك (28 + N) يومًا في الشهر ، فسوف يتحول اليوم الأول من الشهر التالي إلى N أيام بالنسبة ليوم الأسبوع في اليوم الأول من الشهر الحالي.
على سبيل المثال ، بدأ يناير من هذا العام يوم الثلاثاء ، لذا بدأ فبراير يوم الجمعة. الثلاثاء + 3 = الجمعة
كم هو يوم الأسبوع في اليوم الأول من شهر معين؟ للعثور على هذا ، تحتاج إلى جمع "الفائض" أيام على مدى أربعة أسابيع في جميع الأشهر السابقة. يعرض الجدول التحولات بالنسبة ليوم الأسبوع في الأول من يناير. السطر الأول هو سنة غير قفزة ، والثاني هو سنة كبيسة.
ولكن هذا لا يبدو كاشفا للغاية ، ونحن نعلم أن التحول من سبعة أيام لا يغير يوم الأسبوع. لذلك ، نكتب الآن في الجدول البقايا الناتجة عن تقسيم إجمالي التحولات على 7.
الآن شيء آخر! يُرى بوضوح كيفية تحديد يوم الأسبوع في اليوم الأول من أي شهر إذا كان يوم الأسبوع في الأول من يناير معروفًا. تحتاج فقط إلى إضافة تحول لشهر الفائدة. لقد عرفت النمط من فبراير إلى مارس ونوفمبر منذ المدرسة الثانوية ، لكنني لم ألاحظ الآخرين.
حصلنا على إجابة السؤال في بداية المقال.
نظرًا لأن كل المتغيرات من السنة في الجدول توجد جميع التحولات من 0 إلى 6 ، ثم في أي سنة هناك شهر يبدأ في يوم معين من الأسبوع.
لكن الآن يمكنك طرح أسئلة أخرى. على سبيل المثال ، "في أي سنة يوجد شهر واحد فقط؟" أو "في أي سنة من هذه الأشهر هو الحد الأقصى لعدد هذه الأشهر؟" للقيام بذلك ، يجب أن تكون قادرًا على تحديد يوم الأسبوع في الأول من شهر يناير من أي عام.
الجزء 2. سنوات
عندما تعلمت البرنامج ، وكان هذا في الصف العاشر من المدرسة في PascalABC ، كانت إحدى المهام الجدية الأولى هي تنفيذ إجراء يطبع تقويمًا للعام ، والذي تم اعتماده كحجة. كان لدينا نصائح حول ما وظائف لتنفيذ. بشكل عام ، تم حساب عدد الأيام بين تاريخين: المرجع والحالي ، لتحديد يوم الأسبوع في 1 يناير من السنة المطلوبة.
نجح هذا النهج ، ولكن السرعة تعتمد على مدى قرب السنة المطلوبة من المرجع. لقد أزعجني ذلك ، لكن لم أستطع التوصل إلى شيء أفضل بعد ذلك. الآن ، أصبحت اللحظة المثالية لفهم هذا تمامًا.
يتم تعيين سنوات القفزة في التقويم الميلادي على النحو التالي:
- السنة التي يكون عددها مضاعف 400 سنة كبيسة
- السنوات المتبقية ، وعددها مضاعف 100 ، غير قفزة
- السنوات المتبقية ، وعددها مضاعف 4 ، هي سنوات كبيسة
- ما تبقى من السنوات غير قفزة
يوضح هذا الوصف أن دورة القفزة لها 400 عام. لكن ليس من الواضح ما إذا كانت هذه الدورات التي تبلغ أربعمائة عام ستبدأ في نفس اليوم من الأسبوع.
لاحظ أن الأول من يناير من عام إلى آخر يتم إزاحته بيوم أو يومين من الأسبوع ، والكتابة
بعض الكودbool is_leap_year(int year) { if ((year % 400) == 0) return true; if ((year % 100) == 0) return false; if ((year % 4) == 0) return true; return false; } void first_weekdays_table() { ofstream file("weekdays.txt", ios_base::out); int weekday = 3; for (int i = 1801; i <= 3000; ++i) { file << weekday; if ((i % 100) != 0) { file << " "; } else { file << endl; } weekday += is_leap_year(i) ? 2 : 1; weekday %= 7; } file.close(); }
يتم عرض أيام الأسبوع في الأول من شهر يناير من كل عام ، من عام 1801 إلى 3000. تم تعيين يوم الاثنين على أنه "0" ، الثلاثاء "1" ، وما إلى ذلك. سوف نقدم كل شيء في شكل جدول من دورتين كاملتين مدة كل منهما أربع مئة عام ونصف العام. قرون تذهب أفقيا ، والعمودي من السنة في هذه القرون. في الخلايا عند تقاطع القرن والسنة ، يتم كتابة يوم الأسبوع الذي بدأ فيه هذا العام. على سبيل المثال ، يكون يوم الأسبوع الذي بدأ فيه عام 1997 عند تقاطع العمود "1900" والخط "97". هذا الأربعاء. النسخة الكاملة من الجدول:
الجزء 1 ،
الجزء 2 .


في الجدول ، يمكنك أن تلاحظ على الفور شيئين: دورات مئة عام تبدأ حقًا في نفس اليوم من الأسبوع (2001 ، 2401 و 2801 ؛ الاثنين) ، وبدلاً من 2000 هناك "ألف وتسعمائة". وقد تم الأخير عن قصد ، لمزيد من الراحة. الحقيقة الأولى تتيح لنا المضي قدما دون عقبات.
في التقويم الغريغوري ، تبدأ جميع دورات الأربع مائة يوم الاثنين.
لكن الأكثر إثارة للاهتمام تكمن في النسخة الكاملة من الجدول. قد تجد أن كل قرن خلال دورة مدتها أربعمائة عام تتكون من دورة متكررة لمدة ثمانية وعشرين عامًا:
يبدأ القرن الأول بنوبة في الدورة تساوي 0 ، والثاني بنوبة 4 ، والثالث بنوبة 8 والرابعة بنوبة 12. لهذا ، يتم تقديم الجدول في الشكل حيث يوجد "المئات" من القرن وليس هناك صفر. تجدر الإشارة إلى أن هناك 14 خيارًا مختلفًا لهذا العام. في دورة مدتها ثمانية وعشرون عامًا ، مرة واحدة لكل يوم من أيام الأسبوع ، تبدأ بداية سنة كبيسة وثلاثة أضعاف بداية سنة غير قفزة.
الآن يمكننا تحديد يوم الأسبوع لأي تاريخ دون استخدام التواريخ المرجعية. للقيام بذلك ، نحتاج إلى أن نفهم في أي قرن ، خلال دورة من أربعمائة عام ، هو عام ، وما هو حسابه في هذا القرن. وفقًا للجدول ، نحدد يوم الأسبوع في الأول من شهر يناير من العام ، وبمساعدة الجزء الأول من المقالة - يوم الأسبوع في يوم محدد من الشهر المطلوب. بدلا من ألف كلمة
سنكتب بعض الرموز. int get_weekday(int year, int month, int day) { int weekdays[] = {0, 1, 2, 3, 5, 6, 0, 1, 3, 4, 5, 6, 1, 2, 3, 4, 6, 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 5}; int shift_not_leap[] = {0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5}; int shift_leap[] = {0, 3, 4, 0, 2, 5, 0, 3, 6, 1, 4, 6}; bool is_leap = is_leap_year(year); year -= 1; year %= 400; int century = year / 100; year %= 100; int index = (year + (4 * century)) % 28; int weekday = weekdays[index]; weekday += is_leap ? shift_leap[month - 1] : shift_not_leap[month - 1]; weekday += (day - 1); weekday %= 7; return weekday; }
تحديث من 07/03/2019
إذا قدمنا دورة ثمانية وعشرين سنة في شكل جدول ،
0, 1, 2, 3, 5, 6, 0, 1, 3, 4, 5, 6, 1, 2, 3, 4, 6, 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 5
يصبح من الواضح كيف يمكنك حساب تحول يوم الأسبوع إلى 1 يناير:
weekday = (index + (index / 4)) % 7;
بالنظر إلى هذا ، بالإضافة إلى حقيقة أنه يمكن حساب الإزاحات لأشهر في سنة كبيسة من خلال الإزاحة في سنة غير قفزة ، نكتب
الوظيفة التالية int get_weekday_c(int year, int month, int day) { int shifts[] = {0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5}; int shift = shifts[month - 1]; if (is_leap_year(year) and (month > 2)) { shift += 1; }; year = (year - 1) % 400; int century = year / 100; int index = ((4 * century) + (year % 100)) % 28; int weekday = (index + (index / 4)) + shift + (day - 1); return (weekday % 7); }
وبالتالي ، يمكنك حساب يوم الأسبوع لأي تاريخ ، بمعرفة 12 رقمًا فقط: تحول أيام الأسبوع إلى اليوم الأول من كل شهر.
الجزء 3. ملخص
باستخدام جدولين فقط ، يمكنك تحديد يوم الأسبوع لأي تاريخ دون استخدام التواريخ المرجعية.
تسلسل أيام الأسبوع في 1 يناير في دورة مدتها ثمانية وعشرون:
وجدول إزاحة أيام الأسبوع في اليوم الأول من كل شهر لسنوات غير قفزة وكبيسة:
في وقت كتابة المقال ، وجدت على حبري موضوعين متشابهين:
واحد واثنين . يوضح مؤلف الأول ، باستخدام جدول خاص ، كيفية العثور في العقل على يوم من أيام الأسبوع للتواريخ في القرنين العشرين والعشرين والعشرين. يحتوي الجدول الذي قدمه على 56 رقمًا. تستخدم الخوارزمية المقترحة في المقالة جدول أيام الأسبوع وجدولين إزاحة يحتويان على (28 + 2 * 12) = 52 رقمًا تحتاج إلى تذكرها. جميع التعليمات البرمجية المصدر على
جيثب .
حقيقة مثيرة للاهتمام: من 1 إلى 13 فبراير 1918 ، لم يولد أي شخص في روسيا السوفيتية.
اسأل نفسك الأسئلة في الصباح يوم الأحد =)