Максимизировать разницу в квадрате


19

Рассмотрим перестановку целочисленных значений из 1в N. Например, этот пример для N = 4:

[1, 3, 4, 2]

Мы будем считать этот список циклическим, таким, что 1и 2рассматриваются как смежные. Одна величина, которую мы можем вычислить для такого списка - это общая квадратичная разница смежных значений:

(1-3)² + (3-4)² + (4-2)² + (2-1)² = 10

Ваша задача - найти перестановку, которая максимизирует эту величину, учитывая положительное целое число N. В случае N = 4приведенного выше примера не является оптимальным (на самом деле, это минимальный). Мы можем достичь общей разности квадратов 18при следующей перестановке (а также нескольких других):

[1, 4, 2, 3]

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

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

Вывод может быть в любом удобном, однозначном, плоском списке или строковом формате. Вы можете вернуть список со значениями от 0до , N-1а не 1к N.

Применяются стандартные правила .

Тестовые данные

Есть хорошее аналитическое решение этой проблемы. Например, все действительные решения для N = 10эквивалентны следующему списку (до циклических сдвигов и реверсирования):

[7, 5, 6, 4, 8, 2, 10, 1, 9, 3]

Я не хочу раскрывать слишком многое за этим (хотя, вероятно, этого достаточно, чтобы выяснить закономерность), поэтому вместо того, чтобы давать больше примеров, вы можете проверить, что ваши результаты имеют следующие общие квадратичные различия для данного N:

N    Total squared difference

1                         0
2                         2
3                         6
4                        18
5                        36
6                        66
7                       106
8                       162
9                       232
10                      322
33                    11936
100                  333202
333                12308236
1000              333332002

Это запись OEIS A064842 (которая также содержит ссылку на статью с решением этой проблемы, если вы застряли).

Ответы:


7

Желе, 24 21 15 14 10 9 байт

RUĖµ«/€ị"

Чтобы вычислить общую разницу в квадрате, добавьте µ_ṙ1$²Sкод. Попробуйте онлайн!

Фон

Один из способов создания перестановки с максимальным квадратом разности состоит в том, чтобы взять целые числа от 1 до n в порядке возрастания и поменять местами второе слева со вторым справа, четвертое слева с четвертым справа и т. Д. вперед, пока мы не встретимся в середине.

Например, для n = 8, 9 имеем

1 2 3 4 5 6 7 8        1 2 3 4 5 6 7 8 9
  ^   ^ ^   ^            ^   ^   ^   ^

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

1 7 3 5 4 6 2 8        1 8 3 6 5 4 7 2 9

после обмена.

Один из способов достижения этих перестановок, независимо от четности n , заключается в следующем.

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

1 2 3 4 5 6 7 8        1 2 3 4 5 6 7 8 9
8 7 6 5 4 3 2 1        9 8 7 6 5 4 3 2 1

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

1 2 3 4 5 6 7 8        1 2 3 4 5 6 7 8 9
8 7 6 5 4 3 2 1        9 8 7 6 5 4 3 2 1

1 2 3 4 4 3 2 1        1 2 3 4 5 4 3 2 1

Если минимум нечетный, целое число должно оставаться на своем месте, поэтому мы выбираем одно из первого ряда; если оно четное, целые числа должны поменяться местами, поэтому мы выбираем одно из второго ряда.

1   3     6   8        1   3   5   7   9
  7   5 4   2            8   6   4   2

Это желаемый результат.

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

RUĖµ«/€ị"    Main link. Input: n

R            Range. Yields [1, ..., n].
 U           Upend. Yields [n, ..., 1].
  Ė          Enumerate. Yields p := [[1, n], [2, n-1], ... [n-1, 2], [n, 1]].

   µ         Begin a new, monadic chain. Argument: p
     /       Reduce...
      €        each pair of p...
    «          by minimum.
        "    For each minimum and the corresponding pair of p:
       ị       Select the element at that index.
            Indices are modular and 1-based in Jelly, so this selects the first
            element if the minimum is odd, and the second one if it is even.

6

JavaScript (ES6), 52 байта

n=>[...Array(n)].map((_,i)=>(i<n/2|n%2)^i%2?i+1:n-i)

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

объяснение

Этот подход определяет число, которое должно быть в индексе iс длиной, nа не объединять результаты в массив. Это основано на следующем наблюдении ( n = 7на примере):

  • Начните с самого низкого числа слева и самого высокого справа: [ 1, 7 ]
  • Переключите порядок так, чтобы самый низкий был справа, а самый высокий был слева, увеличивал самый низкий, уменьшал самый высокий и помещал их в середину массива:[ 1, 6, 2, 7 ]
  • Повторяйте, пока самые высокие и самые низкие не сходятся: [ 1, 6, 3, 4, 5, 2, 7 ]

Более высокие и более низкие числа могут быть легко выражены как n-iи i+1соответственно.

var solution =

n=>
  [...Array(n)] // create an array of length n
  .map((_,i)=>  // set each value of the array at index i
    (i<n/2      // if we're on the left side,
    |n%2)       // or we're on the right and n is odd, even i => lower, odd i => higher
    ^i%2?       // else even i => higher, odd i => lower
    i+1:n-i
  )
N = <input type="number" id="input" oninput="result.textContent=solution(+this.value)" />
<pre id="result"></pre>


Хороший алгоритм; Я попытался и не смог придумать формулу для их генерации, поэтому мне пришлось использовать более уродливый метод толкания и отмены. Однако я могу, конечно, упростить вашу логику до (i<n/2||n%2)^i%2?i+1:n-i.
Нил

@ Нил Вау, я только что проснулся, решил поиграть в гольф, придумал вашу точную логику и начал печатать прямо после того, как вы ее опубликовали! Сумасшедший ...
user81655

5

Python2, 105 98 байт

7 байтов сохранено благодаря комментарию @Dennis

n=input()
r=([],[n/2+1])[n%2]
for i in range(n/2,0,-1):k=[n+1-i];r=([i]+r+k,k+r+[i])[i%2]
print r

Отредактированная версия 58 байт

lambda n:[(n-i-1,i)[(i+(n,1)[i<n/2])%2]for i in range(n)]

Я уже верил, что это можно сделать как одну строчку, но логика была слишком сложной для меня. Увидев JavaScript-ответ @ user81655 и лямбда-нотацию в @Dennis Python-answer, я дал ему новую попытку.

Условие равно

if i < n/2:
    i%2 != n%2
else:
    (i+1)%2

К сожалению, все усилия по преобразованию экономят только один байт по сравнению с прямым переводом (i<n/2or n%2)!=i%2JavaScript-логики.


3
Добро пожаловать в Программирование Пазлов и Code Golf! Похоже, что это Python 2, поэтому вам не нужно int()вводить вокруг. Кроме того, вы можете поместить тело цикла for в ту же строку, что и for....
Деннис

4

Python, 51 49 байт

lambda n:[(i^min(i,~i%n)%-2)%n for i in range(n)]

Спасибо @xnor за 2 байта!

Попробуйте это на Ideone .

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

Если i является числом в [0, ..., n - 1] , то ~ i% n = - (i + 1)% n = - (i + 1) + n = (n - 1) - i , это означает, что он отображает от 0 до n - 1 , от 1 до n - 2 и, в общем, от j- го элемента слева до j- го справа.

Как объяснено в моем ответе Jelly , мы можем построить вывод, посмотрев на более низкое значение среди i и ~ i% n , и выбрать i, если он четный, и ~ i% n, если он нечетный. Мы достигаем этого следующим образом.

  • Если минимум является четным, min(i,~i%n)%-2даст 0 , поэтому XOR результата с i даст i , а вычисление его остатка по модулю n вернет i .

  • Если минимум нечетный, min(i,~i%n)%-2даст -1 , поэтому XOR результата с i даст ~ i , так что все выражение оценивается как ~ i% n по желанию.


Вы можете сохранить пару символов, выполнив условный оператор as (i^min(i,n+~i)%-2)%n.
xnor

Это не только коротко, но и безумно умно. Спасибо!
Деннис

2

PHP, 77 76 51 50 49 байт

Использует кодировку ISO 8859-1.

Сборка первой половины массива выглядит так:

  • Нечетные числа имеют значение индекса (1, 3, 5 ..)
  • Четные числа имеют значение N+1-index(9, 7, 5)
  • Это приводит к 1, 9, 3, 7, 5

Что касается второй половины массива, то к сумме добавляются внешние значения N+1, что означает, что вы можете получить соответствующее правильное значение, из N-[left value]которого левое значение уже известно.

for(;$k=$argv[1]-$j++;)echo" ",min($j,$k)%2?$j:$k;

Выполните так (это также показывает общую разницу в квадратах) ( -dдобавлено только для эстетики):

php -d error_reporting=32757 -r 'for(;$k=$argv[1]-$j++;)echo~ß,$x[]=min($j,$k)%2?$j:$k;  for(;$c=$x[+$i++];)$b+=($c-($x[$i]?:$x[0]))**2;echo"\n$b\n";' 10
  • Сохраняет байт, отменяя условие влево / вправо, так что вторая троица может быть вложена без скобок
  • Экономия 25 байтов благодаря бесстыдной реализации алгоритма Денниса
  • Сохраненный байт, избавившись от необходимого места после echo
  • Сохраненный байт, используя для получения пробела.

1

Python 2, 100

Я знаю, что уже есть ответ Python, но я думаю, что я мог бы сделать это по-другому.

n=input();a=n%2;b=n/2;x=[b+1,b+a][a:]
for i in range(b+a-1):f=1-i%2*2;x=[x[-1]-f]+x+[x[0]+f]
print x

И в качестве дополнения, чтобы проверить общий балл:

def t(x,n):return sum((x[i]-x[(i+1)%n])**2for i in range(n))

def t(x,n):return sum((x[i]-x[i-1])**2for i in range(n))использует неявный перенос отрицательных индексов и сохраняет 4 байта. Я знаю, не был частью конкурса. ;)
btwlf

1

CJam, 17 15 14 байтов

{,W%ee_::e<.=}

Это функция, которая извлекает целое число n из стека и возвращает взамен перестановку [0… n-1] . Код использует тот же подход, что и мой ответ Jelly .

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

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

,W%ee_::e<.=    Function body. Stack: N

,               Turn N into [0 ... N-1].
 W%             Reverse to push [N-1 ... 0].
   ee           Enumerate. This pushes [[0 N-1] [1 N-2] ... [N-2 1] [N-1 0]].
     _          Push a copy of the array of pairs.
      ::e<      Reduce each pair by minimum.
          .=    Vectorized selection.
                For the Ith minimum M, select the Mth element of the Ith pair.
                Indices are modular and 0-based in CJam, so this selects the first
                element if the minimum is even, and the second one if it is odd.

1

LISP, 86 байт

(defun g(n m)(if(= n m)(list n)(if(< m n)(cons m(reverse(cons n(g(- n 1)(+ m 1))))))))

Входы функции позволяют выбрать начальное (m) и конечное (n) значения последовательности.

Для тестирования функции в соответствии с предоставленными образцами n фиксируется на N, а m на 1.

Вот код для проверки функции:

    (defun g(n m)(if(= n m)(list n)(if(< m n)(cons m(reverse(cons n(g(- n 1)(+ m 1))))))))

(defun sq (c)
    (apply #'+ (mapcar #'(lambda(x y) (* (- x y) (- x y))) c (append (cdr c) (list (car c))))))

(format t "N~20TSequence~50TSquared Difference~%")
(mapcar #'(lambda (x)(format t "~S~20T~S~50T~S~%" x (g x 1) (sq (g x 1)))) '(1 2 3 4 5 6 7 8 9 10 33 100 333 1000))

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


1

Юлия, 39 байт

n->map(i->min(i-1,n-i)%2>0?n-~-i:i,1:n)

Это печатает перестановку 1: n . Перестановка 0: n-1 не требует ни стоимости, ни сохранения байтов:

n->map(i->min(i,n+~i)%2>0?i:n+~i,0:n-1)

Эта последняя версия является прямым портом моего ответа на Python .


0

ES6, 77 байт

n=>[...Array(n)].map(_=>r[++i&2?"push":"unshift"](i&1?n--:++j),i=j=0,r=[])&&r

Эти i&1образцы цифры от крайних к середине. i&2Добавляет их в начале или в конце результата в пар.


0

Р, 117 86 байт

z=1:(n<-scan());a=pmin(z,n:1);for(i in seq(2,,2,n%/%2))z[b]=z[rev(b<-which(a==i,T))];z

редактировать заменил длинную версию с ошибкой реализацией алгоритма @Dennis 'Jelly

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