Pigeonhole Principle & Code Golf


26

Принцип голубиной дыры гласит, что

Если N элементов помещено в M блоков с N > M , то хотя бы один блок должен содержать более одного элемента.

Для многих этот принцип имеет особый статус по сравнению с другими математическими высказываниями. Как писал EW Dijkstra ,

Он окружен какой-то мистикой. Доказательства его использования часто рассматриваются как нечто особенное, что-то особенно гениальное.

Соревнование

Цель этой задачи состоит в том, чтобы проиллюстрировать принцип «голубиных отверстий» с использованием художественных представлений ASCII. В частности:

  1. Возьмите в качестве входных данных N(количество элементов) и M(количество блоков), с Nнеотрицательным и Mположительным. Nможет быть меньше, чем M(даже если принцип не применяется в этом случае).
  2. Произвольно выберите одно из возможных назначений предметов для ящиков. Каждое назначение должно иметь ненулевую вероятность быть выбранным.
  3. Создайте ASCII художественное представление задания следующим образом:

    • Есть Mстроки, каждая из которых соответствует коробке.
    • Каждая строка начинается с непробельного символа, такого как |.
    • За этим символом следует другой непробельный символ, например #, повторяемый столько раз, сколько в этом поле есть элементов.

Рассмотрим, например N = 8, M = 5. Если выбранная распайка элементов для ящиков 4, 1, 0, 3, 0, то представление

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

Другой прогон (приводящий к другому назначению) одной и той же программы может дать

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

Существует некоторая гибкость в отношении представления; увидеть ниже.

Особые правила

Код должен теоретически работать при любых значениях в Nи M. На практике это может быть ограничено размером памяти или типом данных.

Поскольку наблюдения выходных данных недостаточно для определения того, все ли присвоения имеют ненулевую вероятность , каждое представление должно объяснять, как код достигает этого, если не очевидно.

Допустимы следующие варианты представления:

  • Любая пара разных непробельных символов может быть выбрана. Они должны быть согласованы при выполнении программ.
  • Повороты представления на 90 градусов допустимы. Опять же, выбор должен быть последовательным.
  • Трейлинг или пробел допускается.

В качестве примера с другим форматом представления, для N = 15, M = 6результаты двух выполнений программы может быть

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

или

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

Кроме того, N = 5, M = 7может дать, используя другой вариант представления,

  *
* * * *
UUUUUUU

или

 *** **
UUUUUUU

или

   *
*  *
*  * 
UUUUUUU

Обратите внимание, что принцип не применим в этом случае, потому что N< M.

Основные правила

Программы или функции разрешены на любом языке программирования . Стандартные лазейки запрещены.

Вклад может быть сделан любым разумным способом ; и с любым форматом, таким как массив из двух чисел или двух разных строк.

Средство вывода и формат также являются гибкими. Например, выходные данные могут быть списком строк или строкой с символами новой строки; возвращается как выходной аргумент функции или отображается в STDOUT. В последнем случае не нужно беспокоиться о переносе строк, вызванном ограниченной шириной экрана.

Самый короткий код в байтах побеждает.


11
На самом деле мне потребовалось до сих пор, чтобы получить титул ...
Мартин Эндер

@MartinEnder Это то, что в «принципе голубя» больше символов, чем в «код-гольфе», или есть какая-то другая шутка?
user8397947

5
@dorukayhan В стандартном браузере посмотрите текст чуть выше заголовка вопроса ...
Луис Мендо,

Ответы:


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+Mв точно Mположительные части, а затем вычитает 1из каждого разбиения. Это дает нам все возможные разбиения Nна Mнеотрицательные части (которые IntegerPartitionsне сгенерируют иначе). Затем выберите случайный раздел и перемешайте его. Это гарантирует, что разрешены все возможные заказанные разделы с нулями. Наконец, преобразуйте каждую ячейку раздела в выходную строку, увеличив 10 до соответствующей мощности (так, чтобы каждая строка стала 1000...с kнулями). Пример вывода может выглядеть так:

100
10000
1
10
10

Я считаю, PadRightчто не дополняет Mнули, если N< M.
LegionMammal978

1
@ LegionMammal978 Спасибо, удалось исправить это при том же количестве байтов.
Мартин Эндер,

... Я честно впечатлен. Я собирался сделать подобное решение, но PadRightотсутствие возможности сделать это намного дольше.
LegionMammal978

@ LegionMammal978 Еще один способ избежать PadRightбудет IntegerPartitions[#,{#2},0~Range~#].
Мартин Эндер

1
Нет buitltin? Я удивлен ...: 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 %секунд на второй строке.


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 решил сделать это с нулевым заполнением таким же образом, так что это нулевое заполнение пустой строки, чтобы иметь возможность записать ноль или более 0s.


2
Поскольку функции разрешены, вы можете обрезать несколько символов, используя a(b,c){...}вместо mainи scanf.
Кевин

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

С 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. К сожалению, я не смог сохранить байты, используя 10 ^ r из-за способа работы функции Send. Увы и неудачно. Вот некоторые выводы для 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

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;чтобы сохранить разрывы, $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}

Создает анонимную функцию, которая принимает два целых числа в качестве аргументов и возвращает массив строк:

>> 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)
Луис Мендо

@ LuisMendo Спасибо за предложение, я обновлю. Могу ли я так же определить свою фактическую функцию, например, a = @ (m, n) ... так как это также уменьшит количество байтов. Как люди обычно удаляют / сокращают «имя функции (аргументы)» в ответах MATLAB code-golf?
Кростд

Да, вы также можете использовать аномальную функцию в качестве ответа. Вы даже можете пропустить a=. В этом случае вы не можете сделать это в принципе, потому что анонимные функции не могут содержать циклы. Но вы могли бы использовать хитрость, чтобы положить все в eval('...'). Кстати, это обычно считается уродливой и плохой практикой в ​​Matlab, но здесь нам нравятся злоупотребления языками :-)
Луис Мендо

Хм ... Я обновлю, основываясь на вашем предложении, подумаю еще немного и посмотрю, смогу ли я избежать цикла, хотя это кажется маловероятным. Я могу придумать логику, которая могла бы это сделать, но не уверена, как это реализовать. Я думаю об определении числа 10 ^ n и поиске m чисел, которые являются степенями 10, а затем просто их распечатке. Это будет точно такой же вывод, как у меня сейчас ..: D Есть предложения? Не стесняйтесь отправлять это как другой ответ.
Кростд

Я имел в виду m факторов (а не только цифры)
Кростд

1

Октава , 62 54 байта

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

Анонимная функция, которая принимает два числа и выводит двумерный массив символов >для блоков и *объектов. Все результаты одинаково вероятны.

Попробуйте онлайн!


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

Каждое назначение должно иметь ненулевую вероятность быть выбранным.

Этот критерий значительно облегчил написание этой программы.

Пример ввода / вывода:

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

Благодаря некоторым предложениям Луиса я избавился от этой петли в своем предыдущем ответе . Теперь я сначала создаю вертикальный массив mслучайных чисел, складывающихся в n(diff([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 ^ .. Для чего бы это ни стоило, и если это имеет значение, я не использовал это в качестве ссылки для моего ответа ... (но, черт возьми, кто-то бил меня в это!: П)
Кростд

Также хотел бы поблагодарить этот ответ за идею создания mслучайных целых чисел, которые составляют в nцелом, так как я застрял на этой части в течение долгого времени .. (Тем не менее не могу добавить более 2 ссылок в моих ответах, поэтому включение его в комментарии)
Кростд

1

С накоплением , 29 байт

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

Попробуйте онлайн!

Ведет себя путем построения массива M синглетонов, содержащего '|', а затем добавляющего '#'в произвольно выбранный массив Nраз.


Ницца! И поэтому все результаты одинаково вероятны, верно?
Луис Мендо

@LuisMendo так и должно быть, так как randinиспользует алгоритм Фишера-Йейтса внутренне. (Это тот же алгоритм, который в ответе CJam использует FWIW)
Конор О'Брайен,

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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.