Делящиеся числа


47

Я нашел эту последовательность во время работы над Evolution of OEIS , но не удосужился опубликовать ее как ответ. После написания эталонной реализации в Mathematica я подумал, что это забавное упражнение, которое нужно выполнить как отдельную задачу, так что здесь мы идем.

Давайте построим числовой реактор деления! Рассмотрим положительное целое число N. В качестве примера рассмотрим 24. Чтобы разделить это число, мы должны найти наибольшее число последовательных натуральных чисел, которые суммируются N. В этом случае это так 7 + 8 + 9 = 24. Итак, мы разделились 24на три новых числа. Но это не было бы большой частью реактора деления без цепных реакций. Итак, давайте рекурсивно повторим процесс для этих компонентов:

       24
       /|\
      / | \
     /  |  \
    7   8   9
   / \     /|\
  3   4   / | \
 / \     /  |  \
1   2   2   3   4
           / \
          1   2

Обратите внимание, что мы останавливаем процесс всякий раз, когда число не может быть разложено на меньшие последовательные целые числа. Также обратите внимание, что мы могли бы написать 9как 4 + 5, но 2 + 3 + 4имеет больше компонентов. Деление число от Nтеперь определяется как количество целых чисел , полученных в этом процессе, в том числе Nсебя. Вышеуказанное дерево имеет 13 узлов, поэтому F(24) = 13.

Эта последовательность является записью OEIS A256504 .

Первые 40 терминов, начиная с N = 1, являются

1, 1, 3, 1, 5, 6, 5, 1, 6, 7, 12, 10, 12, 11, 12, 1, 8, 16, 14, 17, 18, 18,
23, 13, 21, 18, 22, 23, 24, 19, 14, 1, 22, 20, 23, 24, 31, 27, 25, 26

Первые 1000 терминов можно найти в этой пастбине .

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

Учитывая положительное целое число N, определите его число деления F(N). (Таким образом, вам не нужно покрывать ведущих, 0перечисленных в OEIS.)

Вы можете написать программу или функцию, принимая ввод через STDIN (или ближайшую альтернативу), аргумент командной строки или аргумент функции и выводя результат через STDOUT (или ближайшую альтернативу), возвращаемое значение функции или параметр функции (out).

Это код гольф, поэтому самый короткий ответ (в байтах) выигрывает.

Бонусный вопрос: можете ли вы найти какие-нибудь интересные свойства этой последовательности?


Я заметил, что OEIS, похоже, имеет ошибку при n = 34: начиная с n = 32, он (в настоящее время) перечисляет 1, 22, 22, 23, 24, 31, а не 1, 22, 20, 23, 24, 31.
Матмандан

1
@mathmandan Хороший улов, я, вероятно, предложу исправление (вместе с первой диаграммой).
Мартин Эндер

Связанный вызов: codegolf.stackexchange.com/questions/5703/… (и тот же вопрос по математике.SE: math.stackexchange.com/questions/139842/… )
Илмари Каронен

@mathmandan К вашему сведению, я исправил последовательность и пример сейчас, а также добавил справочную реализацию и первые 10 тысяч терминов.
Мартин Эндер

Выглядит неплохо! Спасибо за вашу работу!
Матмандан

Ответы:


16

Pyth, 23 22 21 байт

Lh&lJfqbsT.:tUb)syMeJ

Это определяет рекурсивную функцию y. Попробуйте онлайн: демонстрация

Объяснение:

L                      define a function y(b): return ...
            tUb          the list [1, 2, ..., b-1]
          .:   )         generate all consecutive sub-sequences
     f                   filter for sub-sequences T, which satisfy:
      qbsT                   b == sum(T)
    J                    and store them in J

                         return 
   lJ                        len(J)
  &                        and (if len(J) == 0 then 0 else ...)
                    eJ       last element of J (=longest sub-sequence) 
                  yM         recursive calls for all these numbers
                 s           sum
 h                         incremented by one (counting the current node)

52

Деление , 1328 989 887 797 байт

Этот ответ немного неоправданно длинен (хотелось бы, чтобы у нас были разборные регионы ) ... пожалуйста, не забудьте прокрутить мимо этого и показать другим ответам некоторую любовь!

Работа над этим кодом была тем, что вдохновило этот вызов. Я хотел добавить ответ в Fission в EOEIS, что привело меня к этой последовательности. Тем не менее, на самом деле изучение Fission и его внедрение заняло несколько недель, включая и выключая его. В то же время, последовательность действительно выросла на мне, поэтому я решил опубликовать для нее отдельную задачу (плюс, в любом случае, EOEIS не был бы слишком далеко внизу дерева).

Итак, я представляю вам, чудовище:

 R'0@+\
/  Y@</ /[@ Y]_L
[? % \  / \ J
   \$@  [Z/;[{+++++++++L
UR+++++++++>/;
9\   ;    7A9
SQS  {+L  /$     \/\/\/\/\/   5/ @  [~ &@[S\/ \  D /8/
~4X /A@[  %5                   /; &    K  } [S//~KSA /
  3    \  A$@S  S\/  \/\/\/   \/>\ /S]@A  /  \ { +X
W7           X  X    /> \      +\ A\ /   \ /6~@/ \/
        /   ~A\;     +;\      /@
    ZX [K    / {/  / @  @ }  \ X @
       \AS   </      \V /    }SZS S/
         X   ;;@\   /;X  /> \ ; X X
 ;       \@+  >/ }$S SZS\+;    //\V
           / \\  /\; X X @  @  \~K{
           \0X /     /~/V\V /   0W//
    \        Z      [K \  //\
W       /MJ $$\\ /\7\A  /;7/\/ /
       4}K~@\ &]    @\  3/\
 /     \{   }$A/1 2  }Y~K <\
[{/\  ;@\@  /   \@<+@^   1;}++@S68
@\ <\    2        ;   \    /
$  ;}++ +++++++L
%@A{/
M  \@+>/
~     @
SNR'0YK
  \  A!/

Ожидается, что на входе нет завершающего символа новой строки, так что вы можете назвать его как echo -n 120 | ./Fission oeis256504.fis.

Компоновка, возможно, все еще может быть более эффективной, поэтому я думаю, что здесь еще много возможностей для совершенствования (например, она содержит 911 581 461 374 пробелов).

Прежде чем мы перейдем к объяснению, обратите внимание на проверку: официальный переводчик работает не совсем так, как есть. а) Mirror.cppне компилируется во многих системах. Если вы столкнулись с этой проблемой, просто закомментируйте ошибочную строку - уязвимый компонент (случайное зеркало) не используется в этом коде. б) Есть пара ошибок, которые могут привести к неопределенному поведению (и, скорее всего, для такой сложной программы). Вы можете применить этот патч, чтобы исправить их. Как только вы это сделаете, вы сможете скомпилировать интерпретатор с

g++ -g --std=c++11 *.cpp -o Fission

Интересный факт: эта программа использует почти все компоненты, которые может предложить Fission, за исключением #(случайное зеркало), :(полупрозрачное зеркало) -или |(обычное зеркало) и "(режим печати).

Что на земле?

Предупреждение: это будет довольно долго ... Я предполагаю, что вы действительно заинтересованы в том, как работает Fission и как можно программировать в ней. Потому что если нет, я не уверен, как я мог бы обобщить это. (Следующий абзац дает общее описание языка.)

Fission - это двумерный язык программирования, где потоки данных и управления представлены атомами, движущимися через сетку. Если вы видели или использовали Marbelous раньше, концепция должна быть смутно знакомой. Каждый атом имеет два целочисленных свойства: неотрицательную массу и произвольную энергию. Если масса становится отрицательной, атом удаляется из сетки. В большинстве случаев вы можете рассматривать массу как «значение» атома, а энергию как некое мета-свойство, которое используется несколькими компонентами для определения потока атомов (то есть большинство видов переключателей зависят от знака энергия). Я буду обозначать атомы (m,E), когда это необходимо. В начале программы сетка начинается со связки(1,0)атомы, где бы вы ни находились, из четырех компонентов UDLR(где буква указывает направление, в котором атом движется изначально). Затем доска заполняется целой кучей компонентов, которые изменяют массу и энергию атомов, меняют их направления или делают другие более сложные вещи. Полный список см. На странице esolangs , но я представлю большинство из них в этом объяснении. Другим важным моментом (который программа использует несколько раз) является то, что сетка является тороидальной: атом, который сталкивается с любой из сторон, вновь появляется на противоположной стороне, двигаясь в том же направлении.

Я написал программу в нескольких небольших частях и собрал их в конце, так что вот как я объясню.

atoi

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

Fission может считывать только байтовые значения отдельных символов, а не целых чисел. Несмотря на то, что это приемлемая практика , я решил, что могу сделать это правильно и проанализировать действительные целые числа в STDIN. Вот atoiкод:

     ;
 R'0@+\
/  Y@</ /[@ Y]_L
[? % \  / \ J 
   \$@  [Z/;[{+++++++++L
UR+++++++++>/;
           O

Двумя наиболее важными компонентами деления являются реакторы деления и синтеза. Реакторы деления являются любыми из V^<>(код выше использует <и >). Реактор деления может хранить атом (отправляя его в клин персонажа), по умолчанию (2,0). Если атом достигнет вершины персонажа, два новых атома будут отосланы в стороны. Их масса определяется путем деления поступающей массы на сохраненную массу (т.е. делится пополам по умолчанию) - левый атом получает это значение, а правый атом получает остаток массы (т. Е. Масса сохраняется при делении) , Оба исходящих атома будут иметь входящую энергию минуснакопленная энергия. Это означает, что мы можем использовать реакторы деления для арифметики - как для вычитания, так и для деления. Если реактор деления попадает с площадки, атом просто отражается по диагонали и затем будет двигаться в направлении вершины персонажа.

Реакторы Fusion являются любыми из YA{}(код выше использует Yи {). Их функция аналогична: они могут хранить атом (по умолчанию (1,0)), и при попадании из вершины два новых атома будут отправлены в стороны. Однако в этом случае два атома будут идентичны, всегда сохраняя поступающую энергию и умножая поступающую массу на сохраненную массу. То есть по умолчанию термоядерный реактор просто дублирует любой атом, попадающий в его вершину. При ударе сбоку реакторы термоядерного синтеза немного сложнее: атом тожесохраняется (независимо от другой памяти), пока атом не попадет на противоположную сторону. Когда это происходит, новый атом высвобождается в направлении вершины, масса и энергия которой являются суммой двух старых атомов. Если новый атом попадает на ту же сторону до того, как соответствующий атом достигает противоположной стороны, старый атом просто перезаписывается. Реакторы синтеза могут быть использованы для осуществления сложения и умножения.

Другой простой компонент , я хочу , чтобы выйти из пути есть [и ]которые просто установить направление атома направо и налево, соответственно (независимо от входящего направления). Вертикальными эквивалентами являются M(вниз) и W(вверх), но они не используются для atoiкода. UDLRтакже действовать как WM][после освобождения их начальных атомов.

В любом случае, давайте посмотрим на код там. Программа начинается с 5 атомов:

  • RИ Lна дне просто получить их прирост массы (с +) , чтобы стать , (10,0)а затем хранили в делении и термоядерного реактора, соответственно. Мы будем использовать эти реакторы для анализа ввода base-10.
  • В Lверхнем правом углу масса уменьшается (с _), чтобы стать (0,0)и хранится в стороне от термоядерного реактора Y. Это нужно для того, чтобы отслеживать число, которое мы читаем - мы будем постепенно увеличивать и умножать это по мере того, как будем читать цифры.
  • В Rверхнем левом углу его масса устанавливается в код символа 0(48) с '0, затем масса и энергия меняются местами @и, наконец, масса увеличивается один раз, +чтобы дать (1,48). Затем он перенаправляется с диагональными зеркалами \и /хранится в реакторе деления. Мы будем использовать 48вычитание для преобразования входных данных ASCII в фактические значения цифр. Нам также пришлось увеличить массу, 1чтобы избежать деления на 0.
  • Наконец, Uнижний левый угол - это то, что фактически приводит все в движение и изначально используется только для управления потоком.

После перенаправления вправо управляющий атом попадает ?. Это входной компонент. Он читает символ и устанавливает массу атома в значение ASCII для чтения, а энергию - в 0. Если вместо этого мы нажмем EOF, энергия будет установлена ​​на 1.

Атом продолжается, а затем ударяет %. Это переключатель зеркала. Для неположительной энергии это действует как /зеркало. Но для положительной энергии она действует как a \(а также уменьшает энергию на 1). Поэтому, пока мы читаем символы, атом будет отражаться вверх, и мы можем обработать символ. Но когда мы закончим с вводом, атом будет отражен вниз, и мы можем применить другую логику для получения результата. К вашему сведению, противоположный компонент есть &.

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

Сначала атом персонажа попадает в (по умолчанию) термоядерный реактор Y. Это разделяет атом, и мы используем левую копию в качестве управляющего атома, чтобы вернуться к компоненту ввода и прочитать следующий символ. Правильная копия будет обработана. Рассмотрим случай, когда мы прочитали персонажа 3. Наш атом будет (51,0). Мы поменялись массой и энергией с тем @, чтобы мы могли воспользоваться вычитанием следующего реактора деления. Реактор вычитает 48энергию (без изменения массы), поэтому он отсылает две копии (0,3)- энергия теперь соответствует цифре, которую мы прочитали. Исходящая копия просто отбрасывается ;(компонент, который просто уничтожает все входящие атомы). Мы продолжим работать с исходящей копией. Вам нужно будет следовать по его пути через/и \немного отражает.

@Непосредственно перед термоядерным реактором SWAPS массы и энергии снова, таким образом, что мы добавим (3,0)к нашему нарастающему итогу в Y. Обратите внимание, что сам промежуточный итог всегда будет иметь 0энергию.

Сейчас Jпрыжок. То, что он делает, это скачок любого входящего атома вперед его энергией. Если это так 0, атом просто продолжает двигаться прямо. Если это 1будет пропустить одну ячейку, если 2это будет пропустить две ячейки и так далее. Энергия расходуется на прыжок, поэтому атом всегда заканчивается энергией 0. Поскольку текущий итог имеет нулевую энергию, скачок пока игнорируется, и атом перенаправляется в термоядерный реактор, {который умножает его массу на 10. Нисходящая копия сбрасывается, в ;то время как исходная копия возвращается в Yреактор в качестве новой рабочей суммы.

Вышеприведенное повторяется (забавным конвейерным способом, где новые цифры обрабатываются до того, как будут выполнены предыдущие), пока мы не нажмем EOF. Теперь %атом отправит вниз. Идея заключается в том , чтобы превратить этот атом в (0,1)теперь перед ударять работает общий реактор так , что а) общее не влияет (нулевую массу) и б) мы получаем энергию 1перепрыгнуть [. Мы можем легко заботиться об энергии $, которая увеличивает энергию.

Проблема в том, что при ?сбросе EOF масса не сбрасывается, поэтому масса все равно будет равна массе последнего прочитанного символа, а энергия будет 0(потому что %уменьшена 1обратно 0). Итак, мы хотим избавиться от этой массы. Для этого мы переставляем массу и энергию @снова.

Мне нужно ввести еще один компонент , прежде чем закончить этот раздел: Z. По сути это то же самое, что %и &. Разница заключается в том, что он позволяет атомам положительной энергии проходить прямо (при уменьшении энергии) и отклоняет атомы неположительной энергии на 90 градусов влево. Мы можем использовать это для устранения энергии атома, повторяя ее Zснова и снова - как только энергия исчезнет, ​​атом отклонится и покинет цикл. Вот этот шаблон:

/ \
[Z/

где атом будет двигаться вверх, когда энергия равна нулю. Я буду использовать этот шаблон в той или иной форме несколько раз в других частях программы.

Таким образом , когда атом покидает эту маленькую петлю, он будет (1,0)и мест , чтобы (0,1)самые @до попадания в реакторе слитого выпустить конечный результат ввода. Тем не менее, промежуточный итог будет отключен в 10 раз, потому что мы предварительно умножили его еще на одну цифру.

Так что теперь с энергией 1этот атом будет пропускать [и прыгать в /. Это отклоняет его в реактор деления, который мы подготовили разделить на 10 и исправить наше постороннее умножение. Опять же, мы отбрасываем одну половину с помощью ;и оставляем другую в качестве выходных данных (здесь представлено с помощью, Oкоторая просто напечатает соответствующий символ и уничтожит атом - в полной программе мы продолжаем использовать атом вместо этого).

itoa

           /     \
input ->  [{/\  ;@
          @\ <\   
          $  ;}++ +++++++L
          %@A{/
          M  \@+>/
          ~     @
          SNR'0YK
            \  A!/

Конечно, нам также нужно преобразовать результат обратно в строку и распечатать его. Вот для чего эта часть. Это предполагает, что ввод поступает не раньше тика 10 или около того, но в полной программе это легко дается. Этот бит можно найти в нижней части полной программы.

Этот код представляет новый очень мощный компонент Fission: стек K. Стек изначально пуст. Когда атом с неотрицательной энергией попадает в стек, атом просто помещается в стек. Когда атом с отрицательной энергией попадает в стек, его масса и энергия будут заменены атомом на вершине стека (который таким образом выталкивается). Однако если стек пуст, направление атома меняется на противоположное, и его энергия становится положительной (т.е. умножается на -1).

Хорошо, вернемся к фактическому коду. Идея itoaфрагмента состоит в том, чтобы многократно использовать вход по модулю 10, чтобы найти следующую цифру, а целочисленное деление ввода на 10 для следующей итерации. Это даст все цифры в обратном порядке (от наименее значимого до наиболее значимого). Чтобы исправить порядок, мы помещаем все цифры в стопку и в конце выталкиваем их одну за другой, чтобы распечатать.

Верхняя половина кода выполняет вычисление цифр: Lплюсы дают 10, которые мы клонируем и подаем в реактор деления и термоядерного синтеза, так что мы можем делить и умножать на 10. Цикл, по сути, начинается после [в верхнем левом углу. , Текущее значение делится: одна копия делится на 10, затем умножается на 10 и сохраняется в реакторе деления, в который затем попадает другая копия на вершине. Это вычисляется i % 10как i - ((i/10) * 10). Также обратите внимание, что Aпосле деления и перед умножением разбивает промежуточный результат, так что мы можем ввести его i / 10в следующую итерацию.

%Прервет цикл когда переменные итерации хитов 0. Так как это более или менее делать-то время цикла, этот код будет работать даже для печати 0(без создания ведущих нулей в противном случае). Как только мы покидаем цикл, мы хотим очистить стек и вывести цифры. Sэто противоположность Z, так что это переключатель, который будет отклонять входящий атом с неположительной энергией на 90 градусов вправо. Таким образом, атом фактически перемещается через край от Sпрямой к точке, Kчтобы выскочить из цифры (обратите внимание на ~то, что у входящего атома есть энергия -1). Эта цифра увеличивается на единицу, 48чтобы получить код ASCII соответствующего символа цифры. AРасщепляет цифру напечатать одну копию с!и подайте другую копию обратно в Yреактор для следующей цифры. Напечатанная копия используется в качестве следующего триггера для стопки (обратите внимание, что зеркала также посылают ее по краю, чтобы ударить Mслева).

Когда стек пуст, он Kбудет отражать атом и превращать его энергию так +1, чтобы он проходил прямо через S. Nпечатает новую строку (просто потому, что она аккуратная :)). И тогда атом идет R'0снова, чтобы оказаться в стороне Y. Так как вокруг нет никаких атомов, это никогда не будет выпущено, и программа завершится.

Вычисление числа деления: рамки

Давайте перейдем к фактическому содержанию программы. Код в основном является портом моей эталонной реализации Mathematica:

fission[n_] := If[
  (div = 
    SelectFirst[
      Reverse@Divisors[2 n], 
      (OddQ@# == IntegerQ[n/#] 
       && n/# > (# - 1)/2) &
    ]
  ) == 1,
  1,
  1 + Total[fission /@ (Range@div + n/div - (div + 1)/2)]
]

где divчисло целых чисел в максимальном разделе.

Основные отличия в том, что мы не можем иметь дело с полуцелыми значениями в Fission, поэтому я делаю много вещей, умноженных на два, и что в Fission нет рекурсии. Чтобы обойти это, я помещаю все целые числа в раздел в очередь для последующей обработки. Для каждого числа, которое мы обрабатываем, мы увеличиваем счетчик на единицу, а когда очередь пуста, мы освобождаем счетчик и отправляем его на печать. (Очередь Qработает точно так же K, как в порядке FIFO.)

Вот основа для этой концепции:

                      +--- input goes in here
                      v 

                     SQS ---> compute div from n          D /8/
                     ~4X               |                /~KSA /
                       3               +----------->    { +X
initial trigger ---> W                               6~@/ \/
                              4                   
                     W        ^                     /
                              |              3
                     ^     generate range    |
                     |     from n and div  <-+----- S6
                     |         -then-      
                     +---- release new trigger

Наиболее важными новыми компонентами являются цифры. Это телепорты. Все телепорты с одной цифрой принадлежат друг другу. Когда атом попадает в любой телепорт, он будет немедленно перемещать следующего телепорта в той же группе, где следующий определяется в обычном порядке слева направо, сверху вниз. Это не обязательно, но помогает с макетом (и, следовательно, немного в гольф). Существует также тот, Xкоторый просто дублирует атом, отправляя одну копию прямо вперед, а другую назад.

К настоящему времени вы могли бы самостоятельно разобраться с большинством фреймворков. В верхнем левом углу находится очередь значений, которые еще предстоит обработать, и выпускаются по одному nза раз. Одна копия nтелепортируется в нижнюю часть, потому что она нужна нам при вычислении диапазона, другая копия переходит в блок в верхней части, который вычисляет div(это, безусловно, самый большой раздел кода). После divвычисления он дублируется - одна копия увеличивает счетчик в верхнем правом углу, который хранится в K. Другая копия телепортируется вниз. Если divэто так 1, мы немедленно отклоняем его вверх и используем в качестве триггера для следующей итерации, не ставя в очередь новые значения. В противном случае мы используем divиn в разделе внизу, чтобы создать новый диапазон (то есть поток атомов с соответствующими массами, которые впоследствии помещаются в очередь), а затем отпустите новый триггер после того, как диапазон будет завершен.

Как только очередь опустеет, триггер будет отражен, пройдя прямо через Sи снова появившись в верхнем правом углу, где он освобождает счетчик (конечный результат), из Aкоторого затем телепортируется itoaчерез 8.

Вычисление числа деления: тело цикла

Таким образом, все, что осталось, - это две секции для вычисления divи генерации диапазона. Вычислительная divэто часть:

 ;    
 {+L  /$     \/\/\/\/\/   5/ @  [~ &@[S\/ \
/A@[  %5                   /; &    K  } [S/
   \  A$@S  S\/  \/\/\/   \/>\ /S]@A  /  \ 
         X  X    /> \      +\ A\ /   \ /
    /   ~A\;     +;\      /@
ZX [K    / {/  / @  @ }  \ X @
   \AS   </      \V /    }SZS S/
     X   ;;@\   /;X  /> \ ; X X
     \@+  >/ }$S SZS\+;    //\V
       / \\  /\; X X @  @  \~K{
       \0X /     /~/V\V /   0W//
\        Z      [K \  //\
           \ /\7\A  /;7/\/

Вы, наверное, уже видели достаточно, чтобы разобраться в этом с некоторым терпением. Разбивка высокого уровня такова: первые 12 столбцов или около того генерируют поток делителей 2n. Следующие 10 столбцов отфильтровывают те, которые не удовлетворяют OddQ@# == IntegerQ[n/#]. Следующие 8 столбцов отфильтровывают те, которые не удовлетворяют n/# > (# - 1)/2). Наконец, мы помещаем все действительные делители в стек, и как только мы закончим, мы опустошаем весь стек в реактор слияния (перезаписывая все, кроме последнего / самого большого делителя), а затем высвобождаем результат, после чего удаляем его энергию (которая была не Ноль от проверки неравенства).

Там много безумных путей, которые на самом деле ничего не делают. Преимущественно, \/\/\/\/безумие наверху ( 5s также являются его частью) и один путь вокруг дна (который проходит через 7s). Я должен был добавить их, чтобы справиться с некоторыми неприятными условиями гонки. Деление может использовать компонент задержки ...

Код, который генерирует новый диапазон из nи divэто:

 /MJ $$\
4}K~@\ &]    @\  3/\
\{   }$A/1 2  }Y~K <\
 \@  /   \@<+@^   1;}++@
  2        ;   \    /

Сначала мы вычисляем n/div - (div + 1)/2(оба условия скомпонованы, что дает одинаковый результат) и сохраняем их для дальнейшего использования. Затем мы генерируем диапазон divсверху вниз 1и добавляем сохраненное значение к каждому из них.

В обоих из них есть два новых общих паттерна, которые я должен упомянуть: один - SXили ZXудар снизу (или повернутые версии). Это хороший способ дублировать атом, если вы хотите, чтобы одна копия шла прямо (поскольку перенаправление выходов термоядерного реактора иногда может быть громоздким). В Sили Zвращается атом на , Xа затем поворачивает зеркальную копию обратно в исходное направление распространения.

Другая модель

[K
\A --> output

Если мы храним какое-либо значение, Kмы можем повторно получить его, ударяя Kотрицательной энергией сверху. AДублирует значение мы заинтересованы в том, и посылает то , что копировать правый на стек на следующий раз , когда нам это нужно.

Что ж, это был настоящий том ... но если вы на самом деле справились с этим, надеюсь, у вас возникла идея, что Fission i͝s̢̘̗̗ ͢i̟nç̮̩r̸̭̬̱͔e̟̹̟̜͟d̙i̠͙͎̖͓̯b̘̠͎̭̰̼l̶̪̙̮̥̮y̠̠͎̺͜ ͚̬̮f̟͞u̱̦̰͍n͍ ̜̠̙t̸̳̩̝o ̫͉̙͠p̯̱̭͙̜͙͞ŕ̮͓̜o̢̙̣̭g̩̼̣̝r̤͍͔̘̟ͅa̪̜͇m̳̭͔̤̞ͅ ͕̺͉̫̀ͅi͜n̳̯̗̳͇̹.̫̞̲̞̜̳


1
Now with 100% fewer scrollbars.так вы говорите .. и это все еще будет продолжение ...
Оптимизатор

13
Все еще более читабельно, чем большинство кода, который выкачивают наши младшие разработчики.
corsiKa

Как создатель Fission, даже я еще не написал такую ​​большую программу! Я впечатлен! Ваше объяснение впечатляет и может послужить учебным пособием по языку.
C0deH4cker

Кроме того, последняя строка вашего ответа выглядит как программа Fission;)
C0deH4cker

12

CJam, 42 41 байт

ri]{_{:X,:)_few:+W%{1bX=}=}%{,(},e_}h]e_,

Простой обход в ширину и условие остановки пустого следующего уровня.

Как это работает :

ri]                                       e# This represents the root of the fissile tree
   {                               }h     e# Now we run a do-while loop
    _{                    }%              e# Copy the nodes at the current level and map them
                                          e# via this code block to get next level nodes
      :X,:)                               e# Store the node value in X and get array [1..X]
           _few                           e# Copy the array and get continuous slices of
                                          e# length 1 through X from the array [1..X]
               :+W%                       e# Right now, we have an array of array with each
                                          e# array containing slice of same length. We join
                                          e# those arrays and reverse them to get slices of
                                          e# higher length in front of lower lengths
                   {1bX=}=                e# Choose the first slice whose sum is same as X
                                          e# The reversal above makes sure that we give
                                          e# preference to slice of higher length in case of
                                          e# multiple slices add up to X
                            {,(},         e# Filter out slices of length 1 which basically
                                          e# mean that the current node cannot be split up
                                 e_       e# Join all slices in a single array. This is our
                                          e# next level in the Fissile tree. If this is empty
                                          e# it means that all no further node can be
                                          e# decomposed. In an {}h do-while loop, this fact
                                          e# itself becomes the stopping condition for the
                                          e# loop
                                     ]e_, e# Wrap all levels in an array. Flatten the array
                                          e# and take its length

Попробуйте онлайн здесь


Это может быть, вероятно, до 35 байтов. Я пытаюсь понять, как ..
Оптимизатор

10

Python 3, 112 байт

def f(n,c=0):
 d=n-c;s=(n-d*~-d/2)/d
 return(s%1or s<1)and f(n,c+1)or+(d<2)or-~sum(f(int(s)+i)for i in range(d))

4 байта сохранены благодаря @FryAmTheEggman.

Объяснение будет позже ...

Бонус факт: каждая степень 2 имеет число деления 1. Это потому, что сумма последовательности четной длины всегда является суммой двух средних чисел, которая является нечетной, умноженной на половину длины последовательности, которая является целым числом , Сумма последовательности нечетной длины - это среднее число, умноженное на длину последовательности, которая является нечетной. Таким образом, поскольку степень 2 не имеет нечетного делителя, она может быть выражена только как сумма самого себя.


2 + 3 + 4 + 5 = 14, что не странно. Ваш аргумент для последовательностей четной длины должен быть изменен на «сумма последовательности четной длины - это сумма двух средних чисел, которая является нечетной, умноженной на половину длины». Остальная часть вашего утверждения проходит без изменений.
Бруно Ле Флох

@BrunoLeFloch Спасибо! Исправленный.
Рандомра

Разве ваш титул не должен отражать улучшения? то есть <strike> 117 </ strike> <strike> 113 </ strike> 112
corsiKa

@corsiKa Обычно я делаю это только для серьезных улучшений. В противном случае было бы слишком много вычеркнутых чисел.
Рандомра

8

Python 2, 111 102 97 байт

Несколько читабельно:

def f(n,c=0):a=n-c;b=n-a*~-a/2;return 1/a or-~sum(map(f,range(b/a,b/a+a)))if b>b%a<1else f(n,c+1)

Не слишком читаем:

def f(n,a=0):b=n-a*~-a/2;return b>0and(f(n,a+1)or b%a<1and(1/a or-~sum(map(f,range(b/a,b/a+a)))))

Оба 97 байта.

bЯвляется ли nминус (a-1)thтреугольное число. Если b % a == 0, то nэто сумма aпоследовательных чисел, начиная с b.


8
Раньше я считал Python общедоступным языком, пока не присоединился к PPCG.
Алекс А.

Я думаю, вам нужно улучшить определение читабельности ..: P
Оптимизатор

Python 2 не позволяет 1else. Работает только 2-е решение. Только в Python 3 elseможно сразу следовать за номером.
mbomb007

@ mbomb007 Насколько мне известно, он отлично работает с 2.7.8 и далее
Sp3000

Хорошо, я использовал 2.7.2.
mbomb007

7

Python 2, 86

f=lambda n,R={1}:n-sum(R)and f(n,R^{[min(R),max(R)+1][n>sum(R)]})or-~sum(map(f,R-{n}))

Менее гольф:

def f(n,R={1}):
    d=sum(R)-n
    if d==0:return (sum(map(f,R-{n}))
    if d<0:return f(n,R|{max(R)+1})
    if d>0:return f(n,R-{min(R)})

Идея состоит в том, чтобы протестировать потенциальные последовательности последовательных целых чисел, которые в сумме n. Прогон сохраняется непосредственно как набор, Rа не через его конечные точки.

Мы проверяем, как сумма текущего прогона сравнивается с желаемой суммой nчерез их разность.

  • Если сумма слишком велика, мы вырезаем самый маленький элемент в серии.
  • Если сумма слишком мала, мы увеличиваем пробег, увеличивая его максимум на 1.
  • Если сумма верна, мы рекурсируем, отображаем fна прогон, суммируем и добавляем 1 для текущего узла. Если запуск выполнен {n}, мы перепробовали все нетривиальные возможные суммы, остановите рекурсию, удалив nсначала.

Спасибо Sp3000 за сохранение 3 символов.


7

Python 2, 85

Я очень горжусь этим ответом, потому что он уже занимает десятки секунд для n = 9 и 5-10 минут для n = 10. В коде гольф это считается желательным атрибутом программы.

f=lambda n,a=1,d=1:a/n or[f(a)+f(n-a,1+1%d*a)+1/d,f(n,a+d/n,d%n+1)][2*n!=-~d*(2*a+d)]

Существует также версия с коротким замыканием, которая не занимает много времени и использует такое же количество байтов:

f=lambda n,a=1,d=1:a/n or~d*(2*a+d)+n*2and f(n,a+d/n,d%n+1)or f(a)+f(n-a,1+1%d*a)+1/d 

Это может быть быстрее, но, по крайней мере, оно превышает предельное значение рекурсии по умолчанию, когда n становится немного выше 40.

Идея состоит в том, чтобы выполнить перебор чисел aи так d, чтобы a + a+1 + ... + a+d == nпри значениях от 1 до n. f(n,a+d/n,d%n+1)Ветвь рекурсии перебирает (a, d)пар. В случае, когда равенство выполнено, мне удается избежать дорогостоящего map(range(...))вызова, разбив его на две ветви, независимо от длины последовательности. Числа a+1через dкоторые сосредоточенные в один вызов f, установив aпараметр так , что другой способ , чтобы разделить последовательность не может быть использован.


Как это работает?
xnor

«Я очень горжусь этим ответом, потому что это уже занимает десятки секунд для n = 9 и 5-10 минут для n = 10. В коде гольф это считается желательным атрибутом программы». +1 только за это.
Сохам Чоудхури

6

Haskell, 76 69 байт

f x=head$[1+sum(map f[y..z])|y<-[1..x-1],z<-[y..x],sum[y..z]==x]++[1]

Использование:

*Main> map f [1..40]
[1,1,3,1,5,6,5,1,6,7,12,10,12,11,12,1,8,16,14,17,18,18,23,13,21,18,22,23,24,19,14,1,22,20,23,24,31,27,25,26]

Как это устроено:

[  [y..z] |y<-[1..x-1],z<-[y..x],sum[y..z]==x]

           make a list of lists with all consecutive integers (at least 2 elements)
           that sum up to x, sorted by lowest number, e.g. 9 -> [[2,3,4],[4,5]].

1+sum(map f[...]) 

           recursively calculate the Fission Number for each list

[...]++[1]

           always append the 1 to the list of Fission Numbers.

head

           take the first element, which is either the Fission Number of the
           longest list or if there's no list, the 1 appended in the step before.  

3

Сетчатка , 66 байт

^|$
,
(`,(1+?)(?=(?<1>1\1)+\D)
$0;
+)`,(1*);1\1
,$1,1$1;
^,|1

.
1

Принимает ввод и печатает вывод в одинарном формате.

Вы можете поместить каждую строку в один файл или запустить код как есть с -sфлагом. Например:

> echo -n 1111111|retina -s fission
11111

Объяснение:

  • Мы сохраняем разделенный запятыми список чисел, которые будут разделены.
  • Для каждого числа мы берем наименьшее начальное значение, которое может создать допустимый сплитуп, и отделяем его от остальных точкой с запятой.
  • Если внутри числа есть точка с запятой, мы заменяем ее запятой и разделяем следующую правильную по размеру (длину предыдущего элемента + 1) часть числа.
  • Мы повторяем шаги 2 и 3, пока не произойдут изменения.
  • Мы получаем запятую для каждого листа и точку с запятой для каждого внутреннего узла плюс дополнительную запятую, потому что мы начали с двух запятых. Таким образом, мы удаляем запятую и части чисел 1и преобразуем остальные в 1.

Состояния строки на протяжении всего процесса с вводом 11111111111111 (unary 14):

,11111111111111,
,11;111111111111,
,11,1;11,1111,11;111;,
,11,1,11;,1111,11,1;11;;,
,11,1,11;,1111,11,1,11;;;,
,,;,,,,;;;,
11111111111

Большое спасибо за @MartinButtner за помощь в чате!


3

CJam (43 байта)

qi,{):X),_m*{{_)*2/}/-X=}=~\,>0\{~$+}/)}%W=

Онлайн демо

Я уверен, что мне не хватает некоторых трюков с продвинутыми циклами, но это аккуратно использует свойство CJam (которое ранее раздражало меня), что внутри %карты результаты остаются в стеке, и поэтому могут быть доступны $с помощью отрицательное смещение.


Завтра я посмотрю поближе, но для начала вам не нужно ,в начале. /и %и несколько других неявно превращают числа в диапазоны.
Мартин Эндер

,_m*можно заменить на 2m*. Формула арифметической прогрессии может быть заменена на ~,>:Y_,+:+и ~\,>0\ становится !Y. Наконец, если вы используете {}#вместо {}=, вам не нужно )после X. Собираем все вместе:ri{):X2m*{~,>:Y_,+:+X=}#!Y{~$+}/)}%W=
Деннис

2

Go, 133 байта

func 算(n int)int{Σ:=1;for i:=0;i<n;i++{for j:=0;j<n;j++{if i*i+i-j*j-j==2*n{for k:=j+1;k<=i;k++{Σ+=算(k)};j,i=n,n}}};return Σ}

Это мой первый код гольф, извините, если я допустил какие-либо ошибки.

При этом используется идея о том, что делящаяся «композиция» также может рассматриваться как разница между двумя последовательностями упорядоченных целых чисел. Для примера возьмем расщепляющуюся «композицию» для числа 13. Это 6,7. Но это можно рассматривать как сумму целых чисел 1 ... 7 минус сумма целых чисел 1 ... 5

  A: 1 2 3 4 5 6 7  sum = 28
  B: 1 2 3 4 5      sum = 15 
A-B:           6 7  sum = 13, which is also 28-15 = 13

Напомним формулу из школьных дней Гаусса, сумма 1 ... n = (n ^ 2 + n) / 2. Таким образом, чтобы найти композицию последовательных целых чисел для данного n, мы могли бы также сказать, что мы ищем «конечные точки» p и q в диапазоне 1 ... n так, чтобы (p ^ 2 + p) / 2 - ( q ^ 2 + q) / 2 = n. В приведенном выше примере мы искали бы «конечные точки» 5 и 7, потому что 7 ^ 2 + 7 = 56/2, 5 ^ 2 + 5 = 30/2, 56 / 2-30 / 2 = 28-15 = 13

Теперь есть несколько возможных способов составления числа, как отметил Мартин, 9 = 2 + 3 + 4, но также и 4 + 5. Но кажется очевидным, что «самая низкая» начальная последовательность также будет самой длинной, потому что она требует больше маленькие числа для суммирования до большего числа, чем для средних чисел. (У меня нет доказательств к сожалению)

Таким образом, чтобы найти композицию из 9, протестируйте каждую «пару конечных точек», p и q, итерируя оба p и q отдельно от 0 до 9, и проверьте, если p ^ p + p / 2 - q ^ 2 + q / 2 = 9. Или, проще, умножьте уравнение на 2, чтобы избежать проблем округления при округлении и сохранить всю математику в целых числах. Тогда мы ищем p и q такие, что (p ^ p + p) - (q ^ q + q) = 9 * 2. Первое совпадение, которое мы найдем, будет конечными точками расщепляющейся композиции, потому что, как отмечалось, самая низкая группа чисел также будет самой длинной, и мы ищем от низкого до высокого (от 0 до 9). Мы вырываемся из цикла, как только мы находим соответствие.

Теперь рекурсивная функция находит эти «делящиеся конечные точки» p и q для заданного n, а затем вызывает себя для каждого из «потомков» в дереве от p до q. Для 9 он найдет 1 и 4, (20-2 = 18), затем перезвонит себе на 2, 3 и 4, суммируя результаты. Для чисел, подобных 4, он просто не находит совпадения и возвращает «1». Это может быть сокращено, но это как моя третья программа go, и я не эксперт по рекурсии.

Спасибо за чтение.


Хорошо сделано! Но почему имена функций / переменных Unicode. Это стоит ненужных байтов, и, конечно, вы могли бы просто использовать обычное письмо?
Мартин Эндер

Спасибо за ваш добрый комментарий. Но я спросил себя, почему бы не посчитать символы вместо байтов :)
Дон Яркий

Потому что это правила по умолчанию здесь. :) Причина, по которой мы обычно считаем байты, а не символы, заключается в том, что в противном случае это происходит , что не очень интересно для всех участвующих сторон. ;) (Тем не менее, любой вызов автор является свободно указать подсчет по символам вместо байт, но я специально не делал).
Мартин Эндер

1

CJam, 40 35 33 байта

ri{__,f-_few:+{1b1$=}=|{F}*}:F~],

Спасибо @Optimizer за предложение few, которое сэкономило 2 байта.

Попробуйте онлайн в интерпретаторе CJam .

Как это устроено

ri      e# Read an integer from STDIN.
{       e# Define function F(X):
  _     e# Push X.
  _,    e# Push [0 ... X-1].
  f-    e# Subract each from X. Pushes Y := [X ... 1].
  _few  e# Push all overlapping slices of Y of length in Y.
  :+    e# Consolidate the slices of different lenghts in a single array.
  {     e# Find the first slice S such that...
    1b  e#   the sum of its elements...
    1$= e#   equals X.
  }=    e#   Since Y is in descending order, the first matching slice is also the longest.
  |     e# Set union with [X]. This adds X to the beginning of the S if S != [X].
  {F}*  e# Execute F for each element of S except the first (X).
}:F     e#
~       e# Execute F for the input.
],      e# Count the integers on the stack.

Если вы объедините мою первую половину со второй, вы получите 34:ri{_,:)_few:+W%{1b1$=}=|{F}*}:F~],
Оптимизатор

@ Оптимизатор: Хорошо. Это позволяет дополнительное улучшение. Спасибо!
Деннис
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.