How to count leap years in range [a, b] without loops on C#

606 views Asked by At

Assuming, you have year a and year b, which are the range of years (they are included), how to properly count leap years in this range without using loops? (gregorian calendar)

I wrote this on C#, but i don't think that my code is great. I also used a global variable, but lol, i think there are a solution that is so much better and more elegant than this. I'm just a newbie, so sorry that i'm asking this dumb question. Also, i don't think you should use DateTime here and etc.

Here's my code:

class Program
{
    public static int leap_years = 0;
    static void Main()
    {
        do
        {
            int a, b;
            leap_years = 0;
            do Console.Write("Enter a: ");
            while (!int.TryParse(Console.ReadLine(), out a) || a < 0);
            do Console.Write("Enter b: ");
            while (!int.TryParse(Console.ReadLine(), out b) || b < 0 || a == b || a >= b);
            Console.WriteLine("Leap years: " + countLeapYears(a, b));
        } while (Console.ReadKey().Key != ConsoleKey.Escape);
    }

Where countLeapYears is

    static public int countLeapYears(int a, int b)
    {
        if (a > b)
            return leap_years;
        else
        {
            if (a % 4 == 0)
            {
                if (a % 100 == 0)
                {
                    if (a % 400 == 0)
                    {
                        Console.WriteLine("Year {0} - leap year", a);
                        leap_years++;
                        a += 4;
                        countLeapYears(a, b);
                    }
                    else
                    {
                        Console.WriteLine("Year {0} - not a leap year", a);
                        a++;
                        countLeapYears(a, b);
                    }
                }
                else
                {
                    Console.WriteLine("Year {0} - leap year", a);
                    leap_years++;
                    a += 4;
                    countLeapYears(a, b);
                }
            }
            else
            {
                Console.WriteLine("Year {0} - not a leap year", a);
                a++;
                countLeapYears(a, b);
            }
        }
        return leap_years;
    }
}
2

There are 2 answers

1
Dmitry Bychenko On BEST ANSWER

Well, we don't have year 0: 1BC is followed by 1AD which spoils the fun. However, if we can work with AD (positive years) only, you can try something like this:

private static int countLeapYears(int from, int to) =>
  (to / 4 - (from - 1) / 4) -
  (to / 100 - (from - 1) / 100) +
  (to / 400 - (from - 1) / 400);

Let's test it with respect to naive computation:

private static int naiveCount(int from, int to) {
  int result = 0;

  for (int i = from; i <= to; ++i) 
    if (i % 400 == 0 || i % 4 == 0 && i % 100 != 0)
      result += 1;

  return result;
}

...

Random gen = new Random(123);

var result = Enumerable
  .Range(1, 20)
  .Select(i => {
    int to = gen.Next(1590, 2222);
    int from = gen.Next(1590, 2222);

    return (from: Math.Min(from, to), to: Math.Max(from, to));
  })
  .Select(test => $"{test.from} - {test.to} actual: {countLeapYears(test.from, test.to),3} expected: {naiveCount(test.from, test.to),3}");
  
Console.Write(string.Join(Environment.NewLine, result));

Outcome:

2163 - 2212 actual:  12 expected:  12
2059 - 2102 actual:  10 expected:  10
1620 - 2056 actual: 107 expected: 107
1600 - 1684 actual:  22 expected:  22
1713 - 1988 actual:  67 expected:  67
1902 - 2164 actual:  65 expected:  65
1709 - 1881 actual:  42 expected:  42
1639 - 2124 actual: 118 expected: 118
1751 - 1948 actual:  48 expected:  48
1594 - 2184 actual: 144 expected: 144
1605 - 1691 actual:  21 expected:  21
1591 - 2082 actual: 120 expected: 120
1993 - 2066 actual:  18 expected:  18
2022 - 2158 actual:  33 expected:  33
1678 - 1919 actual:  57 expected:  57
1966 - 2128 actual:  40 expected:  40
1635 - 2069 actual: 106 expected: 106
1649 - 1963 actual:  75 expected:  75
1719 - 2169 actual: 110 expected: 110
1847 - 2093 actual:  61 expected:  61
2
Japv On
public int LeapYear(int initialYear, int endYear, int     ref countYear)
{
    if (initialYear > endYear)
    {
         return countYear;
    }
    
    if(DateTime.IsLeapYear(initialYear)
    {
         countYear++;
    }
   
    initialYear++;
    LeapYear(initialYear, endYear, countYear)
}