27
\$\begingroup\$

Halley's Comet is the only comet that may appear (i.e. become visible to the naked eye on Earth) twice in a human lifetime. The orbital period of Halley's Comet is not constant: it has varied between 75 to 79 Earth years since the first definite apparition was recorded in 240 BCE. This variability is mainly driven by gravitational interactions between the comet and the planets of the Solar System. For the three millennia up to the year 3000, Halley's Comet appeared, or is projected to appear, in the following years:

66, 141, 218, 295, 374, 451, 530, 607, 684, 760, 837, 912, 989, 1066, 1145, 1222, 1301, 1378, 1456, 1531, 1607, 1682, 1758, 1835, 1910, 1986, 2061, 2134, 2209, 2284, 2358, 2430, 2504, 2579, 2653, 2726, 2795, 2863, 2931, 3000

Apparitions beyond 2134 predicted using this orbit simulator. The simulations account for gravitational perturbations due to the planets but not for other effects, such as changes in the comet's outgassing rate, that also affect its orbit. See here for further discussion.

Task

Write a program or function that takes as input a positive integer up to 3000 (inclusive), representing a year, and outputs/returns the number of years until the next apparition of Halley's Comet. In other words, if the input year is \$y\$ and Halley's Comet next appears in year \$y_\text{H}\ge y\$, find \$y_\text{H}-y\$. The shortest code (in bytes) in each language wins.

Test cases

   1 -> 65
1066 ->  0
1067 -> 78
1986 ->  0
1987 -> 74 
2021 -> 40
\$\endgroup\$

16 Answers 16

22
\$\begingroup\$

JavaScript (Node.js), 74 bytes

n=>Buffer("BKMMOMOMMLMKMMOMOMNKLKLMKLKIKKJHJKJIEDDE").every(c=>(n-=c)>0)-n

Try it online!

Commented

n =>              // n = input year
  Buffer(         // build a buffer consisting of
    "BKMM...EDDE" // the year deltas encoded as ASCII codes
  )               // 'B' -> 66, + 'K' (75) -> 141, etc.
  .every(c =>     // for each value c in there:
    (n -= c)      //   subtract c from n
    > 0           //   and go on while the result is positive
  )               // end of every(), which yields false
  - n             // return -n
\$\endgroup\$
12
\$\begingroup\$

05AB1E, 35 bytes

Uses second-order differences to reconstruct the years, which is 1 byte shorter than first-order differences.

•6?É@fô]öÔMƵ4^-•7в4-.¥75+.¥66+I-.Δd

Try it online!

•6...-•                    # compressed integer
       7в                  # convert to base 7
         4-                # subtract 4 from each
           .¥              # un-delta: cumulative sum with a 0 prepended
             75+           # add 75 to each; this is now the list of years between appearances
                .¥         # un-delta
                  66+      # add 66 to each; this is now the list of years of appearances
                     I-    # subtract the input year
                       .Δd # find the first non-negative integer

Try it with step-by-step output!

The part .¥75+.¥66+ can be written with a loop at the same length as „KBÇv.¥y+}.

\$\endgroup\$
7
\$\begingroup\$

R, 87 81 bytes

(z=cumsum(utf8ToInt("BKMMOMOMMLMKMMOMOMNKLKLMKLKIKKJHJKJIEDDE"))-scan())[z>=0][1]

Try it online!

\$\endgroup\$
6
\$\begingroup\$

MATL, 45 44 bytes

66 75'#AeXj=*pq5 Oe>3-u'F7Za4-,hYs]i-t0<~)X<

Try it online! Or verify all test cases.

Explanation

This uses the second-order consecutive differences to compress the list of years.

66                   % Push 66
75                   % Push 75
'#AeXj=*pq5 Oe>3-u'  % Push this string
F7Za                 % Uncompress from base94 (printable ASCII except quote) to base 7
4-                   % Subtract 4. This is the 2nd-order difference of the list of years
,                    % Do twice
  h                  %   Concatenate (first time with 75, second time with 66)
  Ys                 %   Cumulative sum
]                    % End
i-                   % Subtract input element-wise
t                    % Duplicate
0<~                  % Logical index of non-negative values
)                    % Apply index to keep only non-negative values
X<                   % Minimum. Implicitly display 
\$\endgroup\$
6
\$\begingroup\$

Perl 5 -p, 73 bytes

$,=EDDEIJKJHJKKIKLKMLKLKNMOMOMMKMLMMOMOMMKB;$_-=ord chop$,while$_>0;s/-//

Try it online!

Uses the popular ASCII encoding but puts the first appearance (year 66) at the right side.

\$\endgroup\$
0
4
\$\begingroup\$

J, 61 bytes

(3+/\@u:'BKMMOMOMMLMKMMOMOMNKLKLMKLKIKKJHJKJIEDDE')&(]-~I.{[)

Try it online!

Noticed I independently came up with the same long constant Arnauld used in his answer, which is 66 prepended to the differences of the years, converted to Unicode code points u:.

The J code then converts back to a list of numbers 3 u:, and scan sums those numbers +/\@ to recover the original list of comet years.

We then find the "insert before" index I. of the program input within the list of comet years, and subtract the program input ]-~ from the actual comet list value at that same index I.{[.

\$\endgroup\$
3
\$\begingroup\$

Pyth, 85 42 41 bytes

-hg#Qt.u+NYCM+\B."BOÅ>+‘¸h`bʹ¹dtg"Z

Try it online!


Managed to shed 44 bytes by using Arnauld's delta string.

\$\endgroup\$
2
\$\begingroup\$

Wolfram Language (Mathematica), 80 bytes

-Fold[If[#>0,#-#2,#]&,#,36^^9cyu7y4a7m84jq6pihqeoy2jutbhh4~IntegerDigits~15+65]&

Try it online!

The comet should be visible near its periapsis. Unfortunately, CometData only includes the dates of the previous and next periapses (5 Feb 1986 and 31 May 2061).

\$\endgroup\$
2
\$\begingroup\$

Charcoal, 42 bytes

I⌊Φ⁻EφΣE…”$↷↥CⅉWF3⊖n↧JNω∕υ³*H↨{W”⊕ι℅λN¬‹ι⁰

Try it online! Link is to verbose version of code. Also uses @Arnauld's string, although I didn't realise until I read @Jonah's answer. Explanation:

     φ                  Predefined variable 1000 (shorter than 40)
    E                   Map over implicit range
         ”...”          Compressed form of @Arnauld's string
        …               Truncate to length
               ι        Current index
              ⊕         Incremented
       E                Map over characters
                 λ      Current character
                ℅       Take the ordinal
      Σ                 Summed
   ⁻                    Vectorised subtract
                  N     Input year
  Φ                     Filter where
                     ι  Current value
                   ¬‹ ⁰ Is non-negative
 ⌊                      Take the minimum
I                       Cast to string
                        Implicitly print
\$\endgroup\$
2
\$\begingroup\$

Husk, 47 bytes

-⁰ḟ≥⁰∫m+66ΘB14B95mo-30c"(p_45Ux2|!uvrA(C$TDL3W 

Try it online!

No built-in numeric compression in Husk, so we encode the differences between Halley years as printable ASCII values.

Halley year differences are then reconstructed by subtracting 30 from the ASCII values, converting to base 95, getting base 14 digits and adding 66. The Halley years are the cumulative sum of this: altogether ∫m+66ΘB14B95mo-30c"(p_45Ux2|!uvrA(C$TDL3W (where "(p_45Ux2|!uvrA(C$TDL3W " is the encoding string).

We then just retrive the first element greater-or-equal to the input (ḟ≥⁰), and subtract the input from this (-⁰).

\$\endgroup\$
2
\$\begingroup\$

Zsh, 78 bytes

for ((a=-$1;a<0;a+=##$s[++i]))s=BKMMOMOMMLMKMMOMOMNKLKLMKLKIKKJHJKJIEDDE;<<<$a

Try it online!

I've had this answer prepared since this question was in the sandbox and I'm annoyed I missed it being posted

Explanation:

for ((a=-$1;a<0;a+=##$s[++i]))s=...;<<<$a

for ((     ;   ;            ))     ;       # C-style for loop
      a=-$1;                               # initialise `a` to the input, negated
            a<0;                           # while `a` is less than 0
                              s=...;       # set `s` to the string
                        ++i                # increment `i` (starts at 0 implicitly)
                     $s[   ]               # get the `i`th character of `s`
                   ##                      # get the codepoint number of that character
                a+=                        # add that to `a`
                                   ;<<<$a  # then print `a`

Note: Initially I had s=...;for ((...)):;<<<$r - setting s to the string beforehand, and the body of the loop being : (do nothing). But since the body of the loop is executed before the third clause of the loop (the "each iteration" part), and we only use s in that clause, we can put s in the body to save 2 bytes.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Tip: If you 'follow' Sandbox posts you get a notification when the author modifies/deletes it. \$\endgroup\$
    – Dingus
    Jan 7, 2021 at 10:28
2
\$\begingroup\$

Vyxal, 37 bytes

ɾ»«Ẏ⅛Ṁß)∞'₌›ṫh⁰ḟ†ṙ←⁰»12τ68+¦0p66+$Fhε

Try it Online!

\$\endgroup\$
2
  • \$\begingroup\$ ƛC; can simply become vC \$\endgroup\$
    – lyxal
    May 25, 2021 at 11:59
  • \$\begingroup\$ Also ƛ⁰-; can just be ⁰- no? \$\endgroup\$
    – lyxal
    May 25, 2021 at 12:00
1
\$\begingroup\$

05AB1E, 71 bytes

•1Ò}MKX…иv½ι„qā·&à©Dà|ãвìkPn…(l2ßrÏyO;¬VÕÏcw÷©ûëтÎBʒNdãA₄ΩΓ•3001вʒI@}αW

Try it online!

I know there's a shorter 05AB1E answer, but this uses a different method, so I thought it'd be interesting to see the comparison in byte lengths.

Explained

•...•3001в          # Pushed compressed list of all years
          ʒI@}      # Keep only those >= the input year
              αW    # Push the smallest absolute difference amongst those values
\$\endgroup\$
1
\$\begingroup\$

Jelly, 34 bytes

“¡AMzⱮ⁽ƑẸÑ[NṀtR“¦Ṛ’b8+"6FŻ+66ÄḟḶḢ_

Try it online!

How?

“...“¦Ṛ’b8+"6FŻ+66ÄḟḶḢ_ - Link: year
“...“¦Ṛ’                - list of base 250 integers = [18853782274514630143915309466833, 1683]
        b8              - convert to base 8  = [[3, 5, 5, 7, ..., 2, 1], [3, 2, 2, 3]]
            6           - six
          +"            - zip (implicitly wrapped 6 = [6]) with addition
                                             = [[9,11,11,13, ..., 8, 7], [3, 2, 2, 3]]
             F          - flatten            = [ 9,11,11,13, ..., 8, 7,   3, 2, 2, 3]
              Ż         - prepend zero    = [ 0, 9,11,11,13, ..., 8, 7,   3, 2, 2, 3]
               +66      - add 66          = [66,75,77,77,79, ...,74,73,  69,68,68,69]
                  Ä     - cumulative sums = [66,141,218,295,374, ...,2653,2726,2795,2863,2931,3000]
                    Ḷ   - lowered range (year) = [0,1,2,...,year-1] 
                   ḟ    - filter discard  - e.g. if year = 2860 then [2863,2931,3000]
                     Ḣ  - head                                        2863
                      _ - subtract (year)                                3

FWIW a port of ovs's 05AB1E answer using the second-difference (and also using the filter-out-lowered range technique above) is also 34:

“¬V>ƭṘḌƘH*O⁵i²Ọ’b7_4ÄŻ+75ÄŻ+66ḟḶḢ_
\$\endgroup\$
0
1
\$\begingroup\$

Core Maude, 266 263 262 204 bytes

mod H is pr LIST{Nat}. op n : Nat ~> Nat . vars A B C : Nat . eq n(A)= n(A 66
5710855311372188087909795024940117969860147607). eq n(A B C)= if A > B then
n(A(B +(C & 15)+ 68)(C >> 4))else sd(A,B)fi . endm

Example Session

Maude> red n(1) .  --- Ex: 65
result NzNat: 65
Maude> red n(1066) .  --- Ex: 0
result Zero: 0
Maude> red n(1067) .  --- Ex: 78
result NzNat: 78
Maude> red n(1986) .  --- Ex: 0
result Zero: 0
Maude> red n(2021) .  --- Ex: 40
result NzNat: 40
Maude> red n(3000) .  --- Ex: 0
result Zero: 0

Ungolfed

mod H is
    pr LIST{Nat} .

    op n : Nat ~> Nat .

    vars A B C : Nat .

    eq n(A) = n(A 66 5710855311372188087909795024940117969860147607) .
    eq n(A B C) =
        if A > B
            then n(A (B + (C & 15) + 68) (C >> 4))
            else sd(A, B)
        fi .
endm

The answer is obtained by reducing the function n, which takes the input year.

The large number encodes the differences between successive years (e.g., 141 - 66 = 75) minus 68, packed into a bitstring with 4 bits per value.


Shaved off 3 bytes by replacing two conditional equations with a single equation using if_then_else_fi.

Saved one more byte by deduplicating a long enough repeated sublist using a condition.

Saved 58 bytes by encoding the year differences as a bitstring (and a few other tweaks).

\$\endgroup\$
1
\$\begingroup\$

Python 2, 83 bytes

f=lambda y,t=0x46ba1298ab6f2234037bc64abb247ba8e61402:f(y-t%14-66,t/14)if y>0else-y

Try it online!

Stores the differences between the years (minus 66) as digits in a base-14 number t. Repeatedly subtracts the differences from the given year y until we gets a non-positive number (overshoot), in which case the answer is -y.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.