闰年 - 仅使用 C 中的算术运算符

leap year - by only using arithmetical operators in C

我有一个我见过的最奇怪的练习:我必须通过在控制台中扫描年份来找到闰年,并控制它是否是闰年。

我只能使用+ - / * %作为算术运算符;我不允许使用任何其他运算符或函数。

这是我目前的情况:

   int year = 0;
   bool b = false;
    
   printf ("Type in a year: ");
    
   int helpVar = 1000;
   for (int i = 0; i < 4; i++) {
       year += (getchar() - '0') * helpVar;
       helpVar = helpVar / 10;
   }
    
   b = (((year % 4) + (year % 100) + (year % 400)) + 1) % 2;

所以我不明白我在这里做错了什么。到目前为止它有效,唯一让我害怕的是“1900”年。它不应该是闰年,但根据我的代码似乎是闰年。

我在这里错过了什么?

根据上面的post,aleap year的定义是:对于年份n

  • n应该是4的倍数,可以表示为n % 4 == 0
  • n400 的倍数或 n 不是 100
  • 的倍数

问题是您不应该将所有这三个条件都写成 加法 操作。你应该把它们写成逻辑表达式。

在您的代码中:

b = (((year % 4) + (year % 100) + (year % 400)) + 1) % 2;

有3个条件,year % 4year % 100year % 400,求值后有两种方法使b = true:

  • 三个条件都不成立
  • 只有一种情况是错误的

好吧,让我们说清楚

闰年定义:

  • n是4的倍数而不是100的倍数
  • n 是 400
  • 的倍数

所以你的代码可以是这样的:

int year;
if (year % 400) {
    printf("Fine it's a leap year\n");
} else if (year % 100 == 0) {
    printf("Oh no it's not a leap year\n");
} else if (yaer % 4 == 0) {
    printf("It's a leap year\n");
} else {
    printf("Not a leap year\n");
}

这是一种可能性(也许不是最短的——显然只适用于公历):

b = (((year-1)%4)+1)/4 - (((year-1)%100)+1)/100 + (((year-1)%400)+1)/400;

这个想法是 ((year-1) % n) + 1 等于 n 仅当 yearn 的倍数(对于正数 year),并且小于n 否则。因此,如果将其除以 n,当且仅当 (year % n == 0).

时,您会得到 1

如果 year%4==0 不成立,year%100==0 就不可能成立,因此您可以将其相互减去,但在末尾添加 year%400==0 项。

问题是您的 % 运算是在 整数 算术中完成的,因此,它们的结果很可能是 0 以外的值或1。但是,如果您将每个结果都转换为 bool 类型(假设在 <stdbool.h> header 中定义),那么您的公式将起作用:

    b = (((bool)(year % 4) + (bool)(year % 100) + (bool)(year % 400)) + 1) % 2;

我对您的代码所做的唯一修改是在每个 % 操作的结果上添加 (bool) 转换。


编辑:正如评论中所指出的,上述解决方案'breaks the rules' 通过使用转换运算符。以下修改也有效(以类似的方式),但它在每个 % 结果上使用 ! 运算符(两次);这也违反了规则,但对后代来说可能是件好事:

    b = ((!!(year % 4) + !!(year % 100) + !!(year % 400)) + 1) % 2;

仅使用 隐式 bool 类型转换(因此不使用强制转换运算符)的此解决方案的修改似乎符合规则。这可以使用 temporary/intermediate 变量来保存每个 % 操作的结果:

    bool four = year % 4, hundred = year % 100, fourhund = year % 400;
    b = (four + hundred + fourhund + 1) % 2;

或者,您可以 pre-declare 三个中间 bool 变量,然后进行隐式类型转换 'inline',如下所示:

    bool m4, m100, m400; // You could also move this to where you declare "b"
    b = (((m4 = year % 4) + (m100 = year % 100) + (m400 = year % 400)) + 1) % 2;