Maksimalkan perbedaan kuadrat


19

Pertimbangkan permutasi nilai integer dari 1hingga N. Misalnya contoh ini untuk N = 4:

[1, 3, 4, 2]

Kami akan mempertimbangkan daftar ini menjadi siklik, sehingga 1dan 2diperlakukan sebagai yang berdekatan. Satu kuantitas yang dapat kita hitung untuk daftar tersebut adalah total selisih kuadrat dari nilai yang berdekatan:

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

Tugas Anda adalah menemukan permutasi yang memaksimalkan kuantitas ini, diberi bilangan bulat positif N. Dalam kasus N = 4contoh di atas tidak optimal (pada kenyataannya, itu minimal). Kami dapat mencapai perbedaan kuadrat total 18dengan permutasi berikut (serta beberapa yang lain):

[1, 4, 2, 3]

Algoritme Anda harus berjalan dalam waktu polinomial N. Secara khusus, Anda tidak bisa hanya menghitung perbedaan kuadrat total dari semua permutasi.

Anda dapat menulis sebuah program atau fungsi, mengambil input melalui STDIN (atau alternatif terdekat), argumen baris perintah atau argumen fungsi dan mengeluarkan hasilnya melalui STDOUT (atau alternatif terdekat), nilai pengembalian fungsi atau parameter function (out).

Output dapat dalam format string atau string yang nyaman, tidak ambigu, datar. Anda dapat memilih untuk mengembalikan daftar dengan nilai dari 0ke N-1alih-alih 1ke N.

Aturan standar berlaku.

Data Uji

Ada solusi analitik yang bagus untuk masalah ini. Misalnya, semua solusi yang valid untuk N = 10setara dengan daftar berikut (hingga perubahan dan pembalikan siklus):

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

Saya tidak ingin mengungkapkan terlalu banyak di luar itu (walaupun mungkin cukup untuk mengetahui polanya), jadi alih-alih memberikan contoh lagi, Anda dapat memeriksa bahwa hasil Anda memiliki perbedaan kuadrat total berikut untuk diberikan 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

Ini adalah entri OEIS A064842 (yang juga berisi referensi ke kertas dengan solusi untuk tantangan ini jika Anda macet).

Jawaban:


7

Jelly, 24 21 15 14 10 9 byte

RUĖµ«/€ị"

Untuk menghitung perbedaan kuadrat total, tambahkan µ_ṙ1$²Skode. Cobalah online!

Latar Belakang

Salah satu cara untuk menghasilkan permutasi dengan perbedaan kuadrat dimaksimalkan adalah dengan mengambil bilangan bulat 1 ke n dalam urutan menaik, dan menukar yang kedua dari kiri dengan yang kedua dari kanan, yang keempat dari kiri dengan yang keempat dari kanan, dan seterusnya maju sampai kita bertemu di tengah.

Misalnya, untuk n = 8, 9 yang kita miliki

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

(tanda tanda integer yang akan ditukar), yang menghasilkan

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

setelah bertukar.

Salah satu cara untuk mencapai swap ini, terlepas dari paritas n , adalah sebagai berikut.

Mulailah dengan menulis bilangan bulat dalam urutan menaik dan dalam urutan menurun, satu di bawah yang lain.

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

Untuk setiap pasangan bilangan bulat, hitung minimum pasangan. Ini memberikan jarak ke tepi terdekat, yaitu indeks dari kiri atau kanan (mana yang lebih rendah).

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

Jika minimum ganjil, bilangan bulat harus tetap di tempatnya, jadi kami memilih yang dari baris pertama; jika itu genap, bilangan bulat harus ditukar, jadi kami memilih yang dari baris kedua.

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

Ini adalah output yang diinginkan.

Bagaimana itu bekerja

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 byte

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

9 byte disimpan berkat @Neil!

Penjelasan

Pendekatan ini menentukan angka yang harus di indeks idengan panjang ndaripada menggabungkan hasil ke array. Ini didasarkan pada pengamatan berikut (menggunakan n = 7sebagai contoh):

  • Mulai dengan angka terendah di kiri dan tertinggi di kanan: [ 1, 7 ]
  • Ganti urutan sehingga yang terendah ada di kanan dan yang tertinggi ada di kiri, tambah yang terendah, turunkan yang tertinggi, dan letakkan di tengah array:[ 1, 6, 2, 7 ]
  • Ulangi sampai konvergen tertinggi dan terendah: [ 1, 6, 3, 4, 5, 2, 7 ]

Angka yang lebih tinggi dan lebih rendah dapat dengan mudah dinyatakan sebagai n-idan i+1masing - masing.

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>


Algoritma yang bagus; Saya mencoba dan gagal memikirkan formula untuk menghasilkannya, jadi saya harus menggunakan metode yang lebih buruk yaitu mendorong dan melepaskan. Namun, tentu saja saya dapat menyederhanakan logika Anda (i<n/2||n%2)^i%2?i+1:n-i.
Neil

@Neil Wow, saya baru saja bangun, memutuskan untuk bermain golf ini dan muncul dengan logika yang tepat dan mulai mengetiknya tepat saat Anda mempostingnya! Gila ...
user81655

5

Python2, 105 98 byte

7 byte disimpan berkat komentar oleh @ 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

Versi yang diedit 58 byte

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

Saya sudah percaya itu harus dilakukan sebagai satu-liner, tetapi logikanya terlalu kompleks bagi saya. Melihat JavaScript-answer oleh @ user81655 dan notasi lambda di @Dennis Python-answer, saya mencobanya baru.

Kondisinya sama dengan

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

Sayangnya semua upaya transformasi hanya menghemat satu no byte dibandingkan dengan terjemahan langsung (i<n/2or n%2)!=i%2dari logika-JavaScript.


3
Selamat Datang di Programming Puzzles & Code Golf! Ini tampaknya Python 2, jadi Anda tidak perlu int()sekitar input. Juga, Anda dapat meletakkan tubuh for loop pada baris yang sama dengan for....
Dennis

4

Python, 51 49 byte

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

Terima kasih kepada @xnor karena bermain golf 2 byte!

Cobalah di Ideone .

Bagaimana itu bekerja

Jika saya adalah angka dalam [0, ..., n - 1] , maka ~ i% n = - (i + 1)% n = - (i + 1) + n = (n - 1) - i , artinya peta 0 untuk n - 1 , 1 ke n - 2 dan, secara umum, j th item dari kiri ke j th dari kanan.

Seperti yang dijelaskan dalam jawaban Jelly saya , kita dapat membuat output dengan mengintip nilai yang lebih rendah di antara i dan ~ i% n , dan memilih i jika itu genap dan ~ i% n jika itu ganjil. Kami mencapai ini sebagai berikut.

  • Jika minimum bahkan, min(i,~i%n)%-2akan menghasilkan 0 , sehingga XORing hasilnya dengan i akan menghasilkan i , dan komputasi modulo residunya n akan kembali i .

  • Jika minimumnya ganjil, min(i,~i%n)%-2akan menghasilkan -1 , jadi XORkan hasilnya dengan i akan menghasilkan ~ i , sehingga seluruh ekspresi dievaluasi menjadi ~ i% n seperti yang diinginkan.


Anda dapat menyimpan beberapa karakter dengan melakukan persyaratan sebagai (i^min(i,n+~i)%-2)%n.
xnor

Itu tidak hanya pendek tetapi sangat pintar. Terima kasih!
Dennis

2

PHP, 77 76 51 50 49 byte

Menggunakan penyandian ISO 8859-1.

Merakit paruh pertama array seperti ini:

  • Angka ganjil memiliki nilai indeks mereka (1, 3, 5 ..)
  • Angka genap memiliki nilai N+1-index(9, 7, 5)
  • Ini menghasilkan 1, 9, 3, 7, 5

Sedangkan untuk paruh kedua array, nilai terluar dijumlahkan N+1, yang berarti Anda bisa mendapatkan nilai yang tepat dari N-[left value]mana nilai kiri sudah diketahui.

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

Jalankan seperti ini (ini juga menunjukkan perbedaan kuadrat total) ( -dditambahkan hanya untuk estetika):

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
  • Disimpan byte dengan meniadakan kondisi kiri / kanan sehingga terner kedua dapat bersarang tanpa tanda kurung
  • Disimpan 25 byte dengan mengimplementasikan algoritma Dennis tanpa malu-malu
  • Menyimpan byte dengan menghilangkan ruang yang dibutuhkan setelahnya echo
  • Menyimpan byte dengan menggunakan untuk menghasilkan spasi.

1

Python 2, 100

Saya tahu sudah ada jawaban python, tapi saya pikir saya mungkin telah melakukan ini secara berbeda.

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

Dan sebagai tambahan untuk menguji skor total:

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))menggunakan wrap-around implisit dari indeks negatif dan menyimpan 4 byte. Saya tahu, bukan bagian dari kompetisi. ;)
btwlf

1

CJam, 17 15 14 byte

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

Ini adalah fungsi yang memunculkan integer n dari stack dan mendorong permutasi [0… n-1] sebagai imbalannya. Kode ini menggunakan pendekatan yang sama dengan jawaban Jelly saya .

Cobalah online!

Bagaimana itu bekerja

,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 byte

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

Input dari fungsi memungkinkan untuk memilih nilai awal (m) dan akhir (n) dari urutan.

Untuk menguji fungsi sesuai dengan sampel yang disediakan, n ditetapkan ke N dan m ke 1.

Di sini kode untuk menguji fungsinya:

    (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))

Cobalah di Ideone !


1

Julia, 39 byte

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

Ini mencetak permutasi 1: n . Permutasi 0: n-1 tidak memerlukan biaya atau menghemat byte:

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

Versi terakhir ini adalah port langsung dari jawaban Python saya .


0

ES6, 77 byte

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

The i&1sampel angka dari ekstrem ke tengah. The i&2menambahkan mereka ke awal atau akhir hasil berpasangan.


0

R, 117 86 byte

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

sunting versi lama buggy yang diganti dengan penerapan algoritme @Dennis 'Jelly

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.