【问题标题】:Finding the right ''algorithm'' to determine the first day of a given month找到正确的“算法”来确定给定月份的第一天
【发布时间】:2022-01-04 04:16:43
【问题描述】:

我试图制作一个程序来显示特定年份的给定月份的日历,但我真的找不到能找到该月第一天的算法。

我的程序:

#include <stdio.h>
#include <stdlib.h>

int get_first_weekDay(int year, int months1, int monthDays[months1])
{
    int day;
    int months2 = monthDays[months1];
    int beginning = 3;
    while(months1 > 0)
    {
        months2 = months2 + monthDays[months1];
        months1 = months1 - 1;
    }
    for (int i = 1800; i < year; i++)
    {
        if ((i % 4 == 0 && year % 100 != 0) || year % 400 == 0)
        {
            beginning = beginning + 366;
        }
        else
        {
            beginning = beginning + 365;
        }
    }
    day = (beginning + months2) % 7;
    return day;
}

int main(int argc, char* argv[])
{
    if(argc != 3)
    {
        printf("Usage: ./calendar year month \n");
        return 1;
    }
    int year = atoi(argv[1]);
    int months1 = atoi(argv[2]) - 1;
    int day = 0, dayInMonth, weekDay = 0, startingDay ,month;
    char *months[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
    int monthDays[] = {31,28,31,30,31,30,31,31,30,31,30,31};
    if (argc == 3)
    {
        if((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
        {
            monthDays[1] = 29;
        }
        startingDay = get_first_weekDay(year, months1, &monthDays[months1]);
        for(month = 0; month < 12; month++)
        {
            if(months1 == month)
            {
                dayInMonth = monthDays[month] + 1;
                printf("           %s %d          \n ---------------------------", months[month], year);
                printf("\n Sun Mon Tue Wed Thu Fri Sat\n");
                for(weekDay = 0; weekDay < startingDay; weekDay++)
                {
                    printf("    ");
                }
                for(day = 1; day < dayInMonth; day++)
                {
                    printf("%4d", day);
                    if(++weekDay > 6)
                    {
                        printf("\n");
                        weekDay = 0;
                    }
                    startingDay = weekDay;
                }
            }
        }
        printf("\n");
        return 1;
    }
}

例如,如果我想知道 1800 年的第一个月,它会给我作为输出:

~/calendar/ $ ./calendar 1800 1
           Jan 1800          
 ---------------------------
 Sun Mon Tue Wed Thu Fri Sat
                           1
   2   3   4   5   6   7   8
   9  10  11  12  13  14  15
  16  17  18  19  20  21  22
  23  24  25  26  27  28  29
  30  31

但不正确,因为 1800 年 1 月的第一天不是星期六。 Calendar 1800

谁能帮我找到这样的算法。我真的很感激!

【问题讨论】:

  • 这是哪个cs50问题集?讲座中没有解释吗? if (argc == 3) 块只会创建不必要的缩进。如果不是 3,程序会提前退出,所以当你到达那里时,它必须是 3,所以不需要检查。
  • 请不要编辑代码来修复答案指出的错误。它使答案无效。
  • 您在测试旧年时需要小心,因为过去对日历进行了一些奇怪的调整,这基本上使得这样的程序无法正常工作。一个例子是这样的:en.wikipedia.org/wiki/1752

标签: c cs50


【解决方案1】:

您在月份处理中遇到了一个错误。您打印的是 1800 年 2 月。

int months2 = monthDays[months1];

应该是

int months2 = 0;

在了解代码的作用后,这很容易调试。

代码以 Wed (int beginning = 3;) 开头。

然后它会添加从 1800 年开始到指定年份的每一年的天数。

然后它将每个月的天数添加到指定月份。

所以它应该在 1800 年 1 月加零。

这就是它试图做的,至少。那么为什么它返回6?嗯,很容易确认循环从未进入。这是对的。而且很容易看出它在 1 月之前的月份中增加了非零天数。好吧,这显然是错误的。


年的处理也有问题。

(i % 4 == 0 && year % 100 != 0) || year % 400 == 0

应该是

(i % 4 == 0 && i % 100 != 0) || i % 400 == 0

清理完毕:

#include <stdio.h>
#include <stdlib.h>

static const char *months[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
static const int monthDays[] = {31,28,31,30,31,30,31,31,30,31,30,31};

// Returns 0 or 1
static int is_leap_year(int year) {
    return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
}

static int get_days_in_month(int year, int month0) {
    if (month0 == 1) {
        int leap_days = is_leap_year(year);
        return 28 + leap_days;
    } else {
        return monthDays[month0];
    }
}

static int get_dow(int year, int month0, int day) {
    int dow = 3;  // 1800-01-01 is a Wednesday.

    // For each year up to provided year (exclusive), starting with 1800.
    while (year-- > 1800) {
        int leap_days = is_leap_year(year);
        dow += 365 + leap_days;
    }

    // For each month up to provided month (exclusive)
    while (month0--)
        dow += monthDays[month0];

    // For each day up to the provided day (exlcusive)
    dow += day - 1;

    return dow % 7;
}

int main(int argc, char* argv[]) {
    if(argc != 3) {
        fprintf(stderr, "Usage: ./calendar year month \n");
        return 1;
    }

    int year   = atoi(argv[1]);
    int month0 = atoi(argv[2]) - 1;  // 0-based month
    int day    = 1;                  // 1-based day

    int dow      = get_dow(year, month0, day);
    int num_days = get_days_in_month(year, month0);

    printf("          %s %d\n", months[month0], year);
    printf(" ---------------------------\n");
    printf(" Sun Mon Tue Wed Thu Fri Sat\n");

    for (int i=0; i<dow; ++i)
       printf("    ");

    while (day <= num_days) {
        printf("%4d", day++);
        dow = ( dow + 1 ) % 7;
        if (!dow)
           printf("\n");
    }

    if (dow)
        printf("\n");

    return 0;
}

【讨论】:

  • 由于某种奇怪的原因,它没有为2021 11打印正确的日历
  • 嗯,yes it does
  • 嗯,这很奇怪。如果我编译它,它会给我超过 2000 年的每个月的同一起始日
  • 即使是2000 1,它也没有给出正确的日历,因为我尝试了你刚刚发送的链接
  • Re "即使是 2000 1 也没有给出正确的日历",是的,That is true. 检查还有什么问题。
【解决方案2】:
for (int i = 1800; i < year; i++)
{
    ...
    if ((i % 4 == 0 && year % 100 != 0) || year % 400 == 0)
}

不清楚上述公式试图计算什么。看起来像闰年计算,但year 应该是i

函数get_first_weekDay 需要一个数组作为第三个参数。但你称它为

startingDay = get_first_weekDay(year, months1, &monthDays[months1]);

修复这些错误似乎没有多大作用。其余的计算相当混乱。我重写了函数get_first_weekDay。请注意,它需要一个数组输入。

#include <stdio.h>
#include <stdlib.h>

int is_leap_year(int y)
{ return (y % 4 == 0 && y % 100 != 0) || (y % 400 == 0); }

int get_first_weekday(int year, int month, int days_in_month[12])
{
    int beginning = 3; //1800, Jan, 1 is wedndsay (3)
    for (int y = 1800; y < year; y++)
        beginning += is_leap_year(y) ? 366 : 365;
    for (int m = 0; m < month; m++)
        beginning += days_in_month[m];
    return beginning % 7;
}

void print_calendar(int year, int month)
{
    month--;
    const char* months[] = { "Jan","Feb","Mar","Apr","May","Jun",
        "Jul","Aug","Sep","Oct","Nov","Dec" };
    int days_in_month[] = { 31,28,31,30,31,30,31,31,30,31,30,31 };
    if (is_leap_year(year))
        days_in_month[1] = 29;
    int first_week_day = get_first_weekday(year, month, days_in_month);
    printf("           %s %d            \n", months[month], year);
    printf(" ---------------------------\n");
    printf(" Sun Mon Tue Wed Thu Fri Sat\n");
    for (int wkday = 0; wkday < first_week_day; wkday++)
        printf("    ");
    for (int day = 1; day < days_in_month[month] + 1; day++)
    {
        printf("%4d", day);
        if(((day + first_week_day) % 7) == 0)
            printf("\n");
    }
    printf("\n\n");
}

int main(void)
{
    for (int m = 1; m < 4; m++) print_calendar(1800, m);
    for (int m = 8; m <= 12; m++) print_calendar(2021, m);
    return 0;
}

Run

【讨论】:

  • Re“不清楚上面的公式是想计算什么”,1800年到指定年份之间的年份天数
  • Re "函数 get_first_weekDay 需要一个数组作为第三个参数",不。我的意思是,这甚至是不可能的。参数永远不能是数组。
  • Re "剩下的计算比较混乱。",一点也不。代码以 Wed (int beginning = 3;) 开头。然后它会添加到指定年份的每一年的天数,从 1800 年开始。然后它会添加到指定月份的每个月的天数。
  • @ikegami 您在过去一小时内发布了 4 个不同的修订版stackoverflow.com/posts/70118772/revisions,但可以说您并不感到困惑。然而,它确实让我感到困惑。
  • Re "您在过去一小时内发布了 4 个不同的修订版",哦,远不止这些。使答案更清晰,并添加第二个错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-02-07
  • 2011-06-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-08-16
  • 1970-01-01
相关资源
最近更新 更多