Найти массив, который соответствует набору сумм

17

Рассмотрим массив Aдлины n. Массив содержит только натуральные числа. Например A = (1,1,2,2). Определим f(A)как множество сумм всех непустых непрерывных подмассивовA . В этом случае f(A) = {1,2,3,4,5,6}. Шаги для производства f(A) следующие:

Подмассивы Aесть (1), (1), (2), (2), (1,1), (1,2), (2,2), (1,1,2), (1,2,2), (1,1,2,2). Их соответствующие суммы 1,1,2,2,2,3,4,4,5,6. Поэтому набор, который вы получаете из этого списка{1,2,3,4,5,6} .

задача

Учитывая набор сумм, Sприведенных в отсортированном порядке, содержащих только натуральные числа и длину массива n, ваша задача - вывести хотя бы один массив, Xтакой что f(X) = S.

Например, если S = {1,2,3,5,6}и n = 3тогда действительный вывод X = (1,2,3).

Если такого массива нет, Xваш код должен вывести любое постоянное значение.

Примеры

Вход: n=4, S = (1, 3, 4, 5, 6, 8, 9, 10, 13)возможный выход:X = (3, 5, 1, 4)

Вход: n=6, S = (2, 3, 4, 5, 7, 8, 9, 10, 12, 14, 17, 22)возможный выход:X = (5, 3, 2, 2, 5, 5)

Вход: n=6, S = (2, 4, 6, 8, 10, 12, 16)возможный выход:X = (4, 2, 2, 2, 2, 4)

Вход: n=6, S = (1, 2, 3, 4, 6, 7, 8, 10, 14)возможный выход:X = (4, 2, 1, 1, 2, 4)

Вход: n=10, S = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 15, 16, 17, 18, 19, 20, 23, 24, 25)возможный выход:X = (1, 1, 3, 1, 2, 1, 2, 5, 4, 5).

Входной сигнал: n=15, S = (1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 20, 21, 22, 23, 24, 25, 26, 27, 28, 30, 31), возможно выход: X = (1, 2, 1, 3, 3, 1, 3, 3, 1, 3, 3, 1, 2, 1, 3).

Формат ввода и вывода

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

Продолжительность

Вы должны быть в состоянии выполнить код до завершения для всех примеров в вопросе. В принципе он должен быть верным n, 15но вам не нужно доказывать, что он будет достаточно быстрым для всех входных данных.

Ануш
источник
Комментарии не для расширенного обсуждения; этот разговор был перенесен в чат .
Деннис
Вероятно, должен иметь тестовый набор с 2-значным числом.
Волшебная Осьминога Урна

Ответы:

6

Шелуха , 20 байт

ḟȯ⁰¦ṁ∫ṫ!¡Sof~Λ€∫×:¹g

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

Возвращает одно решение или пустой список, если его не существует. Последний контрольный пример (n=15 ) заканчивается за 3,8 секунды на TIO.

объяснение

Программа состоит из двух частей. В первой части ( ¡и справа от нее) мы создаем бесконечный список, чей kэлемент th является списком, содержащим все kсписки длин, в которых находятся суммы срезов S. Мы делаем это индуктивно, начиная с 1-элементных фрагментов Sи на каждом шаге добавляя каждый элемент Sк каждому списку и сохраняя те, чьи суммы префиксов находятся в S. Во второй части ( !и слева от нее) мы берем nth-ый элемент списка, который содержит length- nсписки. Из них мы выбираем первый, чьи суммы срезов фактически содержат каждый элементS .

В коде давайте сначала заменим oи ȯ(которые объединяют две и три функции в одну) скобками для ясности.

¡S(f~Λ€∫)×:¹g  First part. Input is a list, say S=[1,2,3]
            g  Group equal adjacent elements: [[1],[2],[3]]
¡              Iterate function:
                Argument is a list of lists, say [[1,1],[1,2],[2,1]]
         ×      Mix (combine two lists in all possible ways)
          :     by prepending
           ¹    with the list S: [[1,1,1],[1,1,2],[2,1,1],[1,2,1],[2,1,2],[3,1,1],[2,2,1],[3,1,2],[3,2,1]]
   f            Filter by condition:
        ∫        Cumulative sums: [[1,2,3],[1,2,4],[2,3,4],[1,3,4],[2,3,5],[3,4,5],[2,4,5],[3,4,6],[3,5,6]]
     ~Λ          All of the numbers
 S     €         are elements of S: [[1,1,1]]
                 Only this list remains, since the other cumulative sums contain numbers not from S.
               Result of iteration: [[[1],[2],[3]],[[1,1],[1,2],[2,1]],[[1,1,1]],[],[],[]...

ḟ(⁰¦ṁ∫ṫ)!      Second part. Implicit input, say n=2.
        !      Take nth element of above list: [[1,1],[1,2],[2,1]]
ḟ              Find first element that satisfies this:
                Argument is a list, say [1,2]
      ṫ         Tails: [[1,2],[2]]
    ṁ           Map and concatenate
     ∫          cumulative sums: [1,3,2]
 ȯ ¦            Does it contain all elements of
  ⁰             S? Yes.
               Result is [1,2], print implicitly.

Есть некоторые части, которые требуют большего объяснения. В этой программе верхние индексы ⁰¹ссылаются на первый аргумент S. Однако, если αэто функция, то α¹означает «применить αк S», а ⁰αозначает «подключиться Sко второму аргументу α». Функция ¦проверяет, содержит ли первый аргумент все элементы второго (с учетом кратностей), поэтомуS должен быть и второй аргумент.

В первой части используемую функцию ¡можно интерпретировать как S(f~Λ€∫)(×:)¹. Комбинатор Sведет себя как Sαβγ -> (αγ)(βγ), что означает, что мы можем упростить его (f~Λ€∫¹)(×:¹). Вторая часть ×:¹- это «смешать с Sдобавлением», и ее результат передается первой части. Первая часть f~Λ€∫¹, работает так. Функция fфильтрует список по условию, которое в данном случае является ~Λ€∫¹. Он получает список списков L, поэтому мы имеем ~Λ€∫¹L. Комбинатор ~ведет себя так ~αβγδε -> α(βδ)(γε): первый аргумент передается β, второй - γи результаты объединяются с α. Это значит, что у нас есть Λ(€¹)(∫L). Последняя часть ∫L- это просто кумулятивные суммы L,€¹это функция, которая проверяет членство S,Λпринимает условие (здесь €¹) и список (здесь ∫L) и проверяет, что все элементы удовлетворяют ему. Проще говоря, мы отфильтровываем результаты микширования по тому, вошли ли их кумулятивные суммы S.

Zgarb
источник
Я с нетерпением жду объяснения!
Anush
1
@Anush Я добавил кодовую разбивку.
Згарб
Мне очень нравится это решение. Это вроде красиво.
Anush
6

Рубин , 135 байт

->a,n{r=w=1;r+=1until w=(s=a[0,r]).product(*[s]*~-n).find{|x|x.sum==a.max&&a==[]|(1..n).flat_map{|r|x.each_cons(r).map(&:sum)}.sort};w}

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

Используйте поиск в ширину. n = 10 работает на TIO, n = 15 занимает больше минуты, но работает на моей машине.

Рубин , 147 байт

->a,n{r=w=1;r+=1until w=([a[-1]-a[-2]]).product(*[s=a[0,r]]*~-n).find{|x|x.sum==a.max&&a==[]|(1..n).flat_map{|r|x.each_cons(r).map(&:sum)}.sort};w}

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

Оптимизированная версия, работает на TIO для n = 15 (~ 20 сек)

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

Первые идеи:

  • Сумма выходного массива является последним элементом (max) входного массива.
  • Сумма выходного массива за вычетом первого (или последнего) элемента является вторым последним элементом входного массива.
  • Если массив является решением, то обратный массив также является решением, поэтому мы можем предположить, что первый элемент - это разница между двумя последними элементами входного массива.
  • Вторым элементом может быть разница между вторым и третьим или вторым и четвертым последним элементом входного массива.

Что приводит нас к следующей оптимизации:

Рубин , 175 байт

->a,n{r=w=1;r+=1until w=([a[-1]-a[-2]]).product([a[-2]-a[-3],a[-2]-a[-4]],*[s=a[0,r]]*(n-2)).find{|x|x.sum==a.max&&a==[]|(1..n).flat_map{|r|x.each_cons(r).map(&:sum)}.sort};w}

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

~ 8,5 секунд на TIO. Неплохо...

... и так далее (будет реализовано)

гигабайт
источник
Это выглядит очень красиво!
Ануш
Я взволнован вашими новыми алгоритмами не грубой силы. Если вы хотите проверить больше примеров, я могу добавить их в новый раздел вопроса.
Anush
2
@Anush На самом деле это все еще грубая сила (экспоненциальное время), но с некоторой (полиномиальным фактором) оптимизацией.
user202729
Для меня вы забыли, что первый элемент (элемент поменьше) всегда находится в решении: поэтому у нас есть 1 и последний (сумма всех); и вы говорите второе последнее, но это для меня не ясно ... возможно, есть способ найти всех других таким образом ...
RosLuP
5

Haskell, 117 111 байтов

6 байтов сохранено благодаря @nimi!

f r i n s|n<1=[r|r==[]]|1<2=[y:z|y<-s,t<-[y:map(y+)i],all(`elem`s)t,z<-f[a|a<-r,all(a/=)t]t(n-1)s]
n&s=f s[]n s

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

frSins

Когда значение nравно нулю (поле для гольфа n<1), список должен быть готов, поэтому мы проверяем, все ли значения были просмотрены. Если нет, мы возвращаем пустой список, чтобы указать отсутствие решений, иначе мы возвращаем одноэлементный список, содержащий пустой список, к которому будут добавлены выбранные элементы. Этот случай также мог бы быть обработан с помощью дополнительных уравнений

f [] _ 0 _=[[]]
f _ _ 0 _=[]

Если nне ноль, мы возвращаем

[y:z|y<-s,t<-[y:map(y+)i],all(`elem`s)t,z<-f[a|a<-r,all(a/=)t]t(n-1)s]
 ^1^ ^2^^ ^......3......^ ^.....4.....^ ^.............5.............^

Это список (1) списков, откуда берется первый элемент (2), sа остальные (5) - из рекурсивного вызова при условии (4), в котором находятся все новые суммы s. Новые суммы вычисляются в (3) - заметка, tвзятая из одноэлементного списка, уродливая игра в гольф для того, что в идиоматическом Хаскеле было бы let t=y:map(y+)i. Рекурсивный вызов (5) получает в качестве нового rнабора старый набор без тех элементов, которые появляются среди новых сумм t.

Основная функция &вызывает вспомогательную функцию, говоря, что нам все еще нужно увидеть все значения ( r=s) и что суммы еще не определены ( i=[]).

Для еще семи байтов мы можем ограничить вычисление только первым результатом (если есть), который намного быстрее и обрабатывает все тестовые случаи менее чем за 2 секунды.

Попробуйте онлайн! (это только первый вариант вариации старой версии)

Кристиан Сиверс
источник
1
Это удивительно быстро. Если бы вы могли объяснить алгоритм, это было бы здорово.
Ануш
Я думаю о том, чтобы представить самую быструю версию кода этой проблемы. Как вы думаете, может быть решение по времени?
Ануш
@nimi Спасибо! Ах, старый добрый map, я только попробовал, <$>но для этого потребовались дополнительные парены ... @ Ануш Я понятия не имею, что такое решение за полиномиальное время
Кристиан Сиверс
3

Чисто , 177 байт

import StdEnv,Data.List
$s n=find(\a=sort(nub[sum t\\i<-inits a,t<-tails i|t>[]])==s)(?{#u\\u<-s|u<=(last s)-n}(last s)n)
?e s n|n>1=[[h:t]\\h<-:e|h<=s-n,t<- ?e(s-h)(n-1)]=[[s]]

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

Занимает около 40 секунд на моей машине для n=15тестового случая, но время ожидания на TIO.

Чисто , 297 байт

import StdEnv,Data.List
$s n=find(\a=sort(nub[sum t\\i<-inits a,t<-tails i|t>[]])==s)(~[u\\u<-s|u<=(last s)-n](last s)n(reverse s))
~e s n a|n>4=let u=a!!0-a!!1 in[[u,h:t]\\h<-[a!!1-a!!2,a!!1-a!!3],t<- ?e(s-u-h)(n-2)]= ?e s n
?e s n|n>1=[[h:t]\\h<-e,t<- ?(takeWhile((>=)(s-n-h))e)(s-h)(n-1)]=[[s]]

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

Этот включает в себя некоторые оптимизации, сделанные ГБ, а также некоторые из моих собственных. Я думаю, что некоторые из них можно сделать более общими, поэтому я добавлю объяснение, как только это будет сделано.

На моей машине это займет около 10 секунд, на TIO - 40 секунд.

Οurous
источник
Не могли бы вы изложить оптимизацию, которую вы использовали, пожалуйста? Я очень заинтересован.
Ануш
1
@ Ануш, я отредактирую ответ вместе с @mentionтобой завтра, когда они встанут, к сожалению, сегодня нет времени.
Οurous
3

Python 3 , 177 байт

from itertools import*
s,n=eval(input())
for[*t]in combinations(s[:-2],n-2):
  a=[*map(int.__sub__,t+s[-2:],[0,*t,s[-2]])];
  {sum(a[p//n:p%n+1])for p in range(n*n)}^{0,*s}or-print(a)

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

(некоторые новые строки / пробелы добавлены, чтобы читателям не приходилось прокручивать код)

Прямой порт моего ответа Jelly (с некоторыми изменениями, см. Раздел «примечание» ниже)

Результат локального прогона:

[user202729@archlinux golf]$ printf '%s' "from itertools import*
s,n=eval(input())
for[*t]in combinations(s[:-2],n-2):a=[*map(int.__sub__,t+s[-2:],[0,*t,s[-2]])];{sum(a[p//n:p%n+1])for p in range(n*n)}^{0,*s}or-print(a)" > a.py
[user202729@archlinux golf]$ wc -c a.py
177 a.py
[user202729@archlinux golf]$ time python a.py<<<'([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 15, 16, 17, 18, 19, 20, 23, 24, 25], 10)' 2>/dev/null
[1, 4, 1, 1, 1, 1, 1, 7, 7, 1]

real    0m3.125s
user    0m3.119s
sys     0m0.004s
[user202729@archlinux golf]$ time python a.py<<<'([1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 20, 21, 22, 23, 24, 25, 26, 27, 28, 30, 31], 15)' 2>/dev/null
[3, 1, 2, 1, 3, 3, 1, 3, 3, 1, 3, 3, 1, 2, 1]

real    11m36.093s
user    11m33.941s
sys     0m0.387s
[user202729@archlinux golf]$ 

Я слышал, что itertoolsэто многословно, но моя лучшая combinationsреализация еще более многословна:

c=lambda s,n,p:s and c(s[1:],n-1,p+s[:1])+c(s[1:],n,p)or[]if n else[p]

Примечание .

  • Используя деление / модуль по a[p//n:p%n+1] занимает в 2 раза больше времени, но экономит несколько байтов.
  • Это немного отличается от ответа Jelly - ответ Jelly повторяется в обратном направлении.
  • Благодаря combinationsвозвращению итератора это более удобно для памяти.
user202729
источник
2

Желе , 35 байт

Ẇ§QṢ⁼³
;³ṫ-¤0;I
ṖṖœcƓ_2¤¹Ṫ©ÇѬƲ¿ṛ®Ç

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

Запускать локально: (n = 15 занимает более 1 ГБ ОЗУ)

aaa@DESKTOP-F0NL48D MINGW64 ~/jellylanguage (master)
$ time python scripts/jelly fu z '[1,2,3,4,5,6,7,8,9,10,11,12,14,15,16,17,18,19,20,23,24,25]'<<<10
[8, 6, 2, 1, 1, 1, 1, 3, 1, 1]
real    0m1.177s
user    0m0.000s
sys     0m0.015s

aaa@DESKTOP-F0NL48D MINGW64 ~/jellylanguage (master)
$ time python scripts/jelly fu z '[1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 20, 21, 22, 23, 24, 25, 2
6, 27, 28, 30, 31]'<<<15
[3, 1, 2, 1, 3, 3, 1, 3, 3, 1, 3, 3, 1, 2, 1]
real    12m24.488s
user    0m0.000s
sys     0m0.015s

(на самом деле я запустил версию в кодировке UTF8, которая занимает более 35 байт, но в любом случае результат тот же)

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

Без короткого замыкания этот код занимает примерно (|S|-2N-2)×(N36+N2журналN2) операции, которая оценивает (26-215-2)×(1536+152журнал152)5,79×109Однако из-за неэффективности Python и Jelly, это займет вечность, чтобы завершить. Благодаря короткому замыканию, оно может закончиться намного раньше.

объяснение

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

Поэтому мы можем рассмотреть все способы выбора N-2 отдельные элементы от первого |S|-2 элементы (есть (|S|-2N-2)такие списки), вычислить последовательные различия, чтобы восстановить элементы; затем проверьте, действительно ли это наивно (получить всеN2подмассивы, подсчитать сумму, uniquify. Общая длина подмассивов составляет околоN36)


Не проверено (но должно иметь одинаковую производительность)

Желе , 32 байта

Ṫ©ÑẆ§QṢ⁻³
;³ṫ-¤ŻI
ṖṖœcƓ_2¤¹Ñ¿ṛ®Ç

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


Более неэффективная версия (без короткого замыкания):

Желе , 27 байт

Ẇ§QṢ⁼³
ṖṖœcƓ_2¤µ;³ṫ-¤0;I)ÑƇ

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

Для теста n = 15 он занимает до 2 ГБ ОЗУ и не завершается через ~ 37 минут.


примечание : Ẇ§может быть заменено на ÄÐƤẎ. Это может быть более эффективным.

user202729
источник
1

APL (NARS), символы 758, байты 1516

r←H w;i;k;a;m;j
   r←⊂,w⋄→0×⍳1≥k←↑⍴w⋄a←⍳k⋄j←i←1⋄r←⍬⋄→C
A: m←i⊃w⋄→B×⍳(i≠1)∧j=m⋄r←r,m,¨∇w[a∼i]⋄j←m
B: i+←1
C: →A×⍳i≤k

G←{H⍵[⍋⍵]}

r←a d w;i;j;k;b;c
   k←↑⍴w ⋄b←⍬⋄r←0 ⋄j←¯1
A: i←1⋄j+←1⋄→V×⍳(i+j)>k
B: →A×⍳(i+j)>k⋄c←+/w[i..(i+j)]⋄→0×⍳∼c∊a⋄→C×⍳c∊b⋄b←b,c
C: i+←1⋄→B
V: →0×⍳∼a⊆b
   r←1

r←a F w;k;j;b;m;i;q;x;y;c;ii;kk;v;l;l1;i1;v1
   w←w[⍋w]⋄r←a⍴w[1]⋄l←↑⍴w⋄k←w[l]⋄m←8⌊a-2⋄b←¯1+(11 1‼m)⋄j←2⋄i←1⋄x←↑⍴b⋄i1←0⋄v1←⍬
I: i1+←1⋄l1←w[l]-w[l-i1]⋄v1←v1,w[1+l-i1]-w[l-i1]⋄→I×⍳(l1=i1)∧l>i1⋄→B
E: r←,¯1⋄→0
F: i←1⋄q←((1+(a-2)-m)⍴0),(m⍴1),0⋄r+←q
A:   i+←1⋄j+←1⋄→E×⍳j>4000
B:   →F×⍳i>x⋄q←((1+(a-2)-m)⍴0),b[i;],0⋄q+←r⋄v←q[1..(a-1)]⋄→A×⍳0>k-y←+/v
   q[a]←k-y⋄→A×⍳l1<q[a]⋄→A×⍳∼q⊆w⋄→A×⍳∼l1∊q⋄→A×⍳∼v1⊆⍦q⋄c←G q∼⍦v1⋄ii←1⋄kk←↑⍴c⋄→D
C:   →Z×⍳w d v1,ii⊃c⋄ii+←1
D:   →C×⍳ii≤kk
   →A
Z: r←v1,ii⊃c

Функция G в G x (с помощью функции H) найдет все перестановки x. Функция d в xdy находит, генерирует ли массив y после массива упражнения x возвращающее логическое значение. Функция F в x F y возвращает массив r длины x, так что ydr имеет значение true (= 1) Немного дольше, чем реализация, но именно она вычисляет весь случай в тесте за меньшее время ... Последний случай для n = 15 он запускается только за 20 секунд ... Я должен сказать, что это не находит много решений, возвращает только одно решение (наконец-то это кажется) за меньшее время (не исследованный тест для разных входов ...) 16 + 39 42 + + 8 + 11 + 11 + 18 + 24 + 24 + 54 + 11 + 12 + 7 + 45 + 79 + 69 + 12 + 38 + 26 + 72 + 79 + 27 + 15 + 6 + 13 (758)

  6 F (2, 3, 4, 5, 7, 8, 9, 10, 12, 14, 17, 22)
5 3 2 2 5 5 
  6 F (2, 4, 6, 8, 10, 12, 16)
4 2 2 2 2 4 
  6 F (1, 2, 3, 4, 6, 7, 8, 10, 14)
4 2 1 1 2 4 
  10 F (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 15, 16, 17, 18, 19, 20, 23, 24, 25)
1 1 3 1 2 3 5 1 3 5 
  15 F (1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 20, 21, 22, 23, 24, 25, 26, 27, 28, 30, 31)
1 2 1 3 3 1 3 3 1 3 3 1 2 1 3 
  ww←(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 15, 16, 17, 18, 19, 20, 23, 24, 25)
  ww≡dx 1 1 3 1 2 3 5 1 3 5 
1
RosLuP
источник