Відсоток робочих днів у місяці


11

З огляду на рік і місяць, дізнайтеся відсоток робочих днів у вказаному місяці. Робочі дні з понеділка по п’ятницю, незалежно від свят та інших особливих речей. Використовується григоріанський календар.

Вхідні дані

Рік і місяць у форматі ISO 8601 (РРРР-ММ). Рік завжди має чотири цифри, місяць завжди має дві цифри. Даний рік не буде до 1582 року.

Вихідні дані

Вихід - це відсоток робочих днів (згідно з вищезазначеним визначенням) у даному місяці, округлений до цілого числа. Ні знак знаку відсотка, ні дробові цифри не відповідають.

Зразок 1

Input                Output

2010-05              68

Зразок 2

Input                Output

2010-06              73

Зразок 3

Input                Output

1920-10              68

Зразок 4

Input                Output

2817-12              68

Минув тиждень, відповідь прийнято. Для цікавих, розміри робіт, які ми отримали на нашому конкурсі:

129 - оболонка Z
174 - VB.NET
222 - C
233 - C
300 - C

А також наші власні (необроблені) рішення:

  75 - PowerShell
  93 - Ruby
112 - оболонка Борна


2
Я аспірант, тож ...echo 100
Аморі

Навіть студенти не можуть уникнути фундаментальних визначень у своїй роботі. І я по-різному визначив робочі дні ;-)
Джой

Відповіді:


4

64-розрядний Perl, 67 68

Perl 5.10 або пізнішої версії, запустіть з perl -E 'code here'абоperl -M5.010 filename

map{$d++,/^S/||$w++if$_=`date -d@ARGV-$_`}1..31;say int.5+100*$w/$d

Концесії на розмір коду:

  • чутливий до локальної точки зору: він вважає робочими днями дні, dateвихід яких не починається з великої літери S. Запустіть, LC_ALL=Cякщо є сумніви.
  • Вихід є чистим і добре відформатованим, але там "сміття" на stderr на місяці коротші, ніж 31. 2> /dev/nullЯкщо засмучений.
  • чомусь моя версія dateвважає 2817-12 недійсним місяцем. Хто знав, GNU новий апокаліпсис належить! Потрібна 64-бітна збірка dateдля дат після 2038 р. (Спасибі Джої)

1
Мабуть, це було скасовано "Сифосними Гемами" за часів його правління. ref "Нова історія Священної Біблії"
Мартін Йорк

1
Чи щорічно після 2038 року порушується? Тоді переключення 64-бітної збірки може допомогти через деякий настрій розуму при обробці дат ;-)
Joey

@Joey саме так. Дякую за пораду!
JB

JB: Був лише здогадкою, і я насправді не очікував, що нічого, крім C, все ще використовуватиме лише 32-бітні цілі числа, що рахують секунди з дивної епохи. Хоча, чесно кажучи, я поставив там вимогу про дати> 2038 саме для цієї мети ;-)
Джої,

3

PHP - 135

Я зробив це в PHP, тому що я мав подібну проблему лікувати кілька днів тому.

<?php $a=array(2,3,3,3,2,1,1);$t=strtotime($argv[1]);$m=date(t,$t);echo round((20+min($m-28,$a[date(w,strtotime('28day',$t))]))/$m*100)

(Дещо) Чіткіше і без попереджень про константи, що використовуються як рядки:

<?php
date_default_timezone_set('America/New_York');
$additionalDays = array(2, 3, 3, 3, 2, 1, 1);
$timestamp = strtotime($argv[1]);
$daysInMonth = date('t', $timestamp);
$limit = $daysInMonth - 28;
$twentyNinthDayIndex = date('w', strtotime("+28 days", $timestamp));
$add = $additionalDays[$twentyNinthDayIndex];
$numberOfWorkDays = 20 + min($limit, $add);
echo round($numberOfWorkDays / $daysInMonth * 100);
?>

Це стає можливим за допомогою дуже простого алгоритму для обчислення кількості робочих днів у місяці: перевірити наявність будня 29-го, 30-го та 31-го числа (якщо такі дати існують) та додати 20.


Чудовий алгоритм, поганий гольф. Використовуючи сучасний PHP 5.3.5 і -R, цей підхід можна перетворити на 86 байт (63,7%): $a="2333211";echo.5+min(-8+$m=date(t,$t=strtotime($argn)),20+$a[date(w,$t)])/$m*100|0; Дивіться кроки з гольфу.
Тит

80 байт:<?=.5+min(-8+$m=date(t,$t=strtotime($argn)),20+(5886>>date(w,$t)*2&3))/$m*100|0;
Тіт

2

Пітон 152 символів

from calendar import*
y,m=map(int,raw_input().split('-'))
c=r=monthrange(y,m)[1]
for d in range(1,r+1):
 if weekday(y,m,d)>4:c-=1
print '%.f'%(c*100./r)


2

Windows PowerShell, 80

$x=$args;1..31|%{"$x-$_"|date -u %u -ea 0}|%{$a++
$b+=!!($_%6)}
[int]($b*100/$a)

Ви впевнені, що [int]насправді круги? Я схильний би вважати це підлогами.
zneak

@zneak: PowerShell не є мовою, похідною від С. Він використовує режим округлення за замовчуванням .NET, який є «круглим до найближчого навіть цілого числа». Просто спробуйте: І те, [int]1.5і [int]2.5врожайність 2. Така точна поведінка часто спричиняє проблеми в завданнях, де необхідний додатковий поділ (який тоді вимагає додаткової кількості [Math]::Floor()), але в цьому випадку це не зашкодить і «кругле до рівного» застосовується лише до чисел, які закінчуються, у .5яких тут не може статися.
Джої

Якщо ти впевнений, то я тобі вірю. Я просто очікував, що він працюватиме як C # замість цього, і я не маю жодної машини Windows, на якій можна перевірити вдома.
zneak

@zneak: Ні, точно не працює, як у C #. Щось на зразок [int]PowerShell, як правило, більше конверсія, ніж акторський склад :-). Такі речі, як [int[]][char[]]'abc'робота, яку ви не можете змусити працювати багатьма іншими мовами.
Джої

Necrobump but $input-> $argsзберігає байт.
Веска

1

Д: 186 персонажів

auto f(S)(S s){auto d=Date.fromISOExtendedString(s~"-28"),e=d.endOfMonth;int n=20;while(1){d+=dur!"days"(1);if(d>e)break;int w=d.dayOfWeek;if(w>0&&w<6)++n;}return rndtol(n*100.0/e.day);}

Більш розбірливо:

auto f(S)(S s)
{
    auto d = Date.fromISOExtendedString(s ~ "-28"), e = d.endOfMonth;
    int n = 20;

    while(1)
    {
        d += dur!"days"(1);

        if(d > e)
            break;

        int w = d.dayOfWeek;

        if(w > 0 && w < 6)
            ++n;
    }

    return rndtol(n * 100.0 / e.day);
}

1

Пітон - 142

from calendar import*
y,m=map(int,raw_input().split('-'))
f,r=monthrange(y,m)
print'%.f'%((r-sum(weekday(y,m,d+1)>4for d in range(r)))*100./r)

Завдяки fR0DDY за біт календаря.


1

Рубі, 124 119 111

require 'date'
e=Date.civil *$*[0].split(?-).map(&:to_i),-1
p ((e+1<<1..e).count{|d|d.cwday<6}*1e2/e.day).round

Потрібен Ruby 1.9 через те, що було виділено рік та місяць перед аргументом -1 "день" та ?-для "-". Для Ruby 1.8 ми повинні додати 2 символи:

require 'date'
e=Date.civil *$*[0].split('-').map(&:to_i)<<-1
p ((e+1<<1..e).count{|d|d.cwday<6}*1e2/e.day).round

Редагувати : Поголіть п'ять символів на основі допомоги @ Догберта.
Редагувати : Поголіть ще вісім символів на основі допомоги @ steenslag.


Чому ви призначаєте дату D?
Догберт

@Dogbert Whoops! Голдовер з часів, коли у мене було два Date.civils; Дякую!
Фрогз

'-'можна було записати як ?-у Ruby 1.9
Dogbert

@Dogbert Nice. Я теж кину це. Я вважаю, що повинен бути коротший спосіб вибору днів тижня, але я його ще не знайшов.
Фрогз

e + 1 << 1 на три коротше, ніж ee.day + 1
steenslag

1

PHP 5.2, 88 байт

Хоча я вже гольфував рішення знука до 85 байт (я тільки що знайшов ще один), ось моє власне:
я сумніваюся, що я можу видавити тут ще три байти.

$a=_4444444255555236666304777411;echo$a[date(t,$t=strtotime($argn))%28*7+date(N,$t)]+67;

приймає дані від STDIN: Запустити echo <yyyy>-<mm> | php -nR '<code>'.

Рядок $aвідображає дні на місяць ( date(t)) та тиждень день першого дня місяця ( date(N): понеділок = 1, неділя = 7) до відсотка робочих днів-67; strtotimeперетворює вхід у часову позначку UNIX; решта коду робить хешування.

+1 байт для старшого PHP 5: Замініть Nна wі $a=_...;з $a="...".
ще +3 байти для PHP 4: вставити .-1після $argn.

-5 байт для PHP 5.5 або новішої версії (після виклику):
видаліть усе раніше echoі замініть $aна "4444444255555236666304777411".


Ну ... один байт: %7замість %28.
Тит

0

Ребол - 118 113

w: b: 0 o: d: do join input"-01"while[d/2 = o/2][if d/7 < 6[++ w]++ b d: o + b]print to-integer round w / b * 100

Безголівки:

w: b: 0 
o: d: do join input "-01"
while [d/2 = o/2] [
    if d/7 < 6 [++ w]
    ++ b
    d: o + b
]
print to-integer round w / b * 100

0

C #, 158 байт

s=>{var d=DateTime.Parse(s);int i=0,t=DateTime.DaysInMonth(d.Year,d.Month),w=0;while(i<t)w-=-(int)d.AddDays(i++).DayOfWeek%6>>31;return Math.Round(1e2*w/t);};

Анонімний метод, який повертає необхідний відсоток.

Повна програма з невміленими коментованими методами та тестовими прикладами

using System;

class WorkingDayPercentage
{
    static void Main()
    {
        Func <string, double> f =
        s =>
        {
            var d = DateTime.Parse(s);                      // extracts a DateTime object from the supplied string
            int i = 0,                                      // index variable
                t = DateTime.DaysInMonth(d.Year, d.Month),  // number of total number of days in the specified month
                w = 0;                                      // number of working days in the month

            while (i < t)                                   // iterates through the days of the month
                w -= -(int)d.AddDays(i++).DayOfWeek%6 >> 31;// d.AddDays(i) is the current day
                                                            // i++ increments the index variable to go to the next day
                                                            // .DayOfWeek is an enum which hold the weekdays
                                                            // (int)..DayOfWeek gets the days's index in the enum
                                                            // note that 6 is Saturday, 0 is Sunday, 1 is Monday etc.
                                                            // (int)DayOfWeek % 6 converts weekend days to 0
                                                            // while working days stay strictly positive
                                                            // - changes the sign of the positive numbers
                                                            // >> 31 extracts the signum
                                                            // which is -1 for negative numbers (working days)
                                                            // weekend days remain 0
                                                            // w -= substracts the negative numbers
                                                            // equivalent to adding their modulus

            return Math.Round(1e2 * w / t);                 // the Math.round function requires a double or a decimal
                                                            // working days and total number of days are integers
                                                            // also, a percentage must be returned
                                                            // multiplying with 100.0 converts the expression to a double
                                                            // however, 1e2 is used to shorten the code
        };

        // test cases:
        Console.WriteLine(f("2010-05")); // 68
        Console.WriteLine(f("2010-06")); // 73
        Console.WriteLine(f("1920-10")); // 68
        Console.WriteLine(f("2817-12")); // 68
    }
}

Альтернативна функція, яка додає від’ємні значення до кількості робочих днів, змінюючи знак у відповідь без зайвих витрат на байт:

s=>{var d=DateTime.Parse(s);int i=0,t=DateTime.DaysInMonth(d.Year,d.Month),w=0;while(i<t)w+=-(int)d.AddDays(i++).DayOfWeek%6>>31;return-Math.Round(1e2*w/t);};

0

Oracle SQL, 110 байт

select round(100*sum(1-trunc(to_char(x+level-1,'d')/6))/sum(1))from dual,t connect by level<=add_months(x,1)-x

Це працює з припущенням, що вхідні дані зберігаються в таблиці при використанні dateтипу даних, наприклад

with t as (select to_date('2010-06','yyyy-mm') x from dual)

0

APL (Dyalog Unicode) , 55 байт SBCS

Функція анонімного мовчазного префікса.

.5+100×2÷/(2 5)2{≢⍎⍕↓⍺↓¯2' ',cal' '@5⊢⍵⊣⎕CY'dfns'}¨⊂

 додайте дату, щоб розглядати її в цілому

(2 5)2{ Застосувати до цього наступну функцію, але з лівими аргументами [2,5]та 2:

⎕CY'dfns' скопіюйте бібліотеку "dfns"

⍵⊣ відкиньте звіт на користь дати

' '@5⊢ замініть 5-й символ ( -) пробілом

 виконати це, щоб отримати двоелементний список

cal викликати функцію календаря на цьому

' ', додайте до цього стовпчик пробілів

¯2⌽ обертайте останні два стовпці (субота) вперед

⍺↓ падіння лівого аргументу кількість рядків (2, заголовки) та стовпців (якщо вказано; 5 = Sat + Sun)

 розділити матрицю на список рядків

 формат (згладжується із вставкою подвійного інтервалу)

 виконати (перетворює залишилися числа днів у плоский числовий список)

 підрахуйте ті

2÷/ розділити кожну пару (є лише одна)

100× помножити на сотню

.5+ додати половинку

 підлогу

Спробуйте в Інтернеті!


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.