Генерация минимальной последовательности остатка

21

Каждое число может быть представлено с помощью бесконечно длинной последовательности остатков. Например, если мы берем число 7 и выполняем 7mod2, то 7mod3, тогда 7mod4и так далее, мы получаем 1,1,3,2,1,0,7,7,7,7,.....

Однако нам нужна кратчайшая возможная подпоследовательность, которая еще может быть использована, чтобы отличить ее от всех младших чисел. Повторное использование 7 [1,1,3]является самой короткой подпоследовательностью, потому что все предыдущие подпоследовательности не начинаются с [1,1,3]:

0: 0,0,0,0...
1: 1,1,1,1...
2: 0,2,2,2...
3: 1,0,3,3...
4: 0,1,0,4...
5: 1,2,1,0...
6: 0,0,2,1...

Обратите внимание, что представление 7 [1,1] не работает, потому что его также можно использовать для представления 1. Однако вы должны вывести [1]с вводом 1.

Ввод, вывод

Ваш ввод является неотрицательным целым числом. Вы должны вывести последовательность или список последовательности минимальной длины остатков, как определено выше.

Тестовые случаи:

0: 0
1: 1
2: 0,2
3: 1,0
4: 0,1
5: 1,2
6: 0,0,2
7: 1,1,3
8: 0,2,0
9: 1,0,1
10: 0,1,2
11: 1,2,3
12: 0,0,0,2
30: 0,0,2,0
42: 0,0,2,2
59: 1,2,3,4
60: 0,0,0,0,0,4
257: 1,2,1,2,5,5
566: 0,2,2,1,2,6,6
1000: 0,1,0,0,4,6,0,1
9998: 0,2,2,3,2,2,6,8,8,10
9999: 1,0,3,4,3,3,7,0,9,0

Вот первые 10000 последовательностей , если вам интересно (номера строк выключены на 1).

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

Натан Меррилл
источник
Связанные
Питер Тейлор
@nimi мы говорили об этом в чате, и я решил, что последовательности должны быть длиной не менее 1 элемента.
Натан Меррилл
1
Я немного удивлен, что ты не ограничил это первыми остатками.
Нил
Это нормально, если вывод возвращается в списке?
Р. Кап
@neil, я тоже это учел, но поскольку остатки отличаются от составных чисел, я проголосовал за то, чтобы они сохранились
Натан Меррилл,

Ответы:

5

Mathematica, 60 53 байта

#~Mod~FirstCase[2~Range~#&/@Range[#+2],x_/;LCM@@x>#]&

Несколько быстро (он обрабатывает 10000 за ~ 0,1 секунды, но, скорее всего, не хватит памяти для 100000).

Код выдает ошибку, но вычисляет результат правильно.

объяснение

Ранее в чате мы обнаружили, что требуемые делители всегда можно определить как самый короткий список, {1, 2, ..., n}чье наименьшее общее кратное превышает входное. Короткий аргумент, объясняющий, почему это так: если LCM меньше входного значения, то вычитание LCM из входного значения оставило бы все делители без изменений, поэтому представление не является уникальным. Однако для всех входов, меньших, чем LCM, остатки будут уникальными, в противном случае разница между двумя числами с одинаковыми остатками будет меньше кратных всех делителей.

Что касается кода ... как обычно, порядок чтения Mathematica в гольфе немного смешной.

Range[#+2]

Это дает нам список [1, 2, 3, ..., n+2]для ввода n. +2Является обеспечение того , что правильно работает 0и 1.

2~Range~#&/@...

Карта 2~Range~#(синтаксический сахар для Range[2,#]) над этим списком, поэтому мы получаем

{{}, {2}, {2,3}, ..., {2,3,...,n+2}}

Это списки кандидатов-делителей (конечно, в общем, это гораздо больше, чем нам нужно). Теперь мы находим первый из них, чей LCM превышает вход с:

FirstCase[...,x_/;LCM@@x>#]

Больше синтаксиса: x_это шаблон, который соответствует любому из списков и вызывает его x. /;Присоединяет условие для этой модели. Это условие, LCM@@x>#где @@ применяется функция к списку, т.е. LCM@@{1,2,3}означает LCM[1,2,3].

Наконец , мы просто получить все остатки, используя тот факт , что Modесть Listable, то есть он автоматически сопоставляет по списку , если один из аргументов является список (или если оба они представляют собой списки той же длины):

#~Mod~...
Мартин Эндер
источник
5

Желе , 14 байт

‘Ræl\>iṠ2»2r⁸%

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

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

‘Ræl\>iṠ2»2r⁸%  Main link. Argument: n

‘               Increment; yield n+1.
 R              Range; yield [1, ..., n+1].
  æl\           Cumulatively reduce by LCM.
                This yields [LCM(1), ..., LCM(1, ..., n+1)].
     >          Compare all LCMs with n.
      iṠ        Find the first index of sign(n).
                This yields the first m such that LCM(2, ..., m) > n if n > 0, and
                0 if n == 0.
        2»      Take the maximum of the previous result and 2, mapping 0 to 2.
          2r    Yield the range from 2 up to and including the maximum.
            ⁸%  Compute n modulo each integer in that range.
Деннис
источник
5

MATL , 24 байта

Спасибо @nimi за указание на ошибку в предыдущей версии этого ответа (теперь исправлено)

Q:qtQ!\t0Z)tb=YpsSP2):Q)

Это исчерпывает память в онлайн-компиляторе для двух крупнейших тестовых случаев (но работает на компьютере с 4 ГБ ОЗУ).

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

объяснение

Это применяет определение в прямой форме. Для ввода nон вычисляет двумерный массив, содержащий mod(p,q)с pот 0до nи qот 1до n+1. Каждый pявляется столбцом, и каждый qявляется строкой. Например, при вводе n=7этот массив

0 0 0 0 0 0 0 0
0 1 0 1 0 1 0 1
0 1 2 0 1 2 0 1
0 1 2 3 0 1 2 3
0 1 2 3 4 0 1 2
0 1 2 3 4 5 0 1
0 1 2 3 4 5 6 0
0 1 2 3 4 5 6 7

Теперь последний столбец, который содержит остатки n, поэлементно сравнивается с каждым столбцом этого массива. Это дает

1 1 1 1 1 1 1 1
0 1 0 1 0 1 0 1
0 1 0 0 1 0 0 1
0 0 0 1 0 0 0 1
0 0 1 0 0 0 0 1
0 1 0 0 0 0 0 1
1 0 0 0 0 0 0 1
0 0 0 0 0 0 0 1

где 1указывает на равенство. Последний столбец, очевидно, равен самому себе и поэтому содержит все единицы. Нам нужно найти столбец , который имеет наибольшее количество первоначальных , кроме последнего столбца, и принять к сведению , что число первичных единиц, m. (В данном случае это второй столбец, который содержит m=3исходные). Для этого мы рассчитываем совокупное произведение каждого столбца:

1 1 1 1 1 1 1 1
0 1 0 1 0 1 0 1
0 1 0 0 0 0 0 1
0 0 0 0 0 0 0 1
0 0 0 0 0 0 0 1
0 0 0 0 0 0 0 1
0 0 0 0 0 0 0 1
0 0 0 0 0 0 0 1

тогда сумма каждого столбца

1 3 1 2 1 2 1 8

а затем сортировать не все и принять второе значение, которое есть 3. Это желаемое m, которое указывает, сколько остатков нам нужно выбрать.

Q:q    % take input n implicitly. Generare row array [0 1 ... n]
tQ!    % duplicate. Transform into column array [1; 2; ...; n-1]
\      % modulo, element-wise with broadcast. Gives the 2D array
t0Z)   % duplicate. Take last column
tb     % duplicate, bubble up
=      % test for equality, element-wise with broadcast
Yp     % cumumative product of each column
s      % sum of each column. This gives the number of initial coincidences
SP2)   % sort in decreasing order and take second value: m
:Q     % generate range [2 3 ... m+1]
)      % apply as index into array of remainders of n. Implicitly display
Луис Мендо
источник
4

Желе , 13 11 байт

r¬µ%€R‘$ḟ/Ṫ

Это не выиграет очки скорости брауни ... Попробуйте онлайн! или проверьте меньшие контрольные примеры .

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

r¬µ%€R‘$ḟ/Ṫ  Main link. Argument: n

r¬           Range from n to (not n).
             This yields [n, ..., 0] if n > 0 and [0, 1] otherwise.

  µ          Begin a new, monadic chain. Argument: A (range)

       $     Combine the previous two links into a monadic chain:
     R         Range; turn each k in A into [1, ..., k] or [] if k == 0.
      ‘        Increment to map k to [2, ..., k+1].
   %€        Take each k in A modulo all the integers in the 2D list to the right.
        ḟ/   Reduce by filter-not; sequentially remove all remainder sequences of
             n-1, ..., (not n) from the remainder sequences of n.
          Ṫ  Tail; take the last remainder sequence.
             This gives the shortest sequence for descending A and the longest one
             (i.e., [0]) for ascending A.
Деннис
источник
Почему вы включили два ответа ???
Эрик Outgolfer
Потому что это два совершенно разных подхода. Хотя этот параметр на 3 байта короче, другой на самом деле достаточно быстр для вычисления всех тестовых случаев.
Деннис
Если бы я был тобой, я бы этого не сделал ... если бы это не было голосование "за" и "против".
Эрик Outgolfer
Разные языки / подходы идут в разных ответах. Это был мой самый первый мета вопрос.
Деннис
3

Python 3.5, 117 95 78 байт

import sympy
r=lambda n,m=2,M=1,*l:M>n and l or r(n,m+1,sympy.lcm(m,M),*l,n%m)

Требуется Python 3.5 и sympy ( python3 -m pip install --user sympy). Благодарим @Dennis, чтобы уведомить меня, что Python 3.5 допускает *lхитрость с аргументами по умолчанию.

orlp
источник
С SymPy 0.7.5 вы можете сократить M>n and lдо l*(M>n).
Деннис
3

Python 2, 73 70 69 65 байт

i=l=1
n=input()
while l<=n|1:
 i+=1;a=l;print n%i
 while l%i:l+=a

Полная программа. @Dennis сэкономил 4 байта, улучшив способ обработки нуля.

xsot
источник
3

Haskell, 66 60 51 50 байт

f i=mod i<$>[2..2+sum[1|l<-scanl1 lcm[2..i],l<=i]]

Пример использования: f 42-> [0,0,2,2]. Это алгоритм, описанный в ответе @Martin Büttner .

Я сохраню предыдущую версию для справки, потому что она довольно быстрая:

Haskell, 51 байт

f i=mod i<$>[[2..x]|x<-[2..],foldl1 lcm[2..x]>i]!!0

На f (10^100)моем пятилетнем ноутбуке это занимает 0,03 секунды .

Изменить: @xnor нашел байт для сохранения. Благодарность!

Ними
источник
Сохранение байта путем подсчета индексов, пока lcm не станет слишком высоким:h i=mod i<$>[2..2+sum[1|l<-scanl1 lcm[2..i],l<=i]]
xnor
2

Pyth, 51 Bytes 66 Bytes

IqQZ[Z).q)IqQ1[1))IqQ2,0 1))FdhhQJu/*GHiGHtUd1I>JQVQ aY%QhN)<tYd.q

Попробуйте!

Гораздо более высокая скорость 39-байтовой версии (не работает для 0-2):

FdhhQJu/*GHiGHtUd1I>JQVtd aY%QhN)<tYd.q

Кажется, работает для абсурдно больших чисел, таких как 10 10 3

Примечание: этот ответ не работает для 0, 1 и 2. Исправлено!

poi830
источник
2

JavaScript (ES6), 81 77 байт

f=(n,r=[n%2],l=i=2,g=(j,k)=>j?g(k%j,j):k)=>l>n?r:f(n,[...r,n%++i],i/g(i,l)*l)

Это рекурсивно создает ответ до тех пор, пока LCM не превысит исходное число. Конечно, GCD рассчитывается рекурсивно.

Редактировать: 4 байта сохранены благодаря @ user81655.

Нил
источник
@ user81655 Это просто закулисно ...
Нейл
2

Рубин, 52 байта

->n{m=t=1;a=[];(a<<n%m)until n<t=t.lcm(m+=1);a<<n%m}

Это решение проверяет все возможные, mначиная с 2, которые являются остатком, который делает последовательность уникальной. То, что делает последний mуникальный, не сам остаток, а то, что он mявляется последним членом наименьшего диапазона, (2..m)где наименьшее общее кратное (LCM) этого диапазона больше, чем n. Это связано с китайской теоремой об остатках, где для однозначного определения числа nс числом остатков значение LCM этих остатков должно быть больше, чем n(при выборе nиз (1..n); при выборе nиз a..b, значение LCM должно быть только больше, чем b-a)

Примечание: я поставил a<<n%mв конце кода, потому что until n<t=t.lcm(m+=1)короткое замыкание перед aполучило последний элемент, чтобы сделать его уникальным.

Если у кого-то есть предложения по игре в гольф, пожалуйста, сообщите мне об этом в комментариях или в чате PPCG .

Ungolfing:

def remainder_sequence(num)
  # starting with 1, as the statements in the until loop immediately increments divisor
  divisor = 1
  # starts with 1 instead of 2, as the statements in the until loop
  # immediately change product to a new lcm
  product = 1
  remainders = []

  # this increments divisor first then checks the lcm of product and divisor
  # before checking if num is less than this lcm
  until num < (product = product.lcm(divisor = divisor + 1))
    remainders << num % divisor
  end

  # until always short circuits before the last element is entered
  # so this enters the last element and returns
  return remainders << num % divisor
end
Sherlock9
источник
1

Python 3.5, 194 181 169 152 149 146 байт:

( Спасибо @ Sherlock9 за 2 байта! )

def r(o,c=0):
 y=[[j%i for i in range(2,100)]for j in range(o+1)]
 while 1:
  c+=1;z=y[-1][:c]
  if z not in[f[:c]for f in y[:-1]]:break
 print(z)

Работает отлично, а также довольно быстро. Расчет на минимальную оставшуюся последовательность 100000выходов [0, 1, 0, 0, 4, 5, 0, 1, 0, 10, 4, 4]занял всего около 3 секунд. Он даже смог рассчитать последовательность для ввода 1000000(1 миллион), вывода [0, 1, 0, 0, 4, 1, 0, 1, 0, 1, 4, 1, 8, 10, 0, 9]и занял около 60 секунд.

объяснение

По сути, эта функция в первую очередь создает список yсо всеми, j mod iгде jкаждое целое число в диапазоне 0=>7(включая 7) и iкаждое целое число в диапазоне 0=>100. Затем программа переходит в бесконечный whileцикл и сравнивает одинаковое количество содержимого каждого подсписка в первом-втором-последнем подсписках y( y[:-1:]) с тем же количеством элементов в последнем подсписке ( y[-1]) списка y. Когда подсписок y[-1]это отличается , чем любой другой подсписка, цикл нарушается из - за, и правильная минимальная последовательность остаток возвращается.

Например, если ввод 3, yбудет:

[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], [1, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3]]

Затем, когда он входит в цикл while, он сравнивает каждый подсписок в списке y[:-1:]с таким же количеством элементов в подсписке y[-1]. Например, сначала нужно сравнить [[0],[1],[0]]и [1]. Поскольку последний подсписок находится в остальной части y, он будет продолжаться, а затем будет сравниваться [[0,0],[0,1],[0,2]]и [1,0]. Так [1,0]как теперь НЕ в остальном y в этом конкретном порядке , это минимальная последовательность напоминаний, и, следовательно, [1,0]будет возвращено правильно.

Р. Кап
источник
Чтобы сохранить байты, так y[:c:]же, какy[:c]
Sherlock9
0

C89, 105 байт

g(a,b){return b?g(b,a%b):a;}main(n,m,M){scanf("%d",&n);for(m=M=1;(M=++m*M/g(m,M))<=n;)printf("%d ",n%m);}

Компилирует (с предупреждениями) используя gcc -std=c89. Принимает одно число в stdin и выводит последовательность остатков, разделенных пробелами в stdout.

orlp
источник
1
Это ничего не печатает, когда n = 0
xsot
0

C 89 байтов

a,i=2;main(l,n){for(n=atoi(gets(n))?:!puts(n);n/l;printf("%d ",n%i++))for(a=l;l%i;l+=a);}

Компилировать с GCC. Попробуйте онлайн: n = 59 , n = 0 .

xsot
источник