二乗差を最大化する


19

整数値の順列を検討1するがN。たとえば、次の例N = 4

[1, 3, 4, 2]

このリストは循環的である1と見なし、そのように、および2隣接として扱われます。そのようなリストについて計算できる1つの量は、隣接する値の差の2乗の合計です。

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

あなたの仕事は、正の整数を与えられて、この量を最大にする順列を見つけることNです。N = 4上記の例の場合、最適ではありません(実際、最小限です)。18次の順列(および他のいくつかの順列)の合計平方差を達成できます。

[1, 4, 2, 3]

アルゴリズムは(のN)多項式時間で実行する必要があります。特に、すべての順列の差の合計を単純に計算することはできません。

プログラムまたは関数を作成し、STDIN(または最も近い代替)、コマンドライン引数または関数引数を介して入力を取得し、STDOUT(または最も近い代替)、関数の戻り値または関数(out)パラメーターを介して結果を出力できます。

出力は、任意の便利で明確なフラットリストまたは文字列形式にすることができます。0toのN-1代わりに1toの値を持つリストを返すことを選択できます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つの方法は、整数1からnを昇順に取得し、左から2番目を右から2番目に、左から4番目を右から4番目に、などです。途中で会うまで。

例えば、のための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つの方法は次のとおりです。

整数を昇順と降順で、上下に並べて書き込むことから始めます。

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

最小値が奇数の場合、整数はその位置にとどまるはずなので、最初の行から整数を選択します。偶数の場合、整数を交換する必要があるため、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)

@Neilのおかげで9バイト節約!

説明

このアプローチは、結果を配列に連結するのinはなく、インデックスの長さを決定する必要があります。これは、次の観察に基づいています(n = 7例として使用)。

  • 左側の最小数から始め、右側の最高数から始めます。 [ 1, 7 ]
  • 順序を切り替えて、最低が右側、最高が左側になるようにし、最低を増やし、最高を減らし、配列の中央に配置します。[ 1, 6, 2, 7 ]
  • 最高と最低が収束するまで繰り返します。 [ 1, 6, 3, 4, 5, 2, 7 ]

高い数値と低い数値はn-ii+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 Wow、私は目を覚まし、これをゴルフすることを決めて、あなたの正確なロジックを思いつき、あなたが投稿したとおりにそれを入力し始めました!クレイジー
...-user81655

5

Python2、105の 98バイト

@Dennisのコメントのおかげで7バイト節約

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

ワンライナーとしてそれを行うことが可能であるべきだとすでに信じていましたが、私にとって論理は複雑すぎました。@ user81655によるJavaScript-answerと@Dennis Python-answerのラムダ表記を見て、新しい試みをしました。

条件は等しい

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

残念ながら、すべての変換作業は、JavaScriptロジックの直接変換と比較して1バイトしか節約しません(i<n/2or n%2)!=i%2


3
プログラミングパズルとコードゴルフへようこそ!これはPython 2のように見えるのでint()、入力の周りは必要ありません。また、forループの本体をと同じ行に配置できfor...ます。
デニス

4

Python、51 49バイト

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

2バイトのゴルフをしてくれた@xnorに感謝します!

Ideoneで試してみてください。

使い方

i[0、...、n-1]の数値の場合、〜i%n =-(i + 1)%n =-(i + 1)+ n =(n-1)-i、これは、0n-1に1n-2に、そして一般に、左からj 番目のアイテムを右からj 番目にマッピングすることを意味します。

で説明したように、私のゼリーの答えは、我々は間の低い値で覗き見して出力を構築することができ、私〜I%N、および選ぶ私をそれもあるし、場合〜I%をnはそれが奇数の場合。これを次のように達成します。

  • 最小が偶数の場合、min(i,~i%n)%-2もたらす0を有する結果XORするので、私がもたらす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;

次のように実行します(これは合計の2乗差も示します)(-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
  • 左/右条件を否定することでバイトを保存し、2番目の3項を括弧なしでネストできるようにしました
  • Dennisのアルゴリズムを恥知らずに実装することで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]の順列をプッシュする関数です。コードは私のゼリーの答えと同じアプローチを使用しています

オンラインでお試しください!

使い方

,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アルゴリズムの実装に置き換えて編集する

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.