Если задано положительное целое число N , ваша задача состоит в том, чтобы возвратить количество шагов, необходимых для достижения N следующим алгоритмом :
Найти наименьшее треугольное число Т я такое , что Т я ≥ Н . Постройте соответствующий список L = [1, 2, ..., i] .
Хотя сумма членов L больше, чем N , удалите первый член из списка.
Если сумма членов L теперь меньше N , увеличьте i и добавьте его в список. Продолжайте с шага № 2.
Мы останавливаемся, как только N достигнут. Только первый шаг выполняется систематически. Шаги № 2 и № 3 могут не обрабатываться вообще.
Примеры
Ниже приведен пример для N = 11 :
Таким образом, ожидаемый результат для N = 11 равен 4 .
Другие примеры:
- N = 5 - мы начинаем с T 3 = 1 + 2 + 3 = 6 , затем 2 + 3 = 5 . Ожидаемый результат: 2 .
- N = 10 - требуется только первый шаг, потому что 10 - это треугольное число: T 4 = 1 + 2 + 3 + 4 = 10 . Ожидаемый результат: 1 .
Первые 100 значений
Ниже приведены результаты для 1 ≤ N ≤ 100 :
1, 2, 1, 4, 2, 1, 2, 10, 2, 1, 4, 2, 6, 2, 1, 22, 8, 2, 10, 2,
1, 2, 12, 6, 2, 4, 2, 1, 16, 2, 18, 50, 2, 6, 2, 1, 22, 6, 2, 4,
26, 2, 28, 2, 1, 8, 30, 16, 2, 6, 4, 2, 36, 2, 1, 2, 4, 12, 40, 2,
42, 14, 2,108, 2, 1, 46, 2, 6, 4, 50, 2, 52, 18, 2, 4, 2, 1, 56, 12,
2, 20, 60, 4, 2, 22, 10, 2, 66, 2, 1, 4, 10, 24, 2, 40, 72, 8, 2, 6
правила
- Вы можете написать либо полную программу, либо функцию, которая либо печатает, либо возвращает результат.
- Вам необходимо обработать любое N ≤ 65536 менее чем за одну минуту на оборудовании среднего уровня.
- Если у вас достаточно времени, ваша программа / функция теоретически должна работать для любого значения N , которое изначально поддерживается вашим языком. Если это не так, пожалуйста, объясните почему в своем ответе.
- Это код гольф, поэтому самый короткий ответ в байтах выигрывает!
code-golf
math
integer-partitions
Arnauld
источник
источник
Ответы:
Желе ,
2931 байтМонадическая ссылка, которая возвращает результат (N = 65536 занимает менее двух секунд).
Попробуйте онлайн!
Как?
Подробное объяснение алгоритма можно найти в фантастическом посте Мартина Эндера .
Полнофункциональная 29-байтовая реализация, которую я создал по описанному алгоритму, занимает 4 минуты 30 для N = 65536 на моем ноутбуке, поэтому я полагаю, что это не считается.
Использование цикла while для каждого шага 3 и его повторное использование в качестве шага 1 равно длине, с которой я могу справиться при инициализации списка, поскольку проверка на шаге 3 не означает создание списка до тех пор, пока ничего не останется, а затем поиск первого индекса значения:
источник
Mathematica, 79 байтов
объяснение
Я не мог потрудиться реализовать алгоритм в задаче, поэтому я хотел найти кратчайший путь к решению. Хотя я нашел один, к сожалению, он не побеждает ответ Mathematica, который реализует алгоритм. Тем не менее, я уверен, что это еще не оптимально, и могут быть другие языки, которые могут извлечь выгоду из этого подхода или некоторых идей, полученных в процессе.
Поэтому я утверждаю, что последовательность, которую мы должны вычислить:
f (n) = 2 * ( A212652 (n) - A002024 (n)) + 1 + A023532 (n-1)
В качестве альтернативы, это f (n) = 1, если n - треугольное число и f (n) = 2 * ( A212652 (n) - A002024 (n) + 1) в противном случае.
В первом выражении A023532 просто кодирует эти два разных случая. Две другие последовательности (плюс 1) представляют собой разницу между наибольшим целым числом k в самом длинном разложении n на последовательные целые числа (k-i + 1) + (k-i + 2) + ... + k = n и наибольшее целое число j, так что 1 + 2 + ... + j <n .
В нескольких простых словах, вот как мы находим ответ на нетреугольные числа: первое, найти наибольший треугольное число T J , который меньше , чем п . Тогда j - предпоследнее целое число, которое добавляется на шаге 1 (потому что после добавления j + 1 мы превысим n ). Затем разложите n на как можно больше (или как можно меньше) последовательных целых чисел и назовите максимум среди этих чисел k . Результат просто 2 * (кДж) . Интуитивно понятная причина этого заключается в том, что максимум разложения растет на 1 через каждый шаг, и мы останавливаемся, когда достигаемк .
Нам нужно показать четыре вещи, чтобы доказать, что это работает:
Мы уже показали, почему (1) верно. Далее мы докажем, что мы не можем закончить на шаге вставки, кроме начального (что не происходит для нетреугольных чисел).
Предположим, что мы закончили на шаге вставки, достигнув n после добавления значения p к сумме. Это означает, что перед этим шагом вставки значение было np ( или меньше, если мы добавили несколько значений одновременно). Но этому этапу вставки предшествовал этап удаления (поскольку мы не могли нажать n на шаге 1). Последнее значение q, которое мы удалили на этом шаге удаления, было обязательно меньше p из-за того, как работает алгоритм. Но это означает, что до того, как мы удалили q, у нас было n-p + q ( или меньше ), которое меньше n, Но это противоречие, потому что нам пришлось бы прекратить удалять целые числа, когда мы нажимаем n-p + q вместо удаления другого q . Это доказывает пункт (2) выше. Итак, теперь мы знаем, что мы всегда заканчиваем этапом удаления и что поэтому все нетреугольные числа имеют четные выходные данные.
Далее мы докажем (3), что каждый шаг вставки может вставить только одно значение. Это по существу следствие (2). Мы показали, что после добавления одного значения мы не можем точно указать n , а поскольку при проверке использовалось неравенство, мы также не можем оказаться ниже n (поскольку тогда n-p + q все равно будет меньше n, и мы не должны были удалять что много ценностей в первую очередь). Поэтому всякий раз, когда мы добавляем одно значение, мы гарантированно превышаем n, потому что мы опустились ниже n , удалив меньшее значение. Следовательно, мы знаем, что верхний конец суммы увеличивается на 1 при каждом следующем шаге. Мы знаем начальное значение этого верхнего конца (это наименьшее м такое, чтоT m > n ). Теперь нам просто нужно выяснить этот верхний предел, как только мы достигнем окончательной суммы. Тогда количество шагов просто вдвое больше разницы (плюс 1).
Для этого мы докажем (4), что окончательная сумма - это всегда разложение n на максимально возможное число целых чисел или разложение, где максимум в этом разложении минимален (т. Е. Это самое раннее возможное разложение). Мы снова сделаем это из-за противоречия (формулировка в этой части может быть немного более строгой, но я уже потратил слишком много времени на это ...).
Скажем, самое раннее / самое длинное из возможных разложений n - это некоторое a + (a + 1) + ... (b-1) + b , a ≤ b , и скажите, что алгоритм его пропускает. Это означает, что в момент добавления b a больше не должен быть частью суммы. Если бы a было частью суммы s , то у нас было бы n ≤ s в этот момент. Таким образом, либо сумма содержит только значения от a до b , что равно n, и мы останавливаемся (следовательно, мы не пропустили эту декомпозицию), либо в сумме есть хотя бы одно значение меньше, чем a, в этом случае выигрыш n <sи это значение будет удалено, пока мы не достигнем точной суммы (опять же, декомпозиция не была пропущена). Таким образом, мы должны избавиться от а, прежде чем добавить б . Но это означает, что нам придется столкнуться с ситуацией, в которой а является наименьшим компонентом суммы, а наибольшая - еще не б . Однако в этот момент мы не можем удалить a , потому что сумма явно меньше n (так как b отсутствует), поэтому мы должны сначала добавить значения, пока мы не добавим b и точно не достигнем n . Это доказывает (4).
Итак, взяв эти вещи вместе: мы знаем, что первая пара шагов дает нам максимальное значение A002024 (n) . Мы знаем, что максимальное значение окончательного разложения составляет A212652 (n) . И мы знаем, что этот максимум увеличивается один раз за каждую пару шагов. Следовательно, окончательное выражение равно 2 * ( A212652 (n) - A002024 (n) + 1) . Эта формула почти работает для треугольных чисел, за исключением того, что для них нам нужен только 1 шаг вместо 2, поэтому мы корректируем результат с помощью индикаторной функции треугольных чисел (или ее обратной, в зависимости от того, что удобнее).
Наконец, что касается реализации. Для первой последовательности я использую формулу MIN (нечетное d | n; n / d + (d-1) / 2) из OEIS. Получается сэкономить несколько байтов, если мы возьмем в это выражение множитель 2, чтобы получить MIN (нечетное d | n; 2n / d + d-1) , потому что это -1 затем отменяется с +1 в моей первой версии из f (n), который непосредственно кодирует два случая для треугольных и нетреугольных чисел. В коде это:
Для последней последовательности (
1, 2, 2, 3, 3, 3, ...
) мы можем использовать простую замкнутую форму:И, наконец, обратная индикаторная функция треугольных чисел равна 0, если 8n + 1 - идеальный квадрат. Это может быть выражено в Mathematica как
Есть много способов выразить эти последние две последовательности и сместить некоторое постоянное смещение между ними, поэтому я уверен, что это еще не оптимальная реализация, но я надеюсь, что это может дать другим отправную точку для изучения новых подходов в свои собственные языки.
Поскольку я пошел на все эти проблемы, вот график последовательности до n = 1000 (я мог бы также вычислить 100k за пару секунд, но на самом деле он не показывает никаких дополнительных сведений):
Может быть интересно взглянуть на вариации этих очень прямых линий, но я оставлю это кому-то еще ...
источник
Mathematica, 72 байта
Чистая функция, принимающая целочисленный аргумент.
Как это работает
For
Цикл.Инициализация; установите
l
(нижний),u
(верхний),c
(счетчик) иk
(сумма) в 0.Состояние; повторить пока
k
не равно входу.Приращение; увеличить счетчик
c
.тело
Если вход больше чем
k
:В то время как входное значение больше
k
, увеличениеu
и увеличениеk
наu
.Если ввод не больше чем
k
:В то время как ввод меньше
k
, уменьшитеk
наl
и увеличьтеl
.Вернитесь
c
после цикла.источник
For[,...]
бьетWhile[...]
.Python 2 , 104 байта
Попробуйте онлайн!
Чередует добавление терминов в конец списка и удаление терминов в начале.
источник
Haskell ,
70 63 6864 байтаРЕДАКТИРОВАТЬ:
a
. Исправлены ошибки в объяснении.a
иb
линейно, чтобы получить условия в формуле суммирования для отмены.1#1
является анонимной функцией, принимающей и возвращающей целое числоИспользовать как
(1#1) 100
.Попробуйте онлайн!
Как это работает
(a#b)n
представляет текущий шаг расчета.a, b
числа в1, 3, 5, ..
, в то время какn
может быть положительным или отрицательным в зависимости от шага.[(a+1)/2,(a+3)/2..(b-1)/2]
и номер цели-n
.[(b+1)/2,(b+3)/2..(a-1)/2]
и номер целиn
.a, b
списками и для того, чтобы можно было суммировать с коротким выражениемs=a*a-b*b
.s= -8*sum[(a+1)/2..(b-1)/2]
.s=8*sum[(b+1)/2..(a-1)/2]
.s>8*n
, тоb
увеличивается на 2 перед повторением.s<8*n
, то рекурсия изменяет шаг, меняя местамиa
иb
, и отрицаяn
, и к результату добавляется 1.s==8*n
, то ни одно из двух представлений списка не дает никаких элементов, поэтому сумма равна0
.(1#1) n
представляет собой фиктивную "стадию 2" перед запуском, которая немедленно изменяется на шаг 1, из которого строится список[1..0]=[]
.источник
PHP> = 7,0, 74 байта
использовать оператор космического корабля
Попробуйте онлайн!
расширенный
источник
$argn
?-R
гораздо меньшеargv
илиargi
. Я знал об argc и argv конечно. Очень интересно, спасибо.C
9491 байтПопробуйте онлайн
источник
return
), но для тех, которые работают, не стесняйтесь включать их в свой ответ.JavaScript (ES6), 82 байта
Тестовый фрагмент
Показать фрагмент кода
источник
постоянный ток , 61 байт
Попробуйте онлайн!
объяснение
Основной рекурсивный макрос:
Этот макрос:
S
точное текущее число. Выходит, если это так.S+N
(чрезмерное приближение) илиS-N
(недостаточное приближение), выбор чередуется между итерациями.Когда он завершается, след, оставленный в стеке, сообщает основной программе, сколько итераций потребовалось.
источник
Python 3,
150138 байтChangelog:
источник
else
это необходимо? Я считаю, чтоelse
каждый раз запускается, потому что цикл всегда завершается нормально (безbreak
), и, кажется, без него работает нормально .A=l.append
часть и использоватьl+=[x]
вместо этого.Пакет, 126 байт
Пояснение:
l
ноль, если шаг 2 никогда не выполнялся. Это позволяетn
отслеживать количество итераций шага 3. Поскольку алгоритм никогда не останавливается на шаге 3, следовательно, он должен выполнить шаг 1 один раз и шаг 2n+1
раза для общего количестваn+n+2
шагов. Однако если параметр является треугольным числом, то шаг 2 никогда не выполняется, поэтому нам нужно вычесть один шаг.источник
Python 2,
8681 байтПопробуйте онлайн!
Вычисляет 65536 тестов в
0.183s
TIO.Эта рекурсивная версия на 84 байта не может вычислить все значения до 65536:
Попробуйте онлайн!
источник
Mathematica, 92 байта
Чистая функция, принимающая целочисленный аргумент и возвращающая целое число.
Переменные
a
иb
обозначают (постоянно изменяющиеся) начальные и конечные числа в рассматриваемой сумме, в то время какq
обозначают промежуточную сумму (чисел отa+1
доb
);t
отслеживает все найденные значенияq
. После инициализации этих переменныхFor
цикл продолжает выполнятьсяIf[q<#,q+=++b,q-=++a]
, который либо добавляет новое число в конец, либо вычитает число спереди, как предписывает спецификация, до тех пор, пока не будетq
равен входному значению.Теперь нам просто нужно извлечь количество шагов из
t
спискаq
значений, встречающихся на этом пути. Например, когда на входе11
,For
цикл выходит сt
выравниванием{0,1,3,6,10,15,14,12,9,15,11}
. Лучший способ, который я нашел для вычисления количества шагов из этого, состоит в том, чтобы посчитать, сколько раз различия переходят от повышения к понижению; это то, чтоLength@Split@Sign@Differences@t
делает команда verbose , но я подозреваю, что это можно улучшить.источник
C (tcc), 71 байт (61 + 10)
Аргументы командной строки (включая пробел):
Источник:
Как это работает:
c
считает количество шаговm
иM
хранить минимум и максимум диапазона,s
суммы. Изначально все они равны нулю.Постоянно
c
увеличивается иs
сравнивается сn
. Пока они неравны:Если
c
нечетно, то до тех пор, покаs<n
, добавьте целое число в конец диапазона: увеличитьM
на единицу иs
наM
.Если
c
четное, то, покаs>n
, удалите целое число из начала диапазона: уменьшитеs
наm
и увеличьтеm
на единицу.Когда цикл завершается,
c
он был увеличен слишком много раз. Уменьшение его приводит к правильному результату, и он рассчитывается в правильном регистре, чтобы действовать как возвращаемое значение.Забавно случается, что используются те же самые имена переменных, что и в ответе Халеда . Они не копируются.
источник
Perl 6 , 114 байт
(вдохновленный более ранней реализацией Haskell )
Попробуйте.
Он работает со входом 65536 менее чем за 45 секунд на моем компьютере, но я не смог заставить его работать менее чем за 60 секунд с TIO.run.
У меня есть Rakudo v2017.04 +, где он имеет v2017.01 .
Rakudo / NQP / MoarVM получает оптимизации почти ежедневно, поэтому в промежутке между ними может быть любое их количество, необходимое для своевременного выполнения.
расширенный
Обратите внимание, что в Rakudo есть оптимизация для
Range.sum
того, чтобы ему не приходилось перебирать все значения.источник