鸽子洞原理和代码高尔夫


26

鸽巢原理指出,

如果将N个项目放入M盒中,且N > M,则至少一个盒子必须包含一个以上的项目。

对于许多人来说,与其他数学运算相比,该原理具有特殊的地位。正如EW Dijkstra 写道

它被一些神秘感所包围。使用它的证明通常被认为是特殊的东西,特别是巧妙的东西。

挑战

这项挑战的目的是使用ASCII艺术表示法来说明鸽洞原理。特别:

  1. 以输入N(项目数)和M(框数)N为正和非负MN可能小于M(即使该原则在这种情况下不适用)。
  2. 随机选择可能的项目分配到框之一。每个分配都应具有非零的被选概率。
  3. 生成分配的ASCII艺术作品表示,如下所示:

    • 有几M行,每行对应一个框。
    • 每行以非空白字符开头,例如|
    • 在该字符之后是另一个非空白字符,例如#,与该框中的项目重复的次数很多。

例如考虑N = 8M = 5。如果项盒中的所选赋值是41030,表示是

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

相同程序的不同运行(导致不同的分配)可能会导致

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

表示有一些灵活性;见下文。

具体规则

该代码应理论上任何值运行NM。实际上,它可能受内存大小或数据类型限制的限制。

由于观察输出不足以确定所有赋值是否具有非零概率,因此每个提交都应说明代码如何实现(如果不是很明显)。

允许以下表示形式的变化

  • 可以选择任何一对不同的非空白字符。它们在程序执行之间必须保持一致。
  • 表示形式可以旋转90度。同样,选择必须是一致的。
  • 允许尾随空格或前导空格。

以具有不同表示形式的示例为例,对于N = 15M = 6该程序两次执行的结果可能是

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

要么

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

同样,N = 5M = 7可以给,使用这种表示的另一种变体,

  *
* * * *
UUUUUUU

要么

 *** **
UUUUUUU

要么

   *
*  *
*  * 
UUUUUUU

请注意该原理在这种情况下不适用,因为N< M

一般规则

允许使用任何编程语言编写程序或功能。禁止出现标准漏洞

可以通过任何合理的方式进行输入;并采用任何格式,例如两个数字或两个不同字符串的数组。

输出方式和格式也很灵活。例如,输出可以是字符串列表或带换行符的字符串。作为函数输出参数返回或显示在STDOUT中。在后一种情况下,不必担心由有限的显示宽度引起的换行。

以字节为单位的最短代码获胜。


11
实际上直到现在我才拿到冠军头衔……
Martin Ender

@MartinEnder是“鸽子洞原理”比“代码高尔夫”具有更多字符,还是还有其他玩笑?
dorukayhan希望Monica回来

5
@dorukayhan在标准浏览器中,在问题标题上方稍稍看一下文本……
Luis Mendo

Answers:


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

一个带两个整数参数(框数,后跟项目数)的未命名函数。

它首先将的所有可能分区划分N+MM正部分,然后1从每个分区中减去。这给我们的所有可能划分NM非负部分(这IntegerPartitions本来不会产生)。然后选择一个随机分区并将其随机播放。这保证了允许所有可能的零分区。最后,通过提高10到相应的功率分区的每个区间转换成输出线路(以使得每个线变得1000...k零)。输出示例如下:

100
10000
1
10
10

我相信<< PadRight不会将其填充M为零。NM
LegionMammal978

1
@ LegionMammal978感谢,设法将其修复为相同的字节数。
Martin Ender

...我真的很感动 我本来打算做一个类似的解决方案,但是PadRight的不可列出性会使其更长。
LegionMammal978

@ LegionMammal978另一种避免的PadRight方法是IntegerPartitions[#,{#2},0~Range~#]
Martin Ender

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

是的,%第二行总计28 s。


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

在stdin上接受输入,例如:

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对undefined行为的处理%0s—通常%0会将整数或浮点数零填充,但只能填充(绝不截断),因此无法打印空白。但是没有定义字符串的行为,GCC决定以相同的方式将其零填充,因此将空字符串零填充以能够写入零个或多个0s。


2
由于允许使用函数,因此可以使用和a(b,c){...}代替一些字符。mainscanf
凯文(Kevin)

3

Python 2,102 99 97 90字节

m-1次,请选择x介于0和之间的一个随机数n,并将其从n中减去。然后打印一个1'0'*x

最后,打印1和的其余部分0。并非完全一样,但是所有配置都是可能的。

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

感谢@Dave节省了37个字节!

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
嗨,有几件事可以帮助您减少这一点:calloc将为您提供0初始化的内存(无需自己设置所有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减去它的相同原理。不幸的是,由于Send函数的工作方式,我无法通过使用10 ^ r来保存字节。las和懈怠。这是n = 8,m = 5的一些输出:

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

2

CJam,30 31 21字节

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

输入是n m堆栈上的两个数字。用途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

Röda,79个字节

f n,m{a=[0]*m
i=0{{n--
a[i%m]++}if randomBoolean
i++}while[n>0]
a|[`|`.."#"*_]}

在线尝试!

这将创建一个零数组并在随机位置递增它们。


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

第二次执行:

za
zaa
zaaa
z
za

在线尝试!


您可以使用[,$m,$n]=$argv;PHP 7.1来保存一些字符。您可以\n用实际的换行符替换以节省1个字节。您可以使用for(;$m-->0;)$a[rand(0,$n-1)].=a;保存中断,a $m和分号。[,$m,$n]=$argv;$a=array_fill(0,$n,z);for(;$m-->0;)$a[rand()%$n].=a;echo join("\n",$a);85字节
Christoph

这进一步降低了[,$m,$n]=$argv;for(;$m--;)${rand()%$n}.=a;for(;$n--;)echo"z${$n}\n";67个字节。
Christoph'Apr

@Christoph我[,$m,$n]=$argv;在其他代码高尔夫球上看到了这种表示法,但是无论在我的开发环境中还是在eval.in上都无法使其起作用
roberto06 '17


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

Ruby,52个字节

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

创建一个匿名函数,该函数将两个整数作为参数并返回一个字符串数组:

>> 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的建议。


您可以保存一些字节,将显示语句定义为匿名函数,以避免将其写入两次: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)
Luis Mendo

@LuisMendo感谢您的建议,我会更新。我是否也可以用相同的方式定义我的实际功能,例如 a = @(m,n)...因为这也会减少字节数。人们通常如何在MATLAB代码高尔夫球答案中删除/缩短“函数名(args)”?
Krostd '17

是的,您也可以使用匿名函数作为答案。您甚至可以跳过a=。在这种情况下,原则上您不能这样做,因为匿名函数不能包含循环。但是您可以使用将所有内容放入其中的技巧eval('...')。顺便说一句,这在Matlab中通常被认为是丑陋和不好的做法,但在这里我们喜欢滥用语言:-)
Luis Mendo

嗯..我将根据您的建议进行更新,并对此进行更多思考,看看我是否可以避免循环,尽管这似乎不太可能。我可以想到一种可以做到这一点的逻辑,但不确定如何实现。.我正在考虑定义一个数字10 ^ n,找到所有10的幂的m个数字,然后将它们打印出来。这将是与我现在完全相同的输出。:D有什么建议吗?随时将其发布为另一个答案。
Krostd

我的意思是m个因素(不仅是任何数字)
Krostd'4

1

八度62 54字节

@(n,m)strcat(62,(sum(randi(m,1,n)==(1:m)',2)>=1:n)*42)

匿名函数,它接受两个数字,并输出2D字符数组,其中包含>用于框和 *对象的字符。所有结果均可能。

在线尝试!


1

TI-基本,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])))

我没有直接转换为字符串,而是直接显示数字。通过删除disp(...),我可以减少到58个字节,但是我ans =只用sprintf就得到了额外的字节,不知道这是否可以接受。

初始代码:

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

由于路易斯的一些建议,我摆脱了之前的回答。现在,我首先创建一个垂直的m随机数数组,其总数为ndiff([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)
Krostd

还想感谢这个答案,因为我创建了一个m随机数n加起来等于的整数,因为我在该部分上停留了很长时间。.(仍然不能在答案中添加两个以上的链接,因此包括它在评论中)
Krostd

1

堆叠,29字节

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

在线尝试!

通过构造一个包含的M单例数组的行为'|',然后将其'#'加到随机选择的数组N时间上。


真好!因此,所有结果都有同等的可能性,对吧?
路易斯·门多

@LuisMendo应该是,因为randin内部使用了Fisher-Yates算法。(这与CJam答案使用FWIW的算法相同)
Conor O'Brien

1

Python 2中80个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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.