ピジョンホールの原理とコードゴルフ


26

鳩の巣原理と述べています

N > Mの場合、N個のアイテムをM個のボックスに入れる場合、少なくとも1つのボックスに複数のアイテムを含める必要があります。

多くの人にとって、この原則は他の数学的発表と比較して特別な地位にあります。EWダイクストラが書いたように

それはいくつかの神秘に囲まれています。それを使用する証明は、しばしば特別なもの、特に独創的なものと見なされます。

チャレンジ

この課題の目的は、ASCIIアート表現を使用して鳩の巣の原理を説明することです。具体的には:

  1. 入力N(アイテムの数)およびM(ボックスの数)として、N非負とM正を取ります。Nより小さい場合がありますM(その場合、原則が適用されない場合でも)。
  2. ボックスへのアイテムの可能な割り当ての1つをランダムに選択します。各割り当てには、選択される確率がゼロ以外である必要があります。
  3. 次のように、割り当てのASCIIアート表現を作成します。

    • M行があり、それぞれがボックスに対応しています。
    • 各行は、などの非空白文字で始まり|ます。
    • その文字に続いて、などの別の非空白文字が続き#、そのボックス内のアイテムの数だけ繰り返されます。

たとえばN = 8、考慮してくださいM = 5。ボックスへのアイテムの選択assigmentである場合に41030、表現であります

|####
|#
|
|###
|

同じプログラムの異なる実行(異なる割り当てをもたらす)は、

|#
|##
|#
|#
|###

表現に関してある程度の柔軟性があります。下記参照。

特定のルール

コードはおよびの値に対して理論的に実行する必要NありMます。実際には、メモリサイズまたはデータ型の制限によって制限される場合があります。

出力を観察するだけでは、すべての割り当てにゼロ以外の確率があるかどうかを判断するのに十分ではないため、各サブミッションでは、コードがどのようにそれを達成するかを説明する必要があります。

次の表現バリエーションが許可されます。

  • 異なる非空白文字の任意のペアを選択できます。これらは、プログラムの実行全体で一貫している必要があります。
  • リプレゼンテーションの90度回転は許容されます。繰り返しますが、選択は一貫している必要があります。
  • 末尾または先頭の空白は許可されます。

異なる表現形式と、のための一例としてN = 15M = 6プログラムの2つの実行の結果は、可能性が

VVVVVV
@@@@@@
@@ @@@
 @  @@
    @

または

VVVVV
@@@ @
@@@ @
@ @ @
@ @ @
@

同様にN = 5M = 7表現の別のバリエーションを使用して、

  *
* * * *
UUUUUUU

または

 *** **
UUUUUUU

または

   *
*  *
*  * 
UUUUUUU

N< であるため、この場合、原則がどのように適用されないかに注意してくださいM

一般的なルール

すべてのプログラミング言語でプログラムまたは機能が許可されます。標準的な抜け穴は禁止されています。

入力は、合理的な手段で行うことができます。そして、2つの数字の配列や2つの異なる文字列など、任意の形式で。

出力手段とフォーマットも柔軟です。たとえば、出力は文字列のリストまたは改行を含む文字列です。関数の出力引数として返されるか、STDOUTに表示されます。後者の場合、表示幅が制限されているために生じる行の折り返しを心配する必要はありません。

バイト単位の最短コードが優先されます。


11
それは実際に...タイトルを取得するために今まで連れて行ってくれた
マーティン・エンダー

@MartinEnder「ピジョンホール原則」は「コードゴルフ」よりも多くのキャラクターを持っているのですか、それとも他の冗談がありますか?
ドルカハンはモニカを

5
標準のブラウザで@dorukayhan、少し質問のタイトルの上にテキストを見...
ルイスMendo

回答:


2

ゼリー9 8バイト

=þṗX¥S⁵*

これは、Mを左引数、Nを右引数としてとるダイアディックリンクです。出力は整数の配列です。0は鳩を表し、1は穴を表します。

オンラインでお試しください!

使い方

=þṗX¥S⁵*  Main link. Left argument: m. Right argument: n

    ¥     Combine the two links to the left into a dyadic chain and call it
          with arguments m and n.
  ṗ        Compute the n-th Cartesian power of [1, ..., m], i.e., generate all
           vectors of length n that consist of elements of [1, ..., m].
   X       Pseudo-randomly choose one of those vectors with a uniform distribution.
=þ        Equal table; for each k in [1, ..., m] and each vector to the right,
          compare the elements of the vector with k. Group the results by the
          vectors, yielding a 2D Boolean matrix.
     R    Range; map 1 to [1], 0 to [].
      S   Take the sum of all columns.
       ⁵* Raise 10 to the resulting powers.

10

Mathematica、68バイト

Print/@(10^RandomSample@RandomChoice[IntegerPartitions[+##,{#}]-1])&

2つの整数引数、ボックスの数、その後に項目の数をとる名前のない関数。

最初に可能なすべてのパーティションN+Mを正確にM正の部分に計算し、1その後各パーティションから減算します。これにより、可能性のあるすべての非負の部分Nへのパーティションが得られMます(IntegerPartitionsそうでなければ生成されません)。次に、ランダムなパーティションを選択してシャッフルします。これにより、ゼロの可能なすべての順序付きパーティションが許可されます。最後に、(それぞれの行になるように、対応する電源10を上昇させることによって、出力ラインにパーティションの各ビンを変換1000...してkゼロ)。出力例は次のようになります。

100
10000
1
10
10

<の場合PadRightMゼロにパディングしないと思います。NM
LegionMammal978

1
@ LegionMammal978ありがとう、同じバイト数で修正できました。
マーティンエンダー

...私は正直に感銘を受けました。私は同様の解決策を行おうとしていましたが、PadRightの非リスタビリティはそれをはるかに長くするでしょう。
LegionMammal978

@ LegionMammal978回避するための別の方法がPadRightあることでしょうIntegerPartitions[#,{#2},0~Range~#]
マーティンエンダー

1
ビルティンなし?びっくりしました...:Dでもいい答えです。最初にそれがどのように機能するかを理解する必要があります
。P– HyperNeutrino

9

Python 2、77 86バイト

from random import*
n,m=input()
exec'c=randint(0,n);n-=c;print 10**c;'*~-m
print 10**n

[0、n]の数値を生成し、その数のアイテムを出力し、nから減算します。これをm回行います。

これにより、最後のボックスに到達する可能性は非常に低くなりますが、質問はすべての出力が可能であることを尋ねただけで、等しく可能性はありません。


7

バッチ、164バイト

@for /l %%i in (1,1,%1)do @set h%%i=1
@for /l %%j in (1,1,%2)do @call set/ab=%%random%%%%%%%1+1&call set/ah%%b%%*=10
@for /l %%i in (1,1,%1)do @call echo %%h%%i%%

7つの連続した%サインは、新しい個人的なベストだと思います!注:これにより、同じボックスに9個を超えるアイテムが割り当てられると、奇妙な出力が生成されます。それが問題の場合、180バイトの場合:

@for /l %%i in (1,1,%1)do @set h%%i=1
@for /l %%j in (1,1,%2)do @call set/ab=%%random%%%%%%%1+1&call call set h%%b%%=%%%%h%%b%%%%%%0
@for /l %%i in (1,1,%1)do @call echo %%h%%i%%

はい、%2行目で合計28 秒です。


5

C、102バイト

n,m;main(x){srand(time(0));for(scanf("%d %d",&n,&m);m--;n-=x)printf("|%0*s\n",x=m?rand()%(n+1):n,"");}

標準入力を入力します。例:

echo "5 4" | ./pigeonhole

等しい確率で各出力を生成しませんが、可能なすべての組み合わせを生成できます。

壊す:

n,m;
main(x){
    srand(time(0));             // Seed random number generator
    for(scanf("%d %d",&n,&m);   // Parse input values into n and m
        m--;                    // Loop through each bucket (count down)
        n-=x)                   // Subtract number assigned to bucket from total
        printf(                 // Output a formatted string using padding
            "|%0*s\n",          // (turns out %0s will actually zero-pad a string!)
            x=m?rand()%(n+1):n, // Calculate a number of items for this bucket
            "");
}

GCCの未定義の動作の処理に依存します%0s—通常%0は整数または浮動小数点をゼロで埋め込みますが、埋め込みのみ(切り捨てはできない)なので、空白を印刷することはできません。しかし、文字列の動作は定義されていないため、GCCは同じ方法でゼロパッドすることを決定したため、ゼロ以上0のs を書き込むことができるように空の文字列をゼロパッドします。


2
関数が許可されてa(b,c){...}いるため、mainとの代わりに使用して、いくつかの文字を切り捨てることができますscanf
ケビン

3

Python 2、102 99 97 90バイト

m-1回、とのx間のランダムな量を選択し、nからそれを含めます。その後、印刷と。0n1'0'*x

最後に、s 10s の残りを印刷します。まったく同じチャンスではありませんが、すべての構成が可能です。

from random import*
n,m=input()
exec'x=randrange(n+1);n-=x;print 10**x;'*(m-1)
print 10**n

(壊れたPython回答からの再利用コード。)


この答えは、文字通り同じ答えであり、小さなバグ修正が行われているため、この答えは私の提案のはずだったと思います。
-orlp

1
@orlpこの答えの歴史を見ると、それは最新バージョンのそれになりました。最初にこのようにした場合、代わりにコメントとして投稿します。
L3viathan

ああ、それは問題ありません。見た目(および「再利用コード」を書いた方法)によって、見た目が違って見えました。ごめんなさい。
-orlp

@orlp問題ありません。あなたのものは現在働いており、とにかく私のものよりも短いです。あなたがあなたのものに近すぎると感じたらこの答えを削除することもできます、私は気にしません、あなたの答えをコピーアンドペーストしただけではないことを明確にしたいだけです。
L3viathan

3

Haskell、114 94バイト

import System.Random
n#m=map(10^).take m.until((==n).sum.take m)tail.randomRs(0,m)<$>newStdGen

少し強引なアプローチ:乱数の無限リストを生成し、リストの先頭からn個の数字を取り出して合計し、mと等しいかどうかを確認します。そうでない場合は、リストから最初の要素を取り出して繰り返します。

オンラインでお試しください!

注:インポートなしの73バイト

編集:10 ^トリックでいくつかのバイトを保存しました(新しいバージョンをオンラインで試してください!


2

REXX、74バイト

arg n m
m=m-1
o.=@
do n
  a=random(m)
  o.a=o.a||#
  end
do a=0 to m
  say o.a
  end

出力(8 5):

@#
@###
@
@#
@###

出力(8 5):

@#
@#
@
@####
@##

2

C、175の 138バイト

37バイトを節約してくれた@Daveに感謝します!

i;f(n,m){char**l=calloc(m,8);for(i=0;i<m;)l[i]=calloc(n+1,1),*l[i++]=124;for(i=n+1;--i;)*strchr(l[rand()%m],0)=35;for(;i<m;)puts(l[i++]);}

オンラインでお試しください!


1
こんにちは、これを減らすのに役立ついくつかのことがあります:calloc0で初期化されたメモリ(すべての0を自分で設定する必要はありません)、strchr文字列の終わりを見つけることができ、コンマは操作を連鎖でき、、{}およびの必要性を回避しますx[0] == *x。また気をつけてください。mallocすべてのアイテムが同じボックスにある場合、十分なメモリがありません。
デイブ

2

AHK、66バイト

2-=1
Loop,%2%{
Random,r,0,%1%
Send,|{# %r%}`n
1-=r
}
Send,|{# %1%}

orlpが0からNまでの乱数を使用してNからそれを減算したのと同じ原則に従いました。残念ながら、送信機能の動作方法のため、10 ^ rを使用してバイトを節約できませんでした。悲しいかな。次に、n = 8、m = 5の出力を示します。

|##     |#####    |##       |##     |#      |##   
|##     |#        |#####    |       |###    |#    
|#      |##       |         |###    |###    |     
|###    |         |         |       |#      |     
|       |         |#        |###    |       |#####

2

CJam、30 31 21バイト

:B1a*:C\{CBmrAt.*}*N*

入力はn mスタック上の2つの数字です。用途1列の文字のためと0繰り返し文字のため。

説明:

:B          e# Store m in B (without deleting it from the stack)
1a          e# Push 1 and wrap it in an array: [1]
*           e# Repeat the array m times
:C          e# Store this array in C (without deleting)
\{          e# Do n times:
  CBmrAt    e#   Create an array of 1s with a random element replaced with 10.
  .*        e#   Vectorized multiplication: multiply the respective elements in the arrays.
            e#   Effectively, we multiply a random value in the array by 10 (and add a 0 to the end).
}*          e# End loop.
N*          e# Join with newlines.


1

PHP、100バイト

list($z,$m,$n)=$argv;$a=array_fill(0,$n,z);while($m>0){$a[rand(0,$n-1)].=a;$m--;}echo join("\n",$a);

壊す :

list($z,$m,$n)=$argv;     // assigns the input vars to $m and $n
$a=array_fill(0,$n,z);    // creates an array $a of $n elements containing 'z'
while($m>0){              // randomly populate array $a
    $a[rand(0,$n-1)].=a;  //
    $m--;                 //
}                         //
echo join("\n",$a);       // output $a contents separated by a new line

m=7およびの出力n=5

最初の実行:

za
zaa
za
za
zaa

2回目の実行:

za
zaa
zaaa
z
za

オンラインでお試しください!


[,$m,$n]=$argv;PHP 7.1からいくつかの文字を保存できます。\n実際の改行に置き換えて1バイト節約できます。を使用for(;$m-->0;)$a[rand(0,$n-1)].=a;して、ブレーク、$mおよびセミコロンを保存できます。[,$m,$n]=$argv;$a=array_fill(0,$n,z);for(;$m-->0;)$a[rand()%$n].=a;echo join("\n",$a);85バイト
クリストフ

これはさらに[,$m,$n]=$argv;for(;$m--;)${rand()%$n}.=a;for(;$n--;)echo"z${$n}\n";67バイト下になります。
クリストフ

@Christoph [,$m,$n]=$argv;他のコードゴルフで表記を見ましたが、私の開発環境でもeval.inでも動作させることができませんでした
roberto06

ここで試してください:sandbox.onlinephpfunctions.com/code/…
クリストフ

1
いいね スニペットは私のものとはかなり異なるため、回答として投稿できると思います;)
roberto06

1

JavaScript、105バイト

x=>y=>{s=[];for(;x>1;y-=t)s[--x]="|"+"#".repeat(t=Math.random()*(y+1)|0);s[0]="|"+"#".repeat(y);return s}

オンラインでお試しください!

行を割り当てる方法により、これは下部に向かってより多く配置される傾向がありますが、上部がいくらか取得する可能性はわずかです。


1

ルビー、52バイト

->(n,m){r=Array.new(m){?|};n.times{r[rand m]+=?#};r}

引数として2つの整数を取り、文字列の配列を返す匿名関数を作成します。

>> puts ->(n,m){r=Array.new(m){?|};n.times{r[rand m]+=?#};r}.call 7,5
|#
|#
|##
|##
|#

1

Python 2、81バイト

from random import*
def f(n,m):l=['#']*m;exec('l[randrange(m)]+="o";'*n);return l

文字列のリストを返します。


1

Javascript(ES7)、75バイト

(N,M)=>{for(r='';M;M--,N-=x=~~(Math.random()*(N+1)),r+=10**x+`
`);return r}

ほとんどの答えがすでにそれを使用していることに気付くためだけに、10のべき乗のアイデアを思いついたのは賢いと思いました。


1

AWK、78バイト

{srand();for(;n++;)c[k]=c[k=int($2*rand())]"#"}END{for(;j<$2;)print"|"c[j++]}

最初にアイテムの数、次にボックスの数の2つの引数を取ります。乱数ジェネレータをシードすることから始めて、各実行が異なるようにします。次に、配列内の文字列を作成します。使用例:

awk '{srand();for(;n++;)c[k]=c[k=int($2*rand())]"#"}END{for(;j<$2;)print"|"c[j++]}' <<< "12 5"

Example output:
|##
|###
|##
|##
|###

1

MATLAB、103 94バイト

function a(m,n)
d=@(p)disp(char([1,~(1:p)]+48));for i=1:m-1;p=randi([0,n]);d(p);n=n-p;end;d(n)

書式設定あり

function a(m,n)
for i=1:m-1 
    d=@(p)disp(char([1,~(1:p)]+48));  % inline function for displaying
    p=randi([0,n]);              % picking a random number b/w 0 and n
    d(p);                        % '1' represents the box/pigeonhole, with '0's denoting entries
    n=n-p;
end
d(n);                            % writing the remaining entries/zeros

サンプル出力

>> a(4,7)
10
10000
10
10

各配列エントリはタブで表示されるため、末尾に空白がありますが、これは仕様に従って許容されるはずです。

これは非常に単純な実装のように思えるので、これを改善できると確信しています。

提案してくれた@Luis Mendoに感謝します。


あなたはそれを2回書き込みを避けるために、匿名関数として表示文を定義するかなりの数のバイトを保存することができます:d=@(p)disp(char([1,~(1:p)]+48));for i=1:m-1;p=randi([0,n]);d(p);n=n-p;end;d(n)
ルイスMendo

@LuisMendo提案をありがとう、更新します。同じ方法で実際の関数を定義することもできますか。a = @(m、n)...これもバイト数を減らすためです。一般的に、MATLAB code-golf回答の「関数名(引数)」を削除/短縮するにはどうすればよいですか
クロスト

はい、答えとして無名関数を使用することもできます。スキップすることもできa=ます。この場合、匿名関数にはループを含めることができないため、原則としてこれを行うことはできません。しかし、あなたはすべてを入れるというトリックを使うことができますeval('...')。ところで、それは通常、Matlabではugくて悪い習慣と考えられていますが、ここでは言語の乱用が好きです:
ルイス・メンドー

うーん。あなたの提案に基づいて更新し、それについてもう少し考えて、ループを回避できるかどうかを確認しますが、それはありそうもないようです。私はそれを行うことができるロジックを考えることができますが、それを実装する方法がわかりません。現在の出力とまったく同じになります。.:D提案はありますか?別の回答として自由に投稿してください。
クロスト

私はm個の要素(単なる数字ではない)を意味しました
Krostd


1

TI-Basic、63 62バイト

Prompt N,M
For(A,1,M
N→B
If M-A
randInt(0,N→B
":→Str0
For(C,1,B
Ans+"X→Str0
End
Disp Ans
N-B→N
End

各割り当てには、選択される確率がゼロ以外である必要があります。

この基準により、このプログラムの作成がはるかに簡単になりました。

I / Oの例:

prgmPIDGEON
N=?5
M=?2
:XXXX
:X

説明:

Prompt N,M     # 5 bytes, input number of items, number of boxes
For(A,1,M      # 7 bytes, for each box
N→B            # 4 bytes, on last box, make sure the sum is met by adding N items
If M-A         # 5 bytes, if not last box
randInt(0,N→B  # 8 bytes, add random number of items from 0 to N to box A
":→Str0        # 6 bytes, first character
For(C,1,B      # 7 bytes, add B items to the box
Ans+"X→Str0    # 8 bytes
End            # 2 bytes
Disp Ans       # 3 bytes, print this box
N-B→N          # 6 bytes, subtract the items used in this box
End            # 1 byte, move on to next box

1

MATLAB、73 64 58バイト

アップデート#3

そうでないと負の整数を取得するため、ソートが必要なようです。しかし、私は今に置き換えdisp(sprintf(...))ましたfprintf(...)ので、答えは58バイトのままです。

@(m,n)fprintf('%i\n',10.^diff([0;sort(randi(n,m-1,1));n]))

アップデート#2:

配列を並べ替える必要がないことに気付きました。実際、並べ替えを行うと、配列内の数値の平均が実際に減少します。だから私はsort(...)部品を削除しました。出力は同じままなので、「サンプル出力」は更新していません。

@(m,n)disp(sprintf('%i\n',10.^diff([0;randi(n,m-1,1);n])))

最後に、ルイスによるオクターブの答えに迫ります!:D

アップデート#1:

@(m,n)disp(sprintf('%i\n',10.^diff([0;sort(randi(n,m-1,1));n])))

文字列に変換する代わりに、数値を直接表示するだけです。を削除することで58バイトに減らすことができましたdisp(...)が、ans =sprintfだけで余分なものが得られ、それが許容できるかどうかわかりません。

初期コード:

@(m,n)disp(strjust(num2str(10.^diff([0;sort(randi(n,m-1,1));n])),'left'))

ルイスからのいくつかの提案のおかげで、前の答えでループを取り除きました。ここで、最初mndiff([0;sort(randi(n,m-1,1));n]))になる乱数の垂直配列を作成してから、それらを10の指数として使用し、文字列に変換し、左揃えして表示します。

技術的にdisp(...)を削除してさらに6バイトを節約できましたが、「ans」が出力され、仕様に違反する可能性があります。また、それらを文字列に変更し、目的の終了形式を取得するために左揃えにする方法があるかもしれないので、提案を受け入れます。

サンプル出力:

>> a=@(m,n)disp(strjust(num2str(10.^diff([0;sort(randi(n,m-1,1));n])),'left'));
>> a(4,6)
1000
10  
100 
1   

:ここでは、提案に基づいて、関数を匿名関数に変更しました。サンプル出力では、aデモンストレーションするためにそれを割り当てました。これが仕様に違反しないことを望みますが、もしそれが私に知らせてください、そして、私はそれを変えます。


一番上の答えは同じ10 ^ ..のロジックを使用していることに気づきました。価値があるので、それが重要な場合は、それを私の答えの参照として使用しませんでした。 P)
クロスト

また、この部分に長い間留まっていたため、合計がになるmランダムな整数を作成するという考えのこの回答の功績に留意したかったのnです。コメントで)
Krostd

1

積み上げ、29バイト

('|')\rep\[:randin'#'push@.]*

オンラインでお試しください!

Mを含むシングルトンの配列を作成し、ランダムに選択された配列時間に'|'加算'#'することにより動作しNます。


いいね!それで、すべての結果は同じように正しいでしょう?
ルイスメンドー

@LuisMendo randinは、Fisher-Yatesアルゴリズムを内部で使用するため、そうあるべきです。(これは、CJamの回答がFWIWを使用するのと同じアルゴリズムです)
コナーオブライエン

1

Pythonの280の95 89 88バイト

from random import*
n,m=input()
while m:x=randint(0,n);print'1'+'0'*[n,x][m>1];m-=1;n-=x

オンラインでお試しください!

  • 15バイトの追加:以前の編集には少し欠陥があり、一部のピエゴンは省略されていました。
  • 6バイト保存:他の場合は[n、x] [m> 1]に置き換えられます
  • 1バイト保存:インポート*

1

、19バイト

≔EN⟦⟧θFN⊞‽θ#Eθ⁺|⪫ιω

オンラインでお試しください!リンクは、コードの詳細バージョンです。説明:

  N                 Input `M`
 E                  Map over implicit range
   ⟦⟧               Empty array
≔    θ              Assign resulting nested array to `q`

       N            Input `N`
      F             Loop over implicit range
          θ         Nested array `q`
         ‽          Random element
           #        Literal string
        ⊞           Append to array

             θ      Nested array `q`
            E       Map over array
                 ι  Current element
                  ω Empty string
                ⪫   Join
               |    Literal string
              ⁺     Concatenate
                    Implicitly print on separate lines
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.