Calculating future occurrences of Friday the 13th

前端 未结 7 1629
庸人自扰
庸人自扰 2020-12-15 12:48

I\'d like to be able to start with a year, and calculate occurrences of Friday the 13th. A brute force solution is easy and obvious. I have something slightly better, but

相关标签:
7条回答
  • 2020-12-15 13:25
    initialize startDate to 13th of the month given in the current year
    while (true) {
        if (startDate.dayOfWeek == Date.FRIDAY)
             break;
        else
             startDate.year ++;
    }
    return startDate.year;
    
    0 讨论(0)
  • 2020-12-15 13:27

    Any month that starts with a Sunday has a Friday on the thirteenth. There are only 14 combinations possible knowing what day the first of the year is on (with or without leap year, and sun-sat). You should just calculate it once and get it over with. You'd only check 14*12 possible months to start out with, well with in reason.

    resultant table element (from 2009, 2010):

    [Thursday,false] => Feb, March, Nov
    [Friday,false] => Aug
    

    to fill the table you have a generic month Jan(31),Feb(28).. and then iterate with a seed of each day of the week, noting months that start with sunday, and also with a leap year and without. Pretty straight forward, and once done, you can share it with us :)

    0 讨论(0)
  • 2020-12-15 13:27

    Since your brute force algorithm is apparently the intuitive day-by-day iteration option, perhaps you haven't considered the Doomsday Algorithm. It would allow you to simply check if that 13th is a Friday. As far as I know, it is the most efficient solution to the problem.

    0 讨论(0)
  • 2020-12-15 13:36

    I use PowerShell 7.1 x64 on Windows 10, I am also interested in this, though my programming skill is not advanced enough to develop an independent and complex algorithm (without built-in) to solve this, currently I can do this:

    $start=Read-Host "Input start year"
    $end=Read-Host "Input end year"
    $years=$start..$end
    $blackfriday=@()
    foreach ($year in $years) {
        $blackfriday+=1..12 | foreach { [datetime]"$year-$_-13" } | where { $_.DayOfWeek -eq 'Friday'} | foreach {$_.tostring("yyyy, MMMM dd, ffffdd")}
    }
    $blackfriday
    

    Update: Now I have this, by brute forcing year-month-13 to convert date to days, and get the results where days mod 7 equal to 5, I have used all the advanced algorithms found on Wikipedia: https://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week And I found out they don't work well with PowerShell, don't know if it's just me or Wikipedia got it wrong, anyway here is my script, I have fc compared the results of the script with builtin and this script, and it shows"FC: no differences encountered":

    $months=@(
    [PSCustomObject]@{Month='January';Days=31}
    [PSCustomObject]@{Month='February';Days=28}
    [PSCustomObject]@{Month='March';Days=31}
    [PSCustomObject]@{Month='April';Days=30}
    [PSCustomObject]@{Month='May';Days=31}
    [PSCustomObject]@{Month='June';Days=30}
    [PSCustomObject]@{Month='July';Days=31}
    [PSCustomObject]@{Month='August';Days=31}
    [PSCustomObject]@{Month='September';Days=30}
    [PSCustomObject]@{Month='October';Days=31}
    [PSCustomObject]@{Month='November';Days=30}
    [PSCustomObject]@{Month='December';Days=31}
    )
    function BlackFriday {
        param(
        [Parameter(ValueFromPipeline=$true, Mandatory=$true, Position=0)] [int64] $start,
        [Parameter(ValueFromPipeline=$true, Mandatory=$true, Position=1)] [int64] $end
        )
        $years=$start..$end
        $blackfriday=@()
        foreach ($year in $years) {
            $array=1..12
            foreach ($arra in $array) {
                $month=0
                for ($i=0;$i -lt $arra-1;$i++) {
                    $month+=$months[$i].Days
                }
                [int]$ye=$year
                if ($arra -le 2) { $ye-=1}
                if ($ye % 4 -eq 0) {$leap=$ye/4}
                else {while ($ye % 4 -ne 0) {$ye-=1}
                $leap=$ye/4}
                if ($ye % 100 -eq 0) {$century=$ye/100}
                else {while ($ye % 100 -ne 0) {$ye-=4}
                $century=$ye/100}
                if ($ye % 400 -eq 0) {$cycle=$ye/400}
                else {while ($ye % 400 -ne 0) {$ye-=100}
                $cycle=$ye/400}
                $leap=$leap-$century+$cycle
                $date=[int64](($year-1)*365+$month+13+$leap)
                if ($date % 7 -eq 5) {
                    $name=$months[$arra-1].Month
                    $blackfriday+=[string]"$year, $name 13, Friday"
                }
            }
        }
        $blackfriday
    }
    $start=Read-Host "Input start year"
    $end=Read-Host "Input end year"
    BlackFriday $start $end
    
    0 讨论(0)
  • 2020-12-15 13:37

    One thing I noticed is that the first of the month falls on a Sunday during months with a Friday the 13th. You can probably leverage this to make it easier to calculate.

    0 讨论(0)
  • 2020-12-15 13:38

    This is how I would do it:

    • Assume year is known and is an integer.

    • Loop from 1 to 12

      • Create date with loop index, year and 13 for the day

        • Determine day of week as per established algorithms

        • If day of week calculated above is Friday, do your work

    If you want to start with a month and year (you have to assume some sort of year), your algorithm becomes

    • Assume year is known and an integer

    • Assume month is known and is an integer

    • Loop

      • Create date with index of loop as year, known month variable, and 13 for the day

      • Determine day of week as per established algorithms

      • If day of week calculate above is Friday, return date, else

      • Else increment year by 1

    0 讨论(0)
提交回复
热议问题