Das Urinalprotokoll

38

Hintergrund

Das sogenannte "Urinal-Protokoll", das die Reihenfolge beschreibt, in der einzelne Urinale in einem Männerbad entnommen werden, wurde an mehreren Stellen erörtert. Eine Version finden Sie in diesem xkcd-Blogbeitrag . Diese Frage betrifft eine geringfügige Abweichung:

Anordnung : n Urinale in einer Reihe.
Protokoll : Jede neue Person wählt eines der Urinale aus, die am weitesten von den bereits verwendeten entfernt sind.

Beachten Sie, dass dies keine Einschränkungen dahingehend darstellt, welches Urinal von der ersten Person entnommen wird.

Update : Die Reihenfolge der verschiedenen Arten, wie n Personen n Urinale auswählen können, beginnt mit 1, 2, 4, 8, 20 ... Beachten Sie, dass dies nicht mit OEIS A095236 identisch ist , das geringfügig strengere Einschränkungen beschreibt Frage.

Aufgabe

Bei einer Ganzzahl n zwischen 0 und 10 werden (in beliebiger Reihenfolge) alle möglichen Reihenfolgen ausgegeben, in denen n Personen n Urinale belegen können. Jede Bestellung sollte als endgültige Anordnung gedruckt werden: eine Folge von Ziffern, die die Personen darstellen (1-9 für die ersten 9 Personen, 0 für die Zehnten), beginnend mit dem Urinal ganz links, mit optionalen nicht-alphanumerischen Trennzeichen zwischen (aber nicht vor) oder nach) den Ziffern. Beispielsweise sind die folgenden Ausgaben beide gültig:

>> 3
132
213
312
231

>> 4
1|3|4|2
1|4|3|2
3|1|4|2
4|1|3|2
2|3|1|4
2|4|1|3
2|3|4|1
2|4|3|1

Sie können ein Programm oder eine Funktion schreiben und Eingaben über STDIN (oder die nächstgelegene Alternative), ein Befehlszeilenargument oder ein Funktionsargument vornehmen. Die Ergebnisse sollten an STDOUT (oder die nächstgelegene Alternative) gesendet werden.

Wertung

Kürzester Code gewinnt. Es gelten die Allgemeinen Geschäftsbedingungen.

Uri Granta
quelle
1
Hm. Für 5 Urinale bekomme ich das . Es sollten stattdessen 16 Zeilen sein. Würde jemand bitte erläutern, welche dieser Lösungen falsch sind? Dies wird derzeit implementiert: Wählen Sie eines der Urinale mit maximalem Abstand zu anderen.
Knedlsepp
1
Soviel zum Sandboxen :-( Spec ist wie in der Aufgabe beschrieben, nicht die Sequenzdefinition. Ich aktualisiere sobald ich an einen Computer komme.
Uri Granta
1
@knedlsepp Zeilen 3, 4, 17, 18 sind falsch. In diesen platzieren Sie Person # 3 in einer spanLänge 1, in der eine spanLänge 2 verfügbar ist. Ich habe es plötzlich geschafft, mich zu verwirren. Es scheint, dass das OP absichtlich von dem Link abgeleitet ist, und daher sollte dem Link gefolgt werden?
BrainSteel 18.03.15
Die Spezifikation wurde aktualisiert, um zu beachten, dass die Aufgabe nicht mit A095236 identisch ist.
Uri Granta
Darf man das Format in [1, 3, 2] ausgeben, wobei jede dieser Lösungen durch Zeilenumbrüche getrennt ist? (also nicht nur ein Separator von ",", sondern auch ein Beginn von "[" und Ende von "]")
orlp

Antworten:

11

Pyth, 53 51

MhSm^-dG2HjbmjdmehxkbQusmm+dkfqgTdeSmgbdQUQGUtQm]dQ

Dies ist ein iterativer Ansatz. Bei eventuell teilweise ausgefüllten geordneten Ortssätzen finden wir alle optimalen weiteren Orte, erzeugen dann die entsprechende Ortsliste und wiederholen.

Die Ergebnisse werden im Formular generiert [<first person's location>, <second person's location> ...]und anschließend in das gewünschte Format umgewandelt. MhSm^-dG2Hdefiniert eine Hilfsfunktion, die die minimalen Abstände von einem bestimmten Stand zu einem besetzten Stand (im Quadrat) ermittelt. Amüsanterweise ist der Separator frei.

Beispiellauf.

Erläuterung:

Erstens die Hilfsfunktion g, die den quadratischen Mindestabstand zwischen G und einem beliebigen Wert in H ermittelt.

MhSm^-dG2H
M             def g(G,H): return
 hS           min(                         )
   m     H        map(lambda d:        , H) 
    ^-dG2                      (d-G)**2

Als nächstes finden wir das Maximum über den Urinalpositionen des minimalen quadratischen Abstands zwischen diesem Urinal und irgendeinem belegten Urinal:

( Qist der Eingang.)

eSmgbdQ
eS          max(                                   )
  m   Q         map(lambda b:      , range(len(Q)))
   gbd                       g(b,d)

dIn diesem Fall wird die Liste der belegten Urinale angezeigt, während büber die Urinalpositionen iteriert wird.

Als nächstes finden wir alle Urinalpositionen, deren quadratischer Mindestabstand zum nächsten belegten Urinal gleich dem oben gefundenen Maximalwert ist.

fqgTdeSmgbdQUQ
f           UQ    filter(lambda T:                             , range(len(Q)))
 qgTd                             g(T,d) ==
     eSmgbdQ                                <value found above>

Als Nächstes generieren wir die Urinalstandortlisten, die durch Hinzufügen der oben gefundenen Urinalstandorte zu erstellt werden d. Wir werden dies für jede vorherige Liste von Urinalpositionen tun, wodurch die Listen von Länge Nzu Länge erweitert werden N+1. Gist die Liste der legalen Listen besetzter Urinalstellen einer bestimmten Länge.

smm+dkfqgTdeSmgbdQUQG
sm                  G    sum(map(lambda d:                               ,G)
  m+dk                                   map(lambda k:d+[k],            )
      fqgTdeSmgbdQUQ                                        <above list>

Als nächstes werden wir den obigen Ausdruck wiederholt anwenden, um die vollständige Liste der Listen der besetzten Urinalstellen zu erzeugen. uDie Reduktionsfunktion tut genau dies, so oft Elemente in ihrem zweiten Argument enthalten sind.

usmm+dkfqgTdeSmgbdQUQGUtQm]dQ
usmm+dkfqgTdeSmgbdQUQG           reduce(lambda G,H: <the above expression)
                      UtQ        repeat Q-1 times
                         m]dQ    starting with [[0], [1], ... [Q-1]]. 

Konvertieren Sie von der obigen Darstellung [<1st location>, <2nd location>, ... ]in die gewünschte Ausgabeform [<person in slot 1>, <person in slot 2>, ... ]. Anschließend wird das Ausgabeformular in die Ausgabezeichenfolge eingefügt und gedruckt. Drucken ist implizit.

jbmjdmehxkbQ
jbm             '\n'.join(map(λ k:                                    ,<above>)
   jdm     Q                      ' '.join(map(λ b:                ,Q)
        xkb                                        b.index(k)
      eh                                                     +1 %10
isaacg
quelle
Verdammt, ich sollte aufhören, rekursive Lösungen in Pyth zu schreiben. Ich werde immer geschlagen: P
Orlp
kajsdlkas^23asdjkla1lasdkj~JZasSSA- fast halb so groß.
Optimierer
@Optimizer Ich werde eine Erklärung hinzufügen, wenn ich weniger beschäftigt bin.
isaacg
8

Pyth, 75, 71, 67

DcGHFk_UQJf!s:GeS,0-TkhS,h+TklGUQIJRsmcX>G0dhHhHJ)R]Gjbmjdmebkcm0Q0

Rekursive kombinatorische Lösung.

Es ist eine ziemlich direkte Übersetzung aus dieser Python-Lösung:

N = int(input())

def gen(l, p):
    for d in reversed(range(N)):
        s = []
        for i in range(N):
            if not sum(l[max(0,i-d):min(i+d+1, len(l))]):
                s.append(i)

        if s:
            r = []
            for possib in s:
                j = l[:]
                j[possib] = p+1
                r += gen(j, p+1)

            return r

    return [l]

print("\n".join(" ".join(str(x % 10) for x in sol) for sol in gen([0] * N, 0)))
orlp
quelle
Wie funktioniert das? Ausführlicher als "rekursive kombinatorische Lösung".
Tbodt
@tbodt Fügte den Python-Code hinzu, den ich vor der Übersetzung in Pyth geschrieben habe.
Orlp
5

C 929 878 Bytes

Das ist ein Monster, Leute. Es tut uns leid.

typedef unsigned long U;typedef unsigned char C;U f(int*u,n){C c[8],a[8];*(U*)(&c)=-1;int i,b=0,l=-9,s=-2,f=0,d;for (i=0; i<n; i++) {if (!u[i]&&s<0)s=i,l=0;if(!u[i])l++;if(u[i]&&s>=0){if(!s)l=2*l-1;d=(l-1)/2;if(b<d)*(U*)(a)=0,*(U*)(c)=-1,*c=s,*a=l,f=1,b=d;else if(b==d)c[f]=s,a[f++]=l;s=-1;}}if(s>=0&&l){l=2*l-1;d=(l-1)/2;if(b<d)*(U*)(c)=-1,*c=s,*a=l,f=1,b=d;else if(b==d)c[f]=s,a[f++]=l;}d=f;for(i=0;i<d;i++){if((c[i]+1)&&c[i]){if(c[i]+a[i]==n)c[i]=n-1;else{if(!(a[i]%2))c[f++]=b+c[i]+1;c[i]+=b;}}}return*(U*)c;}void P(int*u,n,i,c,m){for(i=0;i<n;i++){if(!u[i])c++;if(u[i]>m)m=u[i];}if(!c){for(i=0;i<n;i++)printf("%d",u[i]==10?0:u[i]);printf("\n");}else{int s[8][n];for(i=0;i<8;i++)for(c=0;c<n;c++)s[i][c]=u[c];U t=f(u,n);C*H=&t;for(i=0;i<8;i++)if((C)(H[i]+1))s[i][H[i]]=m+1,P(s[i],n,0,0,0);}}void L(n){int u[n],i,j;for(i=0;i<n;i++){for(j=0;j<n;j++)u[j]=j==i?1:0;P(u,n,0,0,0);}}

Definiert 3 Funktionen f(int*,int), P(int*,int,int,int,int)und L(int). Rufen Sie auf L(n), und es wird an STDOUT ausgegeben.

Ausgabe für n=5:

14352
15342
31452
31542
41352
51342
41532
51432
24153
25143
34152
35142
23415
23514
24513
25413
24315
25314
24351
25341

Update: Ich habe Trennzeichen entfernt und den Code korrigiert. Der alte Code schlug nicht nur für n = 7 + fehl, sondern gab für n = 10 überhaupt nichts aus (oops!). Ich habe diesen Haufen gründlicher getestet. Es werden jetzt Eingaben von bis zu n = 13 unterstützt (obwohl "%d"geändert werden sollte, "%x"damit hexadezimal gedruckt wird). Die Größe der Eingabe ist abhängig von sizeof(long)und wird 8in der Praxis angenommen.

Hier ist eine Erklärung, wie es funktioniert und warum es so eine merkwürdige Einschränkung gibt:

Diese wurden häufig verwendet, daher definieren wir sie, um ein paar Bytes zu sparen:

typedef unsigned long U; typedef unsigned char C;

Hier ist f:

U f(int*u,n){
    C c[8],a[8];
    *(U*)(&c)=-1;
    int i,b=0,l=-9,s=-2,f=0,d;
    for (i=0; i<n; i++) {
        if (!u[i]&&s<0)
            s=i,l=0;
        if(!u[i])
            l++;
        if(u[i]&&s>=0){
            if(!s)
                l=2*l-1;
            d=(l-1)/2;
            if(b<d)
                *(U*)(a)=0,
                *(U*)(c)=-1,
                *c=s,
                *a=l,
                f=1,
                b=d;
            else if(b==d)
                c[f]=s,a[f++]=l;
            s=-1;
        }
    }
    if(s>=0&&l){
        l=2*l-1;
        d=(l-1)/2;
        if(b<d)
            *(U*)(c)=-1,
            *c=s,
            *a=l,
            f=1,
            b=d;
        else if(b==d)
            c[f]=s,a[f++]=l;
    }
    d=f;
    for(i=0;i<d;i++){
        if((c[i]+1)&&c[i]){
            if(c[i]+a[i]==n)
                c[i]=n-1;
            else{
                if(!(a[i]%2))
                    c[f++]=b+c[i]+1;
                c[i]+=b;
            }
        }
    }
    return*(U*)c;
}

fnimmt eine Reihe von ganzen Zahlen der Größe nund nsich. Das einzig clevere daran ist, dass es eine zurückgibt unsigned long, die char[8]von der aufrufenden Funktion in eine umgewandelt wird . Jedes Zeichen in dem Array wird somit entweder auf 0xFFeinen Index gesetzt oder auf einen Index, der auf ein gültiges Urinal für die nächste Person zeigt. Denn n<10wir brauchen nie mehr als 5 Bytes, um jedes gültige Urinal aufzunehmen, das die nächste Person benutzen kann.

Hier ist P:

void P(int*u,n,i,c,m){
    for(i=0;i<n;i++){
        if(!u[i])c++;
        if(u[i]>m)m=u[i];
    }
    if(!c){
        for(i=0;i<n;i++)
            printf("%d",u[i]==10?0:u[i]);
        printf("\n");
    }
    else{
        int s[8][n];
        for(i=0;i<8;i++)
            for(c=0;c<n;c++)
                s[i][c]=u[c];
        U t=f(u,n);
        C*H=&t;
        for(i=0;i<8;i++)
            if((C)(H[i]+1))
                s[i][H[i]]=m+1,P(s[i],n,0,0,0);
    }
}

Pnimmt ein Array mit uder Größe an, auf das ngenau ein Element gesetzt ist 1, und der Rest ist 0. Es findet und druckt dann jede mögliche Permutation rekursiv.

Hier ist L:

void L(n){
    int u[n],i,j;
    for(i=0;i<n;i++){
        for(j=0;j<n;j++)
            u[j]=j==i?1:0;
        P(u,n,0,0,0);
    }
}

Lruft einfach P nmal mit unterschiedlichen startpositionen jedes mal auf.

Für die Interessierten wird dies (weniger Golf) fdie Sequenz in A095236 erzeugen .

U f(int*u,n) {
    C c[8];
    *(U*)(&c) = -1;
    int i,b=0,l=-10,s=-2,f=0,d;
    for (i=0; i<n; i++) {
        if (!u[i]&&s<0) {
            s=i,l=0;
        }
        if(!u[i]){
            l++;
        }
        if (u[i]&&s>=0) {
            if (!s) {
                l=2*l-1;
            }
            if (b<l) {
                *(U*)(&c)=-1;
                c[0]=s;
                f=1;
                b=l;
            }
            else if (b==l)
                c[f++]=s;
            s=-1;
        }
    }
    if (s>=0&&l) {
        l=2*l-1;
        if (b<l) {
            *(U*)(&c)=-1;
            c[0]=s;
            f=1;
            b=l;
        }
        else if (b==l)
            c[f++]=s;
    }
    d=f;
    for (i=0; i<d; i++) {
        if ((c[i]+1)&&c[i]) {
            if (c[i]+b==n) {
                c[i]=n-1;
            }
            else{
                if (!(b%2)) {
                    c[f++]=(b-1)/2+c[i]+1;
                }
                c[i]+=(b-1)/2;
            }
        }
    }
    return *(U*)c;
}
BrainSteel
quelle
"1 4 ..." zu Beginn scheint gegen die Spezifikation zu sein: Wenn die erste Zahl 1 ist, sollte die nächste 5 sein.
anatolyg
2
@anatolyg Nein. Hier ist eine schrittweise Erklärung, wie "1 4" passieren kann: gist.github.com/orlp/a5706ba664b70209b48a
orlp
Denken Sie daran, dass die Trennzeichen optional sind. Sie können 1 Byte (!) Sparen, indem Sie das Leerzeichen nach dem% d entfernen :-)
Uri Granta
@UriZarfaty Danke! Tatsächlich müssen hier eine Menge Bytes gespeichert werden. Ich schreibe gerade eine bessere Lösung und eine Erklärung.
BrainSteel
@yo 'Ich denke du verwirrst die Ausgabe ein wenig. Eine Ausgabe von 14352Mittelwert Person # 1 wählte das Urinal ganz links. Person Nr. 2 wählte die am weitesten rechts stehende, die dann Nr. 3 in die Mitte zwang. Es ist nicht die Nummer des als nächstes entnommenen Urinals, die ausgegeben werden soll.
BrainSteel
4

Python 2, 208

n=input()
r=range(n)
l=[0]*n
def f(a,d=-1):
 if a>n:print''.join(l);return
 for i in r:
  t=min([n]+[abs(i-j)for j in r if l[j]])
  if t==d:p+=[i]
  if t>d:p=[i];d=t
 for i in p:l[i]=`a%10`;f(a+1);l[i]=0
f(1)

Rekursiver Ansatz.

Jakube
quelle
4

JavaScript (ES6) 153 160 169

Bearbeiten Mit Math.min können Sie (natürlich) die maximale Entfernung ermitteln: Optimierter Code und 16 gespeicherte Bytes.

Rekursive Suche, kann mit n> 10 arbeiten, einfach% 10 entfernen (und warten, während die Konsole alle Ausgaben abrollt).

Ich verwende ein einzelnes Array, um den verwendeten Steckplatz (positive Zahlen) oder den aktuellen Abstand zum nächsten Steckplatz (negative Zahlen also <und >im Code vertauscht) zu speichern .

F=n=>{(R=(m,d,t,x=Math.min(...d=m?
  d.map((v,i)=>(c=i<t?i-t:t-i)?v<c?c:v:m%10)
  :Array(n).fill(-n)))=>
x<0?d.map((v,i)=>v>x||R(-~m,d,i)):console.log(d+[]))()}

Ungolfed

F=n=>{
  var R=(m, // current 'man', undefined at first step
   d, // slot array
   t // current position to fill
  ) =>
  {
    if (m) // if not at first step
    {
      d[t] = m % 10; // mark slot in use, (10 stored as 0 )
      d = d.map((v,i) => { // update distances in d[] 
        var c = i<t ? i-t : t-i; // distance from the current position (negated)
        return v < c ? c : v; // change if less than current distance
      });
    }
    else
    {
      d = Array(n).fill(-n) // fill distance array with max at first step
      // negative means slot free, value is the distance from nearest used slot
      // >= 0 means slot in use by man number 1..n 
    }
    var x = Math.min(...d);
    if ( x < 0 ) // if there is still any free slot
    {
      d.forEach((v,i) => { // check distance for each slot 
        if (v <= x) // if slot is at max distance, call recursive search
          R(-~m, [...d], i) // ~- is like '+1', but works on undefined too
      });
    }
    else
    {
      console.log(d+[]); // no free slot, output current solution
    }
  }

  R() // first step call
}

Test In der Firefox / FireBug-Konsole

F(5)

1,4,3,5,2
1,5,3,4,2
3,1,4,5,2
3,1,5,4,2
4,1,3,5,2
5,1,3 4,2
4,1,5,3,2
5,1,4,3,2
2,4,1,5,3
2,5,1,4,3
3,4,1,5,2
3 , 5,1,4,2
2,3,4,1,5
2,3,5,1,4
2,4,3,1,5
2,5,3,1,4
2,4,5, 1,3
2,5,4,1,3
2,4,3,5,1
2,5,3,4,1

edc65
quelle
2

Mathematica, 123, 104

f[n_,s_:{}]:=If[Length@s<n,f[n,s~Join~{#}]&/@MaximalBy[Range@n,Min@Abs[#-s]&];,Print@@Ordering@s~Mod~10]
Alephalpha
quelle
@ Martinbüttner n~f~s~Join~{#}wird werden Join[f[n,s],{#}].
alephalpha
Ach ja, ich fand es richtig assoziativ.
Martin Ender
1

MATLAB, 164

function o=t(n),o=mod(r(zeros(1,n)),10);function o=r(s),o=[];d=bwdist(s);m=max(d);J=find(d==m);if~d,o=s;J=[];end,n=max(s)+1;for j=J,o=[o;r(s+n*(1:numel(s)==j))];end
knedlsepp
quelle
1

Perl, 174

Nicht sehr kurz, aber lustig. Ich zähle nicht use feature 'say';auf die Bytesumme.

$n=pop;@u="_"x$n." "x$n."_"x$n;for$p(1..$n){@u=map{my@r;for$x(reverse 0..$n){
s/(?<=\D{$x}) (?=\D{$x})/push@r,$_;substr $r[-1],pos,1,$p%10/eg and last;
}@r}@u}y/_//d&&say for@u

Entgolft:

$n = pop; # Get number of urinals from commandline
@state = ( "_" x $n . " " x $n . "_" x $n );

for my $person (1 .. $n) {
  # Replace each state with its list of possible next states.
  @state = map {
    my @results;
    for my $distance (reverse 0 .. $n) {
      # If there are any spots with at least $distance empty on
      # both sides, then add an entry to @results with the current
      # $person number in that spot, for each spot. Note that this
      # is only used for its side-effect on @results; the modified $_
      # is never used.
      s{
        (?<=\D{$distance})
        [ ]
        (?=\D{$distance})
      }{
        push @results, $_;
        substr $results[-1], pos(), 1, $person % 10;
      }xeg
      # If we found any spots, move on, otherwise try
      # with $distance one lower.
      and last;
    }
    # New state is the array we built up.
    @results;
  } @state;
}

# After adding all the people, remove underscores and print the results
for my $result (@state) {
  $result =~ tr/_//d;
  say $result;
}
hobbs
quelle
1

C 248 Bytes

Dieser Code verwendet einen rekursiven Algorithmus, um das gewünschte Ergebnis zu generieren.

void q(char*f,int l,int d,char*o){char*c=f;while(f<c+l){if(!*f){sprintf(o+4*d,"%03i,",f-c);*f=1;q(c,l,d+1,o);*f=0;}f++;}if(d+1==l){o[4*d+3]=0;printf("%s\n",o);}}int main(int i,char**v){int k=atoi(v[1]);char*c=calloc(k,5),*o=c+k;q(c,k,0,o);free(c);}

Erweitert:

void printperms(char* memory,int length,int offset,char*output)
{
    char*temp_char=memory;
    while(memory<temp_char+length)
    {
        if(!*memory)
        {
            sprintf(output+4*offset,"%03i,",memory-temp_char);
            *memory=1;
            printperms(temp_char,length,offset+1,output);
            *memory=0;
        }
        memory++;
    }
    if(offset+1==length)
    {
        output[4*offset+3]=0;
        printf("%s\n",output);
    }
}

int main(int i,char**v)
{
    int argument=atoi(v[1]);
    char*t=calloc(argument,5),*output=t+argument;
    printperms(t,argument,0,output);
    free(t);
}
Gerwin
quelle
1

Bash, 744 674 Bytes

Das ist noch viel zu lang :). Ich verwende eine Zeichenfolge, um die Reihe der Urinale darzustellen, und einen Flutungsalgorithmus, um die entferntesten Urinale in jeder Rekursionsphase zu finden. Der ungolfed Code ist fast selbsterklärend. Die Anzahl der Urinale wird von der Tastatur gelesen.

Code (Golf):

read l;u=----------;u=-${u::$l}-
s(){ u=${u:0:$1}$2${u:$((1+$1))};}
m(){ local u=$1;a=();while :;do [ 0 -ne `expr index - ${u:1:$l}` ]||break;t=$u;y=$u;for i in `seq $l`;do [ ${y:$i:1} = - ]||{ s $(($i-1)) X;s $(($i+1)) X;};done;done;while :;do k=`expr index $t -`;[ 0 != $k ]||break;t=${t:0:$(($k-1))}X${t:$k};if [ 1 -ne $k ]&&[ $(($l+2)) -ne $k ];then a+=($(($k-1)));fi;done;}
e(){ local u f b v;u=$1;f=$2;if [ 1 -eq $l ];then echo 1;return;fi;if [ 1 = $f ];then for i in `seq $l`;do v=$u;s $i 1;e $u 2;u=$v;done;else m $u;b=(${a[@]});if [ 0 -eq ${#b} ];then echo ${u:1:$l};else for i in ${b[@]};do v=$u;s $i $(($f%10));e $u $(($f+1));u=$v;a=(${b[@]});done;fi;fi;}
e $u 1

Verwenden:

$ source ./script.sh
input number of urinals from keyboard

Und ungolfed geht es:

read l  # read number of urinals
u=----------
u=-${u:0:$l}- #row is two positions longer (it will be helpful when finding the most distant urinals)

# So, for the end, with 6 men, u might look like this:
# -143652-

# subu no fellow_no => set urinal [number] occupied by [fellow_no]
# this is just convenience for resetting a character inside a string
subu(){ u=${u:0:$1}$2${u:$((1+$1))};}


# this will be iterated in longest to find the remotest places:
# -1---3---2- => spreadstep => X1X-X3X-X2X => spreadstep => X1XXX3XXX2X
# see longest() to get more explanation.
spreadstep()
{
    y=$u
    for i in `seq 1 $l`
    do
    if [ "${y:$i:1}" != "-" ]
    then
        subu $(($i-1)) X
        subu $(($i+1)) X
    fi
    done
}

# Find the urinals with the longest distance. It uses spreadstep() - see above.
# -1---3---2- => spreadstep => X1X-X3X-X2X => spreadstep => X1XXX3XXX2X
# ---> last state with free ones was X1X-X3X-X2X ,
#                                     123456789
# free urinals are no. 3 and no. 7 => save them to arr
longest()
{
    local u=$1
    arr=()
    while true
    do
        if [ 0 -eq `expr index - ${u:1:$l}` ]
        then
            break
        fi
        save=$u
        spreadstep
    done

    while true
    do
        index=`expr index $save -`
        if [ 0 == $index ]
        then
            break
        fi

        save=${save:0:$(($index-1))}X${save:$index}
        if [ 1 -ne $index ] && [ $(($l+2)) -ne $index ]
        then
            arr+=($(($index-1)))
        fi
    done
}

# main function, recursively called
# the first fellow may take any of the urinals.
# the next fellows - only those with the longest distance.
placements_with_start()
{
    local u=$1
    local fellow=$2
    if [ 1 -eq $l ] # special case - there is no 2nd fellow, so below code would work incorrect 
    then
        echo "1"
        return
    fi
    if [ 1 == $fellow ]       # may take any of urinals
    then
        for i in `seq 1 $l`
        do
            local _u=$u
            subu $i 1                     # take the urinal
            placements_with_start $u 2    # let the 2nd fellow choose :)
            u=$_u
        done
    else
        longest $u   # find the ones he can take
        local _arr=(${arr[@]})
        if [ 0 -eq ${#_arr} ]
        then
            echo ${u:1:$l}    # no more free urinals - everyone took one - print the result
        else
            for i in ${_arr[@]}
            do
                local _u=$u
                subu $i $(($fellow % 10))                # take urinal
                placements_with_start $u $(($fellow+1))  # find locations for for next fellow
                u=$_u
                arr=(${_arr[@]})
            done
        fi
    fi
}

placements_with_start $u 1
pawel.boczarski
quelle