24
\$\begingroup\$

Background

Bathroom Etiquette, when pertaining to the available urinals, states that the next urinal to be filled in should be the one that minimizes the total discomfort. The total discomfort equation is given by the following set of equations:

dist(x,y) = linear distance between person x and person y in Urinal Units
discomfort(x) = sum(1/(dist(x,y)*dist(x,y))) for all persons y excluding person x
total_Discomfort = sum(discomfort(x)) for all x

A more in depth paper dealing with a similar (not the exact same) problem can be found here: (Thanks to @Lembik for alerting me to this amazing whitepaper!)


Input/Output

Given an input of a empty and full urinals, output the resulting set of urinals with the addition of one person. If there is a tie for a position the urinals should fill in left to right. The output should be in the same format as the input.

  • If given a case with full urinals, return the input.
  • The input will always have at least one urinal defined.


Test Cases

INPUT     -> OUTPUT
1000001   -> 1001001
101010101 -> 111010101
100       -> 101
00000     -> 10000
1111111   -> 1111111
0100      -> 0101
101000    -> 101001


Rules

This is , so shortest code in bytes wins. Standard loop-holes are forbidden.

\$\endgroup\$
6
  • 2
    \$\begingroup\$ Related problem: codegolf.stackexchange.com/questions/47952/the-urinal-protocol \$\endgroup\$
    – hobbs
    Commented Nov 1, 2016 at 3:42
  • 1
    \$\begingroup\$ It is recommended to wait about a week before accepting an answer. Accepting in less than a day might decrease the amount of answers your challenge receives. \$\endgroup\$
    – Emigna
    Commented Nov 1, 2016 at 15:05
  • 1
    \$\begingroup\$ I'd suggest adding 0100 and 101000 in the test cases (some regex-based approach work on the actual test cases but won't work on those ones which should still be handled) \$\endgroup\$
    – Dada
    Commented Nov 1, 2016 at 15:07
  • \$\begingroup\$ also related: codegolf.stackexchange.com/questions/94074/… \$\endgroup\$
    – Titus
    Commented Nov 2, 2016 at 14:02
  • \$\begingroup\$ @TheBitByte How is it offensive? It's a pretty accurate description of how men choose urinals in a bathroom. \$\endgroup\$
    – mbomb007
    Commented Nov 11, 2016 at 22:50

10 Answers 10

11
\$\begingroup\$

MATL, 19 18 17 bytes

lyf!Gn:-H_^Xs&X<(

Try it online! Or verify all test cases (slightly modified code).

Explanation

It suffices to compute the distance from each potential new position to the already occupied ones. The remaining distances are do not depend on the potential new position, and so constitute a constant term, which can be ignored.

Let's take input [1 0 0 0 0 0 1] as an example.

l      % Push 1
       % STACK: 1
y      % Take input implicitly. Duplicate from below
       % STACK: [1 0 0 0 0 0 1], 1, [1 0 0 0 0 0 1]
f!     % Indices of nonzero elements, as a column array
       % STACK: [1 0 0 0 0 0 1], 1, [1 7]
Gn:    % Push [1 2 ... n], where n is input size (array of possible positions)
       % STACK: [1 0 0 0 0 0 1], 1, [1; 7], [1 2 3 4 5 6 7]
-      % Matrix with all pairs of differences 
       % STACK: [1 0 0 0 0 0 1], 1, [1; 7], [0 -1 -2 -3 -4 -5 -6;
                                             6  5  4  3  2  1  0]
H_^    % Raise each entry to -2
       % STACK: [1 0 0 0 0 0 1], 1, [   Inf 1.0000 0.2500 0.1111 0.0625 0.0400 0.0278;
                                     0.0278 0.0400 0.0625 0.1111 0.2500 1.0000    Inf]
Xs     % Sum of each column
       % STACK: [1 0 0 0 0 0 1], 1, [Inf 1.04 0.3125 0.2222 0.3125 1.04 Inf]
&X<    % Index of minimum. Takes the first if there is a tie
       % STACK: [1 0 0 0 0 0 1], 1, 4
(      % Assign: write 1 at the position of the minimizer
       % STACK: [1 0 0 1 0 0 1]
       % Implicitly display
\$\endgroup\$
4
\$\begingroup\$

Jelly, 13 12 bytes

J_þTݲSiṂ$Ṭo

Try it online! or Verify all test cases.

Explanation

J_þTݲSiṂ$Ṭo  Input: boolean array A
J             Indices, returns [1, 2, ..., len(A)]
   T          Truthy indices, returns the indices which have a truthy value
 _þ           Form the subtraction (_) table (þ) between them
    İ         Inverse, find the reciprocal of each
     ²        Square each
      S       Sum the sublists column-wise
         $    Monadic chain
        Ṃ       Minimum
       i        Find the first index of that
          Ṭ   Untruth indices, returns a boolean array with 1's at those indices
           o  Logical OR between that and A, and return
\$\endgroup\$
4
\$\begingroup\$

JavaScript (ES6), 89 bytes

a=>a[a.map((e,i)=>!e&&(t=0,a.map((e,j)=>t+=(j-=i)&&e/j/j),t<m&&(m=t,k=i)),k=0,m=1/0),k]=1

Outputs by modifying the input array.

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

R, 83 76 67 bytes

Just realized that I can save several bytes by not bothering to check if the candidate urinals are empty. Non-empty urinals will always return an Inf discomfort value, so they're excluded in the course of the calculation. Also, just using direct indexing rather than replace, so it's shorter but less elegant.

x=scan()
x[which.min(rowSums(outer(seq(x),which(!!x),`-`)^-2))]=1
x

Explanation

x=scan()

We read the current state from stdin and call it x. We assume that the input is a sequence of 1s and 0s separated by spaces or newlines. For the purposes of the explanation, let's say we input 1 0 0 0 0 0 1.

x[which.min(rowSums(outer(seq(x),which(!!x),`-`)^-2))]=1

We replace a value of x at a particular index with 1. Everything between the [ ] is figuring out what the best index is.

Since the existing urinals are immutable, we don't need to consider the distances between them. We only need to consider the distances between the occupied urinals and the possible new one. So we determine the indices of the occupied urinals. We use which, a function to return the indices of a logical vector which are TRUE. All numbers in R, when coerced to type logical, are TRUE if nonzero and FALSE if zero. Simply doing which(x) will result in a type error, argument to 'which' is not logical, as x is a numeric vector. We therefore have to coerce it to logical. ! is R's logical negation function, which automatically coerces to logical. Applying it twice, !!x, yields a vector of TRUE and FALSE indicating which urinals are occupied. (Alternative byte-equivalent coercions to logical involve the logical operators & and | and the builtins T and F, e.g. F|x or T&x and so on. !!x looks more exclamatory so we'll use that.)

                                 which(!!x)

This is paired with seq(x), which returns the integer sequence from 1 to the length of x, i.e. all urinal locations (and thus all possible locations to consider).

                          seq(x)

Now we have the indices of our occupied urinals: 1 7 and our empty urinals 1 2 3 4 5 6 7. We pass `-`, the subtraction function, to the outer function to get the "outer subtraction", which is the following matrix of distances between all urinals and the occupied urinals:

[,1] [,2]

[1,] 0 -6

[2,] 1 -5

[3,] 2 -4

[4,] 3 -3

[5,] 4 -2

[6,] 5 -1

[7,] 6 0

                    outer(seq(x),which(!!x),`-`)

We raise this to the -2th power. (For those who are a little lost, in the OP, "discomfort" is defined as 1 / (distance(x, y) * distance(x, y)), which simplifies to 1/d(x,y)^2, i.e. d(x,y)^-2.)

                    outer(seq(x),which(!!x),`-`)^-2

Take the sum of each row in the matrix.

            rowSums(outer(seq(x),which(!!x),`-`)^-2)

Get the index of the smallest value, i.e. the optimal urinal. In the case of multiple smallest values, the first (i.e. leftmost) one is returned.

  which.min(rowSums(outer(seq(x),which(!!x),`-`)^-2))

And voilà, we have the index of the optimal urinal. We replace the value at this index in x with 1. In the case of 1111 as input, it doesn't matter which one we replace, we'll still have a valid output.

x[which.min(rowSums(outer(seq(x),which(!!x),`-`)^-2))]=1

Return the modified input.

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

PHP, 135 bytes

$a=explode(1,$argv[1]);$b=0;foreach($a as$c=>$d){$l=strlen($d);if($l>$b){$b=$l;$e=$c;}}if($b)$a[$e][intval($b/2)]=1;echo implode(1,$a);

I'm sure there's a considerably quicker way of doing it, but I've got a fuzzy head and can't think of one!

Old code

The code without minification:

$a=explode(1,$argv[1]);
$b=0;
foreach($a as $c=>$d){
    $l=strlen($d);
    if($l>$b){
        $b=$l;
        $e=$c;
    }
}
if($b){
    $a[$e][intval($b/2)]=1;
}
echo implode(1,$a);
\$\endgroup\$
2
\$\begingroup\$

Python 3 223 222 165 Bytes

Okay, I know this isn't the prettiest answer out there, and I'm sure it can be golfed down quite a bit, but I was just messing around and seeing what I could do

Shout out to mbomb007 for the tips on whitespace and comparators Also, I saw my online character counter was taking all the tabs and turning them into spaces, so the count is a lot less than I originally had

def u(a):
 m,r,x=9,0,len(a)
 for i in range(x): 
    d=0
    if a[i]<'1':
     for j in range(x):
        if a[j]>'0':d+=float((j-i)**-2)
     if d<m:r=i;m=d
 return a[:r]+'1'+a[r+1:]

Showing revised whitespace:

def u(a):
<sp> m,r,x=9,0,len(a)
<sp> for i in range(x): 
<tab> d=0
<tab> if a[i]<'1':
<tab><sp> for j in range(x):
<tab><tab> if a[j]>'0':d+=float((j-i)**-2)
<tab><sp> if d<m:r=i;m=d
<sp> return a[:r]+'1'+a[r+1:]

Original:

def u(a):
    m,r,x=9,0,len(a)
    for i in range(x): 
        d=0
        if a[i]!='1':
            for j in range(x):
                if a[j]=='1':d+=float(1/(j-i)**2)
            if d<m:r=i;m=d
    return a[:r]+'1'+a[r+1:]

This expects a string passed to it of 1's and 0's like "10001" and returns a string "10101"

Edit: Changed 1/float((j-i)**2) to float((j-i)**-2)

\$\endgroup\$
4
  • \$\begingroup\$ !='1' can be <'1' and =='1' can be >'0'. Also, consider this tip \$\endgroup\$
    – mbomb007
    Commented Nov 1, 2016 at 16:20
  • \$\begingroup\$ Thanks for that whitespace tip. I definitely did not know that. That's awesome! \$\endgroup\$
    – bioweasel
    Commented Nov 1, 2016 at 16:24
  • \$\begingroup\$ That whitespace tip only works in Python 2, I think. Maybe early version of Python 3, but idk. You'll have to restrict your answer to Python 2 or some specific version of 3 with it working. \$\endgroup\$
    – mbomb007
    Commented Nov 1, 2016 at 16:37
  • \$\begingroup\$ I've got it running in a 3.5.2 shell in IDLE and it's running without an issue, so I think it's alright still \$\endgroup\$
    – bioweasel
    Commented Nov 1, 2016 at 18:05
2
\$\begingroup\$

Python 3, 574 471 347 bytes

I'll probably work on this some more, considering the other Python solution is like a fifth of this one :[.

def a(I):
 D,l,r={},len(I),range
 for i in r(l):
  if I[i]<1:
   n,t,n[i]=I[:],[],1
   for j in r(l):
    if n[j]>0:
     q,Q=[],0
     for k in r(l):
      if k!=j and n[k]>0:q.append((k-j,j-k)[k<j])
     for i in q:Q+=1/(i**2)
    t.append(Q)
   T=sum(t)
   if T not in D.keys():D[T]=i
 if len(D)>0:I[D[min(D.keys())]]=1
 print(I)

Well that's much better now that I've learned you can use single spaces.

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

Python, 165 163 158 147 141 140 139 bytes

def u(p):e=enumerate;a=[(sum((i-j)**-2for j,y in e(p)if"0"<y),i)for i,x in e(p)if"1">x];return a and p[:min(a)[1]]+"1"+p[min(a)[1]+1:] or p
\$\endgroup\$
1
  • \$\begingroup\$ rewrite second line as if"1"*len(p)==p:return p to save a byte \$\endgroup\$
    – FlipTack
    Commented Nov 1, 2016 at 22:07
1
\$\begingroup\$

05AB1E, 18 bytes

$āsƶ0Kδ-znD_°+OWkǝ

I/O as a list.

Port of @miles' Jelly answer, so make sure to upvote that answer as well!
Unfortunately, dividing by 0 won't result in INF in 05AB1E like it does in Jelly and MATL, but instead in 0. So 4 bytes (D_°+) are to account for that..

Try it online or verify all test cases.

Explanation:

$                  # Push 1 and the input-list
 ā                 # Push a list in the range [1,length] (without popping the list)
  s                # Swap so the input-list is at the top again
   ƶ               # Multiply each value by its 1-based index
    0K             # Remove all 0s
      δ-           # Pop both lists, and create a subtraction table
        z          # Convert each inner value to 1/value
         n         # Square each inner value
          D        # Duplicate this list
           _       # Check for each inner value whether it equals 0 (1 if 0; 0 otherwise)
            °      # Take 10 to the power this (10 if 0; 1 otherwise)
             +     # Add it to each value at the same positions
              O    # Sum each inner list
               W   # Push the minimum (without popping the list)
                k  # Pop both, and push the (first) (0-based) index of this minimum
                 ǝ # Insert the 1 at this index into the (implicit) input-list
                   # (after which the result is output implicitly)
\$\endgroup\$
0
\$\begingroup\$

Scala, 173 bytes

Golfed version. Try it online!

a=>{var(m,r,x,d)=(9d,0,a.size,0d);for(i<-0 to x-1)if(a(i)=='0'){d=0;for(j<-0 to x-1)if(a(j)=='1')d+=math.pow(j-i,-2);if(d<m){r=i;m=d}};a.substring(0,r)+'1'+a.substring(r+1)}

Ungolfed version. Try it online!

object Main {
  def u(a: String): String = {
    var m = 9.0
    var r = 0
    val x = a.length
    for (i <- 0 until x) {
      var d = 0.0
      if (a(i) == '0') {
        for (j <- 0 until x) {
          if (a(j) == '1') {
            d += math.pow(j - i, -2)
          }
        }
        if (d < m) {
          r = i
          m = d
        }
      }
    }
    a.substring(0, r) + '1' + a.substring(r + 1)
  }

  def main(args: Array[String]): Unit = {
    println(u("1000001"))    // should print: 1001001
    println(u("101010101"))  // should print: 111010101
    println(u("100"))        // should print: 101
    println(u("00000"))      // should print: 10000
    println(u("1111111"))    // should print: 1111111
    println(u("0100"))       // should print: 0101
    println(u("101000"))     // should print: 101001
  }
}
\$\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.