Наиболее эффективный тест високосный год:
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
.
Это работало? Кроме того, важна читаемость кода, поэтому «yearr» является плохим именем для функции, чтобы узнать, год ли високосный год. 'main' возвращает' int' в C, а не 'void'. –
При компиляции пересмотренного кода GCC говорит: 'In function 'yearr': yearr.c: 12: warning: control достигает конца не-void function'. Если вы правильно отпечатаете свой код, вам будет легче понять, почему это так - достаточно сказать, если год делится на 4, а не делится на 100, вы не говорите своему абоненту, действительно ли это високосный год. –
'if (! Yearr (год)) { printf (" Это високосный год. "); } else { printf («Это не високосный год»); } ' Вместо того, чтобы один из приведенных выше ', если (yearr (год)) { Е ("Это не високосный год."); } else { printf ("Это високосный год"); } ' Вам не кажется, что нижеследующее легко понять? –