便器プロトコル


38

バックグラウンド

男性のトイレで個々の小便器を選ぶ順番を説明する、いわゆる「尿プロトコル」は、複数の場所で議論されてきました。1つのバージョンがこのxkcdブログ投稿で提供されています。この質問は、わずかなバリエーションに関するものです。

配置:行のnの小便器。
プロトコル:新しい人はそれぞれ、すでに使用されている便器から最も遠い便器の1つを選択します。

これは、最初の人が小便器を選ぶ際に制限を設けないことに注意してください。

更新:n人がn個の小便器を選択できるさまざまな方法のシーケンスは、1、2、4、8、20 ...で始まります。これは、OEIS A095236と同じではないことに注意してください。質問。

仕事

0〜10の整数nを指定すると、n人が小便器を占有できるすべての可能な順序が(任意の順序で)出力されます。各順序は、最終的な配置として印刷する必要があります。左端の小便器から始まり、オプションの非英数字セパレーター(前ではなく)で始まる、人々(最初の9人は1〜9、10番目は0)を表す数字のシーケンス以降)数字。たとえば、次の出力は両方とも有効です。

>> 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

STDIN(または最も近い代替)、コマンドライン引数、または関数引数を介して入力を取得して、プログラムまたは関数を作成できます。結果はSTDOUT(または最も近い代替)に印刷する必要があります。

得点

最短のコードが優先されます。標準の利用規約が適用されます。


1
ふむ 5つの小便器のためにこれを得る。代わりに16行にする必要があります。誰がそれらの解決策のどれが間違っているのか詳しく説明していただけますか?これは現在実装中です。他の人との距離が最大の小便器を選択してください。
knedlsepp

1
サンドボックス化についてはそれほどです:-(仕様はタスクで説明されたものであり、シーケンス定義ではありません。コンピューターに到達したらすぐに更新します。
Uri Granta

1
@knedlsepp行3、4、17、18は正しくありません。これらではspan、長さ1の人#3を配置し、span長さ2の人が利用可能です。私は突然自分自身を混乱させることができました。OPは意図的にリンクから派生しているため、リンクをたどる必要がありますか?
BrainSteel

タスクがA095236と同じではないことに注意するために仕様を更新しました。
ウリグランタ

[1、3、2]で形式を出力することは許可されていますか?そのようなソリューションはそれぞれ改行で区切られていますか?(つまり、「、」の区切り文字だけでなく、「[」の始まりと「]」の終わりも)
-orlp

回答:


11

ピス、53 51

MhSm^-dG2HjbmjdmehxkbQusmm+dkfqgTdeSmgbdQUQGUtQm]dQ

これは反復的なアプローチです。順序付けられた場所のセットに部分的に入力されている可能性がある場合、最適な追加の場所をすべて見つけてから、対応する場所リストを生成し、繰り返します。

結果はフォーム[<first person's location>, <second person's location> ...]で生成され、その後、これは目的の形式に変換されます。MhSm^-dG2H指定されたストールから占有ストールまでの最小距離を見つけるヘルパー関数を定義します(2乗)。面白いことに、セパレータは無料です。

実行例。

説明:

まず、ヘルパー関数g。これは、GとHの値の間の最小二乗距離を検出します。

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

次に、その小便器と占有されている小便器との間の最小二乗距離の小便器位置の最大値を見つけます。

Q入力です。)

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

dこの場合は、使用されている小便器のリストですが、小b便器の場所を繰り返し処理します。

次に、最も近い占有小便器からの最小二乗距離が上記の最大値に等しいすべての小便器の位置を見つけます。

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

次に、上記の小便器の位置をに追加して作成された小便器の位置リストを生成しますd。これを以前の便器の場所のリストごとに行い、リストを長さNからに拡張しますN+1G指定された長さの使用済み小便器所在の法的リストのリストです。

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

次に、占有された小便器の場所のリストの完全なリストを生成するために、上記の式を繰り返し適用します。u、reduce関数は、2番目の引数にある要素と同じ回数だけこれを行います。

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]]. 

上記の表現([<1st location>, <2nd location>, ... ])から目的の出力形式に変換し[<person in slot 1>, <person in slot 2>, ... ]ます。次に、出力フォームが出力文字列に結合され、印刷されます。印刷は暗黙的です。

jbmjdmehxkbQ
jbm             '\n'.join(map(λ k:                                    ,<above>)
   jdm     Q                      ' '.join(map(λ b:                ,Q)
        xkb                                        b.index(k)
      eh                                                     +1 %10

ダムニット、Pythで再帰的なソリューションを書くのをやめるべきです。私は常に打たれる:P
orlp

kajsdlkas^23asdjkla1lasdkj~JZasSSA-サイズのほぼ半分。
オプティマイザー

@Optimizer忙しくないときに説明を追加します。
isaacg

8

ピス、75 71 67

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

再帰的組み合わせソリューション。

これは、このPythonソリューションからのかなり直接的な翻訳です。

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)))

それはどのように機能しますか?「再帰的組み合わせソリューション」より詳細。
tbodt

@tbodtは、Pythに翻訳する前に書いたPythonコードを追加しました。
-orlp

5

C、929 878バイト

これは怪物だ、みんな。ごめんなさい。

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);}}

、3つの機能を定義しf(int*,int)P(int*,int,int,int,int)L(int)。を呼び出しL(n)、STDOUTに出力します。

の出力n=5

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

更新:セパレーターを削除し、コードを修正しました。古いコードは、n = 7 +で失敗しただけでなく、n = 10で何も出力できませんでした(おっと!)。この束をより徹底的にテストしました。最大n = 13の入力をサポート"%d"する"%x"ようになりました(ただし、16進数で印刷するように変更する必要があります)。入力のサイズは依存しsizeof(long)、実際にあると仮定されます8

これがどのように機能するのか、そしてなぜそのような奇妙な制限が存在するのかを説明します:

これらは頻繁に使用されるため、数バイトを節約するように定義します。

typedef unsigned long U; typedef unsigned char C;

ここにあり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;
}

fsizeの整数の配列nnそれ自身を取ります。ここで唯一巧妙なのは、呼び出し関数によってにunsigned long変換されるを返すchar[8]ことです。したがって、配列内の各文字は0xFF、次の人の有効な小便器を指すインデックスまたはインデックスに設定されます。ためにn<10、我々は次の人が使用できる便器ごとに有効を保持するために5つ以上のバイトを必要としません。

ここにあり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);
    }
}

Pは、正確に1つの要素がに設定されたuサイズの配列を取り、残りはです。次に、可能なすべての順列を再帰的に見つけて出力します。n10

ここにあり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);
    }
}

LP n毎回異なる開始位置で時間を呼び出すだけです。

興味のある人のために、これは(ゴルフfが少ない)A095236でシーケンスを生成します。

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;
}

最初の「1 4 ...」は仕様に反しているようです。最初の数値が1の場合、次の数値は5でなければなりません
。– anatolyg

2
@anatolygいいえ。「1 4」がどのように発生するかを段階的に説明します。gist.github.com
orlp

セパレータはオプションです。(!)あなたは%dの後にスペースを削除することによって1つのバイトを保存することができます:-)
ウリグランタ

@UriZarfatyありがとう!実際には、ここに保存されるべき大量のバイトがあります。私は現在、より良い解決策と説明を書いています。
BrainSteel

@yo '出力が少し混乱していると思います。の出力は、14352人#1が左端の小便器を選択したことを意味します。人#2が一番右の人を選択し、それが#3を中央に押し込んだ。出力する必要があるのは、次に選んだ小便器の番号ではありません。
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)

再帰的アプローチ。


4

JavaScriptの(ES6)153 160 169

Math.minを使用して編集し、(もちろん)最大距離を見つけます:合理化されたコードと16バイトの保存。

再帰検索は、n> 10で機能し、%10を削除するだけです(コンソールがすべての出力を展開するまで待機する準備ができます)。

私は、最も近いスロットから、使用中のスロット(正数)または現在の距離を格納する単一のアレイを使用する(したがって、負の数<>コードに交換されます)。

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+[]))()}

非ゴルフ

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
}

Firefox / FireBugコンソールでテストする

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



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

1

Perl、174

それほど短くはありませんが、楽しいです。use feature 'say';バイト合計にはカウントしていません。

$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

脱ゴルフ:

$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;
}

1

C、248バイト

このコードは、再帰アルゴリズムを使用して、望ましい結果を生成します。

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);}

拡張:

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);
}

1

Bash、744 674バイト

これはまだ長すぎます:)。文字列を使用して小便器の行を表し、フラッディングアルゴリズムを使用して、再帰の各フェーズで最も遠い小便器を見つけます。改変されていないコードはほとんど自明です。小便器の数はキーボードから読み取られます。

コード(ゴルフ):

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

つかいます:

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

そして、それは行く:

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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.