最大化平方差


19

考虑从1到的整数值的置换N。例如此示例N = 4

[1, 3, 4, 2]

我们将认为此列表是循环的,因此12被视为相邻列表。我们可以为这样的列表计算的一个量是相邻值的总平方差:

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

给定正整数,您的任务是找到一个最大化此数量的排列N。在N = 4上面的例子中不是最佳的(实际上,这是最小的)。18通过以下排列(以及其他几个排列),我们可以实现的总平方差:

[1, 4, 2, 3]

您的算法必须在(的N)多项式时间内运行。特别是,您不能简单地计算所有排列的总平方差。

您可以编写程序或函数,通过STDIN(或最接近的替代方案),命令行自变量或函数自变量获取输入,并通过STDOUT(或最接近的替代方案),函数返回值或函数(out)参数输出结果。

输出可以采用任何方便,明确,平坦的列表或字符串格式。您可以选择从与返回值的列表0,以N-1代替1N

适用标准规则。

测试数据

这个问题有一个很好的分析解决方案。例如,所有有效的解决方案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(如果您遇到困难,它也包含对本文的引用以及解决此难题的方法)。

Answers:


7

Jelly,24 21 15 14 10 9个字节

RUĖµ«/€ị"

要计算总平方差,请附加µ_ṙ1$²S到代码中。在线尝试!

背景

产生具有最大平方差的置换的一种方法是,将整数1n按升序排列,然后将左侧的第二个与右侧的第二个交换,将左侧的第四个与右侧的第四个交换,等等。直到我们在中间相遇为止。

例如,对于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)

@Neil节省了9个字节!

说明

此方法确定应在索引处的数字,i其长度为,n而不是将结果连接到数组。这基于以下观察(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 '16

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中的lambda表示法,我尝试了一下。

条件等于

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

不幸的是,与JavaScript逻辑的直接转换相比,所有转换工作仅节省了一个字节(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)]

感谢@xnor打高尔夫球2个字节!

Ideone上尝试一下。

怎么运行的

如果是一个数字 [0,...,n-1]中的数字,则〜i%n =-(i + 1)%n =-(i + 1)+ n =(n-1)-i,这意味着它映射0N - 11N - 2,并且在一般情况下,Ĵ 从左侧向项目Ĵ 从右侧。

正如Jelly答案中所解释的,我们可以通过观察i〜i%n中的较低值来构造输出,如果i是偶数,则选择i,如果是奇数,则选择〜i %n。我们实现如下。

  • 如果最小值是偶数,min(i,~i%n)%-2将产生0,因此将结果与i进行异或将产生i,并且计算其余数以n为模将返回i

  • 如果最小值是奇数, min(i,~i%n)%-2将产生-1,所以进行异或运算的结果与将产生〜我,所以整个表达式评估为〜我%正如所期望。


您可以通过执行条件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
  • 通过否定左/右条件保存一个字节,以便第二个三进制可以嵌套而不带括号
  • 通过无耻地实现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算法实现替换越野车长版

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.