Максимізуйте різницю в квадраті


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]

Ваш алгоритм повинен запускатися в поліноміальний час (з 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
  ^   ^ ^   ^            ^   ^   ^   ^

(carets марка цілих чисел, які потрібно поміняти), що призводить до

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.
Ніл

@Neil Нічого собі, я просто прокинувся, вирішив пограти в цей гольф і придумав вашу точну логіку і почав набирати це правильно, як ви це розмістили! Божевільний ...
користувач81655

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-відповіді, я спробував її.

Умова дорівнює

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

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


3
Ласкаво просимо до головоломки програмування та коду для гольфу! Здається, це Python 2, тому int()навколо вводу вам не потрібен . Крім того, ви можете розмістити тіло для циклу на тій же лінії, що і тіло for....
Денніс

4

Пітон, 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 до п - 1 , 1 до п - 2 і, в загальному, J - й елемент зліва на J - го праворуч.

Як пояснювалося в моєму Jelly відповідь , ми можемо побудувати висновок по визирав на більш низьке значення серед I і ~ I% п , і виберіть I , якщо вона навіть і ~ I% п , якщо вона непарна. Ми досягаємо цього наступним чином.

  • Якщо мінімум є рівним, min(i,~i%n)%-2вийде 0 , тож XOR, отримавши результат i , дасть i , і обчисливши його залишковий модуль n поверне i .

  • Якщо мінімум непарний, min(i,~i%n)%-2вийде -1 , тож XORing результату з i дасть ~ i , тому весь вираз оцінюється до ~ i% n за бажанням.


Ви можете зберегти пару символів, виконавши умовно як (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

Пітон 2, 100

Я знаю, що відповідь пітона вже є, але я думаю, що, можливо, я це зробив інакше.

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

R, 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.