2010-07-10 7 views
5

Я сделал программу, используя C, чтобы узнать, является ли введенный год високосным годом или нет. Но, к сожалению, он плохо работает. В нем говорится, что год является прыжком, а предыдущий год не прыжком.Как быстро найти программный год в C

#include<stdio.h> 
#include<conio.h> 
int yearr(int year); 
void main(void) 
{ 
    int year; 
    printf("Enter a year:"); 
    scanf("%d",&year); 
    if(!yearr(year)) 
    { 
     printf("It is a leap year."); 
    } 
    else 
    { 
    printf("It is not a leap year"); 
    } 


getch(); 
} 
int yearr(int year) 
{ 
    if((year%4==0)&&(year/4!=0)) 
    return 1; 
    else 
    return 0; 
} 

После прочтения комментариев я отредактировал мой кодированию как:

#include<stdio.h> 
#include<conio.h> 
int yearr(int year); 
void main(void) 
{ 
    int year; 
    printf("Enter a year:"); 
    scanf("%d",&year); 
    if(!yearr(year)) 
    { 
     printf("It is a leap year."); 
    } 
    else 
    { 
    printf("It is not a leap year"); 
    } 


getch(); 
} 
int yearr(int year) 
{ 
    if((year%4==0) 
    { 
    if(year%400==0) 
    return 1; 
    if(year%100==0) 
    return 0; 
    } 
    else 
    return 0; 
} 
+1

Это работало? Кроме того, важна читаемость кода, поэтому «yearr» является плохим именем для функции, чтобы узнать, год ли високосный год. 'main' возвращает' int' в C, а не 'void'. –

+0

При компиляции пересмотренного кода GCC говорит: 'In function 'yearr': yearr.c: 12: warning: control достигает конца не-void function'. Если вы правильно отпечатаете свой код, вам будет легче понять, почему это так - достаточно сказать, если год делится на 4, а не делится на 100, вы не говорите своему абоненту, действительно ли это високосный год. –

+0

'if (! Yearr (год)) { printf (" Это високосный год. "); } else { printf («Это не високосный год»); } ' Вместо того, чтобы один из приведенных выше ', если (yearr (год)) { Е ("Это не високосный год."); } else { printf ("Это високосный год"); } ' Вам не кажется, что нижеследующее легко понять? –

ответ

14

Ваша логика определения високосного года неверна. Это должно вам начать (из Википедии):

if year modulo 400 is 0 
     then is_leap_year 
else if year modulo 100 is 0 
     then not_leap_year 
else if year modulo 4 is 0 
     then is_leap_year 
else 
     not_leap_year 

x modulo y означает остаток x деленной на y. Например, 12 по модулю 5 равно 2.

1

http://www.wwu.edu/depts/skywise/leapyear.html

Leap Year Правила

Существует високосное каждый год которого число отлично делится на четыре - в течение многих лет, которые, как делятся на 100 и не делится на 400. Второй части лет правил эффектов века, за исключением. Например; годами 1600 и 2000 годов являются високосные годы, но век лет 1700, 1800 и 1900 не являются. Это означает, что три раза из каждых четырехсот лет существует восемь лет между високосными годами.

1
if(year%400 ==0 || (year%100 != 0 && year%4 == 0)) 
    { 
     printf("Year %d is a leap year",year); 
    } 
    else 
    { 
     printf("Year %d is not a leap year",year); 
    } 

Изменить его, как и выше. Также читайте this.

+6

Не стоит предлагать решение домашней проблемы. –

2

Проблема с кодом заключается в том, что вы возвращаете ненулевое значение от yearr, если считаете, что год является високосным годом. Поэтому вам не нужно ! в вашем операторе if.

6
int isLeapYear(int year) 
{ 
    return (year % 400 == 0) || ((year % 100 != 0) && (year % 4 == 0)); 
} 
+0

Почему downvote? :( – st0le

+0

Неэффективность, в первую очередь из-за порядка исполнения. Mod 400 и Mod 100 должны рассчитываться каждый раз, когда срок действия Mod 4 мог бы исключить их в 75% случаев (см. Мой ответ выше). Мало? Да, в схеме вещей. Однако при поиске ссылочного ответа алгоритм Википедии слишком часто повторяется как хороший (и это не очень хорошо). Для некоторых эта деталь не важна, но для меня алгоритм Википедии похож на запись 2/4-го вместо 1/2. –

+0

@ KevinP.Rice, 2-летний вопрос, но я буду укусить. Если вы видите код OP, он явно новичок и никогда не просил об эффективном Учитывая контекст, если бы я был учителем, я бы никогда не рекомендовал OP использовать ваш ответ. Кроме того, я всегда получал бы читаемый код по микро-оптимизированному коду в любой день, но каждому свое. Cheers! – st0le

3

Хотя логика, делящаяся на 400, является безупречной, она не так эффективна с точки зрения вычислений, как деление на 4 в первую очередь. Вы можете сделать это с логикой:

#define LEAPYEAR(y) ((y % 4) == 0 && ((y % 100) != 0 || (y % 400) == 0)) 

Это делится на 4 для каждого значения, но 3/4 из них, тестирование заканчивается там. Для 1/4, которые проходят первый тест, он затем делит на 100, исключая значения 24/25; для остальных 1 из 100 он также делит на 400, придя к окончательному ответу. Конечно, это не огромная экономия.

+0

Вы должны заменить 'y% 4'' y & 3' и заменить 'y% 400' на' y & 15'. Обоснование: модульное деление на некоторых платформах медленное, тогда как один побитовый-И, как правило, один цикл инструкций. Некоторые компиляторы языка выполняют эту оптимизацию автоматически, но ее выкапывание в код гарантирует это. Modulo 400 может быть заменен на '& 15', потому что 16 - коэффициент 400, но не 100 (или 200). Для 8-битных систем «& 15» гораздо легче справиться. Технически «% 100» может быть заменено на '% 25', но это вряд ли улучшится для любой системы. –

+0

@Kevin P. Rice Будет ли «y% 4' с' y & 3' корректно работать, если целые числа не являются дополнением 2 и 'y <0'? – chux

+0

@ KevinP.Rice: Переключение с Джулиана на григорианское немного сложнее, чем «это случилось в 1582 году». Многие римско-католические страны изменились в 1584 году; страны под влиянием Великобритании изменились в 1752 году; другие страны изменились в разное время (Россия в XX веке, Русская православная церковь все еще не изменилась, AFAIK). Проверьте выход из 'cal 9 1752' на машине Unix-ish. Кроме того, для отрицательных значений года вам необходимо бороться с наличием или отсутствием года 0. –

94

Наиболее эффективный тест високосный год:

if ((year & 3) == 0 && ((year % 25) != 0 || (year & 15) == 0)) 
{ 
    /* leap year */ 
} 

Этот код действует в C, C++, C#, Java и многих других C-подобных языках.Код использует один TRUE/FALSE выражение, которое состоит из трех отдельных тестов:

  • четвёртого года тест: year & 3
  • сотый год тест: year % 25
  • четырёхсотый год тест: year & 15

A полное обсуждение того, как работает этот код ниже, но сначала обсуждение алгоритма Википедии вызывается для:

Алгоритм Википедии НЕИСПРАВНОСТЬ/НЕПРЕДНАМЕРЕННЫЙ

Wikipedia опубликовал алгоритм псевдокода (см.: Wikipedia: Leap year - Algorithm), который подвергся постоянному редактированию, мнению и вандализму.

НЕ РЕАЛИЗАЦИЯ АЛГОРИТМА WIKIPEDIA!

Один из старейших стоящих (и неэффективных) алгоритмов Википедии появилась следующим образом:

if year modulo 400 is 0 then 
    is_leap_year 
else if year modulo 100 is 0 then 
    not_leap_year 
else if year modulo 4 is 0 then 
    is_leap_year 
else 
    not_leap_year 

выше алгоритм является неэффективным, потому что он всегда выполняет тесты на 400-й год и 100-й год, даже в течение многих лет, что быстро завершит «тест 4-го года» (тест по модулю 4) —, который составляет 75% времени! Переупорядочивая алгоритм для выполнения теста 4-го года, мы значительно ускоряем процесс.

"НАИБОЛЕЕ ЭФФЕКТИВНЫЙ" псевдокод АЛГОРИТМ

я предоставил следующий алгоритм для Википедии (более одного раза):

if year is not divisible by 4 then not leap year 
else if year is not divisible by 100 then leap year 
else if year is divisible by 400 then leap year 
else not leap year 

Это "наиболее эффективным" псевдо-код просто меняется порядок испытаний, так что сначала происходит деление на 4, за которым следуют менее часто встречающиеся тесты. Поскольку «год» не делит на четыре 75 процентов времени, алгоритм заканчивается только после одного теста в трех из четырех случаев.

Примечание: я боролся различными редактора Википедии, чтобы улучшить алгоритм опубликованный там, утверждая, что многие начинающие — и профессиональные — программистов быстро прибыть на странице Википедии (из-за верхние списки поисковых систем) и осуществлять Википедию псевдокода без каких-либо дальнейших исследований. Редакторы Википедии отреклись от всех попыток, которые я сделал, чтобы улучшить, аннотировать или даже просто записать опубликованный алгоритм. По-видимому, они считают, что найти эффективность - проблема программиста. Это может быть правдой, но многие программисты слишком торопились, чтобы выполнить прочные исследования!

ОБСУЖДЕНИЕ «НАИБОЛЕЕ ЭФФЕКТИВНОЕ» Високосный год ТЕСТ

Побитовое И вместо по модулю:

я заменил два по модулю операций в алгоритме Википедии с помощью операции побитового И операции. Почему и как?

Выполнение расчета по модулю требует деления.Во время программирования ПК часто не так много думать об этом, но при программировании 8-разрядных микроконтроллеров, встроенных в небольшие устройства, вы можете обнаружить, что функция разделения не может быть изначально выполнена процессором. На таких процессорах разделение представляет собой сложный процесс, включающий повторение цикла, смещение битов и добавление/вычитание операций, которые очень медленные. Очень желательно избегать.

Оказывается, что по модулю полномочий двух может быть альтернативно достигнуто с использованием операции побитового И операции (см: Wikipedia: Modulo operation - Performance Issues):

х% 2^п == х & (2^п - 1)

Многие оптимизирующие компиляторы преобразуют такие операции с модулем в побитовое-И для вас, но менее продвинутые компиляторы для меньших и менее популярных процессоров не могут. Побитовое - И представляет собой единую инструкцию для каждого процессора.

Заменяя modulo 4 и modulo 400 тестов с & 3 и & 15 (см ниже: «Факторинг уменьшить математику»), мы можем гарантировать, что самые быстрые результаты кода без использования гораздо медленнее операций деления.

Существует не сила двух, равная 100. Таким образом, мы вынуждены продолжать использовать модульную операцию для 100-летнего теста, однако 100 заменяется на 25 (см. Ниже).

Факторинг упростить математику:

В дополнение к использованию побитовое И для замены по модулю операций, то можно отметить два дополнительных споров между алгоритмом Википедии и оптимизированной выражение:

  • modulo 100 заменяется modulo 25
  • modulo 400 заменяется & 15

100-летний тест использует modulo 25 вместо modulo 100. Мы можем сделать это, потому что 100 факторов равны 2 x 2 x 5 x 5. Поскольку 4-летний тест уже проверяет факторы 4, мы можем исключить этот коэффициент из 100, оставив 25. Эта оптимизация, вероятно, незначительна почти для каждой реализации ЦП (так как и 100, и 25 подходят в 8 бит).

В 400-летнем испытании используется & 15, что эквивалентно modulo 16. Опять же, мы можем это сделать, потому что 400 факторов составляют 2 x 2 x 2 x 2 x 5 x 5. Мы можем устранить коэффициент 25, который проверяется на 100-летнем тесте, оставляя 16. Мы не можем далее уменьшить 16, потому что 8 в 200 раз, поэтому устранение каких-либо факторов создало бы нежелательный позитив на 200-й год.

Оптимизация на 400 лет очень важна для 8-разрядных ЦП, во-первых, потому что она позволяет избежать деления; но, что более важно, поскольку значение 400 - это 9-разрядное число, с которым гораздо сложнее справиться в 8-битном ЦП.

короткого замыкания Логические И/ИЛИ операторов:

В последней, а самое главное, оптимизация используемыми являются короткого замыкания логического элемента И («& &„) и ИЛИ (“||») операторов (см.: Wikipedia: Short-circuit evaluation), которые реализованы на большинстве C-подобных языков. Операторы короткого замыкания названы так потому, что они не удосуживаются оценить выражение с правой стороны, если выражение на левой стороне само по себе определяет результат операции.

Например: Если год 2003, то year & 3 == 0 является ложным. Нет никакого способа, чтобы тесты с правой стороны логического И могли сделать результат истинным, поэтому ничто иное не оценивается.

Выполняя сначала тест на 4-й год, только четвертый тест (простой побитовый-И) оценивается три четверти (75 процентов) времени. Это значительно ускоряет выполнение программы, тем более что оно позволяет избежать деления, необходимого для 100-летнего теста (операция по модулю 25).

ЗАПИСКА СКОБКАХ РАЗМЕЩЕНИЕ

Один из комментаторов чувствовали Скобки были неуместны в моем коде и предложили подвыражения перегруппировать вокруг логического оператора (вместо вокруг логического ИЛИ), следующим образом:

if (((year & 3) == 0 && (year % 25) != 0) || (year & 15) == 0) { /* LY */ } 

Выше неверно. Логический оператор И имеет более высокий приоритет, чем логический ИЛИ, и будет оцениваться сначала с новыми скобками или без них. Скобки вокруг логических аргументов AND не влияют. Это может привести один устранить субгруппировки полностью:

if ((year & 3) == 0 && (year % 25) != 0 || (year & 15) == 0) { /* LY */ } 

Но, в как случаев выше, правая часть логического ИЛИ (год испытание четырёхсотого) оцениваются почти каждый раз (т.е. , годы не делимы на 4 и 100). Таким образом, полезная оптимизация была ошибочно устранена.

Скобки в моем исходном коде реализовать наиболее оптимальное решение:

if ((year & 3) == 0 && ((year % 25) != 0 || (year & 15) == 0)) { /* LY */ } 

Здесь, логическое ИЛИ вычисляется только в течение многих лет делится на 4 (из-за короткого замыкания и). Правая часть логического ИЛИ оценивается только в течение лет, делящихся на 4 и 100 (из-за короткого замыкания ИЛИ).

ПРИМЕЧАНИЕ ДЛЯ C/C++ ПРОГРАММИСТЫ

C/C++ программисты могли бы чувствовать это выражение более оптимизированные:

if (!(year & 3) && ((year % 25) || !(year & 15))) { /* LY */ } 

Это еще не оптимизировано! Хотя явные тесты == 0 и != 0 удаляются, они становятся неявными и все еще выполняются. Хуже того, код больше не действует в строго типизированных языках, таких как C#, где year & 3 оценивает int, но для операторов логического И (&&), ИЛИ (||) и NOT (!) требуются аргументы bool.

+0

@tarabyte Очень добрый. Спасибо. Теперь, если мы сможем заставить Википедию опубликовать лучшую статью для пользы мира ... –

+0

Я понимаю аргумент эффективности, но почему вы помечаете алгоритм Википедии как «ненадежный». В каком случае край он терпит неудачу? –

+4

@EricJ. Википедия ненадежна, потому что она постоянно редактируется анонимными людьми. Я видел неправильные алгоритмы редактирования. WP не является достоверным источником информации и часто ошибочен или ориентирован на мнение. Его следует рассматривать как конвергентный, но не канонический. –

1
 

    #include 
    void main(void) 
    { 
     int year; 
     printf("Enter a year to check if it is Leap Year\n"); 
     scanf("%d",&year); 
     if(year%400==0) /*  Why mod 400 */ 
      printf("%d is a Leap Year\n",year); 
     else if(year%100==0) /*  Why mod 100 */ 
      printf("%d is not a Leap Year\n",year); 
     else if(year%4==0) 
      printf("%d is a Leap Year\n",year); 
     else 
      printf("%d is not a Leap Year\n",year); 

    } 

3

Это может быть правильное решение. Алгоритм, приведенный в Википедии, неверен.

-(BOOL)isLeapYear: (int)year{  

    if(year%4==0){ 
     if(year%100!=0){ 
     return YES; 
    } 
    else if(year%400!=0){ 
     return YES; 
    } 
    else return NO; 
    } 

    else return NO; 
    } 
0

Как и другие упомянутые условия для високосного года неверны. Он должен:

int yearr(int year) 
{ 
    if(((year%4 == 0) && (year%100 !=0)) || (year%400==0)) 
     return 1;  
    else  
     return 0;  
} 

Читайте здесь how to check leap year in C.

-5

#define is_leap(A) !((A) & 3)

Просто убедитесь, что вы не ввели отрицательный год :)

+0

1900 не был високосным годом. https://en.wikipedia.org/wiki/Leap_year#Gregorian_calendar – jogo

+0

Уже было 11 предыдущих ответов, и они дают полное решение. Ваше решение даже не заботится о том, чтобы некоторые случаи года делились на 100 и 400. Таким образом, не ответ, и это должно быть удалено. – phoenix

-1

Рассчитать макс/последний день за месяц: 1..12, год: 1..3999

maxDays = month == 2 ? 
    28 + ((year & 3) == 0 && ((year % 25) != 0 || (year & 15) == 0)) : 
    30 + ((month & 1)^(month > 7)); 
+0

hm- кто-то пропускает отличный трюк от XOR'ing odd/even month OFFSET, вы вряд ли найдете нигде. – user6801759

0

Ответ Kevin обеспечивает оптимальный 8-ти операционный тест (с использованием констант XOR с константами), но если вы ищете что-то более читаемое, попробуйте этот 9-й тестовый тест.

year % 4 == 0 && !((year % 100 == 0)^(year % 400 == 0)) 

Таблица истинности для (year % 100 == 0)^(year % 400 == 0)

       (year % 100 == 0)^(year % 400 == 0) 
100 doesnt divide year  . F 
only 100 divides year  . T 
100 and 400 divides year . F 

Теперь !(year % 100 == 0)^(year % 400 == 0) дает то, что вы хотите.

+0

На самом деле, я использую побитовое-И, а не XOR. Кроме того, я заменяю два по модулю операции (деление) побитовым-И. На многих небольших системах разделение требует многочисленных операций с ЦП, поэтому экономия (по моему мнению) перевешивает небольшую разницу в читаемости. Кроме того, для этого нужны кодовые комментарии. –

1
I used this code: 

#include <stdio.h> 

int main() 
{ 
    int yr; 
    printf ("Enter a year \n"); 
    scanf ("%d", &yr); 

    if (yr%400 == 0) 
     printf("\n LEAP YEAR."); 

    else if (yr%4==0 && yr%100!=0) 
     printf("\n LEAP YEAR."); 
    else 
     printf ("\n NOT LEAP YEAR."); 
} 

 Смежные вопросы

  • Нет связанных вопросов^_^