19
\$\begingroup\$

In musical notation, groups of notes shorter than one beat are joined together by a line at the bottom called a beam. Here are a few bars of music with the beams highlighted:

enter image description here

(Taken from Second Suite in F by Gustav Holst; you can hear how these notes sound at 0:13 in this video.)

You may notice that beam #6 is above the ovals that define the notes, and its vertical lines attached to their right sides, while the others are below the notes and attached to their left. What determines this? There are a few simple rules, which are the subject of this challenge.

  1. By default, the placement of the beam is determined by its highest or lowest note. If the note farthest from the centermost line of the staff is above it, then the beam goes below the notes. If the note with the highest absolute value is above the centermost line of the staff, then the beam goes below the notes.
  2. If the highest and the lowest note are the same distance from the center line, the direction of the beam is determined by which side of the center line the majority of notes in the beam lie on. If a majority of notes lie above the centre line, the beam goes down. If a majority of notes lie below the centre line, the beam goes up. Notes on the center line do not count either way.
  3. If the highest and the lowest note are the same distance from the center line, and equally many notes lie above and below the center line, the beam may be placed either above or below the notes.

In the sample notation above, the beams of clusters #1, 2, 3, and 5 are determined to be below the notes because their highest notes (their second, second, first, and third respectively) are above the center line. #6's lowest note is its first/third note, which is below the center line. #4's highest and lowest note are equally distant from the center line and it contains no majority either way. Its beam could be placed either above or below, but my composition program renders it as below.

Your commission

Write a program that takes in a list of integers, which may be zero or negative, and outputs a truthy/falsey value. These integers represent notes and their distance from the center line; positive values represent notes above the line, zero represents notes on the line, and negative values represent notes below the line. Determine, given a cluster of notes that sit those distances from the center line, which way the cluster's beam should go; output TRUE if the beam should go above the notes and FALSE if the beam should go below the notes. To restate the rules above and give the algorithm:

  1. Find the number in the list with the largest absolute value. If it is greater than zero, output FALSE. If it is less than 0, output TRUE. If it is 0, output may be TRUE or FALSE as convenient. If there are positive and negative numbers with the same, maximal absolute value, continue.
  2. Determine whether the majority of numbers are positive and negative, ignoring zeroes. If a majority of numbers are positive, output FALSE. If a majority of numbers are negative, output TRUE.
  3. Otherwise, output may be TRUE or FALSE as convenient.

You might be given only one number. In that case, it has the highest absolute value, so if it is greater than 0 output false; if it is less than zero output true; and if it is 0 you may output true or false.

You may not assume the numbers are sorted.

Test cases

5, 4, 3 -> FALSE                     # #3
1, 0, -1 -> TRUE or FALSE            # #4
0, 1, 2, 0 -> FALSE                  # #5
-2, -1, -2 -> TRUE                   # #6
3, 1, 4, 1, 5, -9 -> TRUE            # Largest absolute value below 0, rule 1
3, 1, 4, 1, 5, -5 -> FALSE           # Largest absolute value tied, determined by majority, rule 2
3, 1, -3, -1, 5, -5 -> TRUE or FALSE # Largest absolute value tied and no majority, unspecified
\$\endgroup\$
0

13 Answers 13

10
\$\begingroup\$

R, 40 bytes

function(x)min(x)+max(x)+mean(sign(x))<0

Try it online!

If min(x)+max(x) is non-zero, its sign corresponds to the note farthest from the centre and gives the direction of the beam.

Adding mean(sign(x)) can never change the sign. Indeed, either (a) all notes are of the same sign and mean(sign(x)) is of the same sign as min(x)+max(x) or (b) notes are of both signs and mean(sign(x)) is strictly in \$(-1,1)\$ whereas min(x)+max(x) is an integer, so the sign of the sum is unchanged.

If min(x)+max(x)==0, the sign of the mean decides the direction.

If undecided, defaults to FALSE.

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

Python 3, 55 bytes

lambda a:(min(a)+max(a)or sum((i>0)-(i<0)for i in a))<0

Try it online!

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

JavaScript (ES6),  66  64 bytes

a=>(a.map(m=v=>(k+=v>0||-!!v,a=v>a?a:v,m=v<m?m:v),k=0)|a+m||k)<0

Try it online!

Commented

a => (                   // a[] = input array, re-used to store the minimum value
  a.map(m =              // initialize m = maximum to a non-numeric value
    v => (               // for each value v in a[]:
      k +=               //   update k:
        v > 0 ||         //     increment it if v > 0
        -!!v,            //     decrement it if v < 0
      a = v > a ? a : v, //   update a to min(a, v) (always v if a is non-numeric)
      m = v < m ? m : v  //   update m to max(m, v) (always v if m is non-numeric)
    ),                   //
    k = 0                //   start with k = 0
  )                      // end of map()
  | a + m                // use a + m if it's not equal to 0
  || k                   // otherwise, use k
) < 0                    // test if the above value is negative
\$\endgroup\$
1
  • 1
    \$\begingroup\$ 63 bytes with with Math \$\endgroup\$
    – tsh
    Jan 17, 2020 at 7:38
2
\$\begingroup\$

Jelly, 13 9 bytes

ṠÆm+Ṁ+Ṃ<0

Try it online!

A monadic link taking a list of integers and returning a Jelly Boolean indicating the direction of the stems as per the spec.

Modified with inspiration from @RobinRyder’s excellent R answer so be sure to upvote that one too!

Explanation

Ṡ         | Signs
 Æm       | Mean
   +Ṁ     | Add max (of original argument)
     +Ṃ   | Add min (of original argument)
       <0 | Less than 0
\$\endgroup\$
2
\$\begingroup\$

05AB1E, 15 14 13 bytes

ÄZQÏ‚¤Ëè.±Od≠

Output will be false if either of the two is a possible output.

-1 byte and fixed a bug for [0,0,1,-1,-1] thanks to @Grimmy.

Try it online or verify all test cases.

Explanation:

Ä         # Convert each value in the (implicit) input-list to their absolute value
          #  i.e. [3,1,-5,4,1,5,-5] → [3,1,5,4,1,5,5]
          #  i.e. [0,1,-2,0] → [0,1,2,0]
 Z        # Push the maximum of these absolute values (without popping the list itself)
          #  i.e. [3,1,5,4,1,5,5] → 5
          #  i.e. [0,1,2,0] → 2
  Q       # Check for each absolute value whether it's equal to this maximum
          #  i.e. [3,1,5,4,1,5,5] and 5 → [0,0,1,0,0,1,1]
          #  i.e. [0,1,2,0] and 2 → [0,0,1,0]
   Ï      # And only leave the values of the (implicit) input-list at the truthy indices
          #  i.e. [3,1,-5,4,1,5,-5] and [0,0,1,0,0,1,1] → [-5,5,-5]
          #  i.e. [0,1,-2,0] and [0,0,1,0] → [-2]
    ‚     # Pair it with the (implicit) input-list
          #  i.e. [3,1,-5,4,1,5,-5] and [-5,5,-5] → [[3,1,-5,4,1,5,-5],[-5,5,-5]]
          #  i.e. [0,1,-2,0] and [-2] → [[0,1,-2,0],[-2]]
     ¤    # Push the last value of this pair (without popping) again
      Ë   # And check if all values are equal (1 if truthy; 0 if falsey)
          #  i.e. [-5,5,-5] → 0
          #  i.e. [-2] → 1
       è  # Use it to index into the pair
          #  i.e. [[3,1,-5,4,1,5,-5],[-5,5,-5]] and 0 → [3,1,-5,4,1,5,-5]
          #  i.e. [[0,1,-2,0],[-2]] and 1 → [-2]
.±        # Take the signum of each value
          #  i.e. [3,1,-5,4,1,5,-5] → [1,1,-1,1,1,1,-1]
          #  i.e. [-2] → [-1]
  O       # Sum those together
          #  i.e. [1,1,-1,1,1,1,-1] → 4
          #  i.e. [-1] → -1
   d      # Check if it's non-negative (>=0)
    ≠     # And invert the boolean (so basically check if it's negative / <0)
          #  i.e. 4 → 1 → 0
          #  i.e. -1 → 0 → 1
          # (after which this is output implicitly as result)
\$\endgroup\$
5
  • 1
    \$\begingroup\$ This gives incorrect output on lists such as [1,2,3,-3,-3], where the multiplicity of signs of maxima does not match the general multiplicity of signs. \$\endgroup\$ Jan 17, 2020 at 11:23
  • \$\begingroup\$ @LegionMammal978 Should be fixed now, thanks for letting me know. As I mentioned, I misread rule 2 and thought it only applied to the abs(max) values instead of the full list. \$\endgroup\$ Jan 17, 2020 at 12:19
  • 1
    \$\begingroup\$ d treats 0 as positive, which makes you fail test cases like [0,0,1,-1,-1]. Using .±Od instead of d.M fixes it at the cost of 1 byte. \$\endgroup\$
    – Grimmy
    Jan 17, 2020 at 13:28
  • \$\begingroup\$ 13 with ÄZQÏ‚¤Ëè.±Od≠. \$\endgroup\$
    – Grimmy
    Jan 17, 2020 at 13:35
  • \$\begingroup\$ @Grimmy Ah, good catch regarding the [0,0,1,-1,-1]. And thanks for the -2 with the Ë and indexing into the pair. :) \$\endgroup\$ Jan 17, 2020 at 13:48
1
\$\begingroup\$

Retina 0.8.2, 58 bytes

O#`\S+
\d+
$*
+` -1+ +1+ 
 
^-(1+)($| ((?!.*\1$)|-.* \1$))

Try it online! Link includes test cases. Explanation:

O#`\S+

Sort the numbers numerically.

\d+
$*

Convert to unary.

+` -1+ +1+ 
 

Match inner pairs of negative and positive numbers and delete them until only one negative and/or positive number remains.

^-(1+)($| ((?!.*\1$)|-.* \1$)

Output a truthy value if the (largest) negative number is the only number, or if its absolute value is greater than the largest positive number, or if another negative number remains and its absolute value equals the largest positive number.

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

PHP, 178 127 111 107 105 bytes

<?php foreach($argv as$n){$c+=$s=$n<=>0;($a=$n*$s)>$m&&$x=$s;$a-$m||$x=0;$a>=$m&&$m=$a;}echo$x?$x<0:$c<0;

Try it online!

Ungolfed

<?php
$notes = explode(',', $argv[1]);

$max_note = 0;
$max_sign = 0;
$counts = 0;
foreach ($notes as $note) {
    $sign = $note <=> 0;
    $counts += $sign;

    if (abs($note) > $max_note) {
        $max_sign = $sign;
    } elseif (abs($note) == $max_note) {
        $max_sign = 0;
    }

    if (abs($note) >= $max_note) {
        $max_note = abs($note);
    }
}

echo ($max_sign == 0) ? ($counts < 0) : ($max_sign == -1);
?>
\$\endgroup\$
1
\$\begingroup\$

Wolfram Language (Mathematica), 26 bytes

Min@#+Max@#+Mean@Sign@#<0&

Try it online! Pure function. Takes a list of numbers as input, and returns True or False as output. Uses the same logic as Robin Ryder's answer. Additionally, here's the 49-byte function I came up with myself:

Tr[{}⋃Sign@MaximalBy[#,Abs]/.{-1,1}->Sign@#]<0&
\$\endgroup\$
1
\$\begingroup\$

J, 17 bytes

0>>./+<./+#%~1#.*

Try it online!

Based on Robin Ryder's insightful alrogithm - make sure to upvote his.

  • 0> Is 0 greater than...
  • >./ The max...
  • + Plus...
  • <./ The min...
  • + Plus...
  • # The input length...
  • %~ Divided into...
  • 1#. The sum of...
  • * The signs.
\$\endgroup\$
1
\$\begingroup\$

Burlesque, 23 bytes

psJ)snav/^>]j<]CL++@0.<

Try it online!

Following the logic of @RobinRyder. Takes input as A B C ...

psJ  # Parse input to list of ints and dup
)sn  # Map sign
av   # Average (returns double)
/^   # Swap and Dup
>]   # Max
j<]  # Min
CL++ # Collect all and sum
@0.< # <0.0
\$\endgroup\$
1
\$\begingroup\$

C (gcc), 118 115 109 bytes

#define A a[p]
p;s;q;r;f(a,t)int*a;{p=t;for(q=r=*a;p--;s+=(A>0)-(A<0))q=q>A?q:A,r=r<A?r:A;a=q+r+(s>0)<(s<0);}

Uses Robin Ryder's method.

Try it online!

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

Perl 6 Raku, 31 bytes

{0>.min+.max+sum($_».sign)/$_}

Try it online!

Based on Robin Ryder's solution.

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

Factor, 44 bytes

[ dup minmax + swap [ sgn ] map mean + 0 < ]

Try it online!

Port of Robin Ryder's R answer.

\$\endgroup\$

Your Answer

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

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