Python 重新排列列表以最大化相邻元素的差异
我感兴趣的是以这样一种方式重新排列列表,以最大化相邻元素之间差异的平方和(循环)。下面是一段Python代码,它在阶乘时间内强制执行解决方案,因此您可以了解我的意思:Python 重新排列列表以最大化相邻元素的差异,python,algorithm,permutation,Python,Algorithm,Permutation,我感兴趣的是以这样一种方式重新排列列表,以最大化相邻元素之间差异的平方和(循环)。下面是一段Python代码,它在阶乘时间内强制执行解决方案,因此您可以了解我的意思: def maximal_difference_reorder(input): from itertools import permutations best_sum = 0 best_orderings = [] for x in permutations(input): d = np
def maximal_difference_reorder(input):
from itertools import permutations
best_sum = 0
best_orderings = []
for x in permutations(input):
d = np.sum(np.diff(x)**2) + (x[0] - x[-1])**2
if d > best_sum:
best_orderings = [x]
best_sum = d
elif d == best_sum:
best_orderings.append(x)
return best_orderings
对于最大差异重排序(范围(4))
,这将产生以下结果:
如您所见,所有结果都是循环旋转和相互反射。如果分数是用差值之和而不是平方来确定的,我相信所有的排列都会得到均匀的分数,给定一个均匀间隔的输入
暴力强迫很有效,但O(n!)很糟糕,所以有可能在较小的渐近计算时间内完成吗?如果它适用于不均匀的输入网格或其他计分功能,则可获得额外积分
顺便说一句,这不是家庭作业或面试问题,尽管这可能是个好问题。相反,我试图为一系列参数化数据生成一个颜色光谱,并试图避免颜色彼此相邻。您的问题只是一个稍微掩饰的例子 调用输入列表
c
(用于“城市”)。选择任何M
,这是(c[i]-c[j])**2的上限。这很容易在线性时间内完成,因为列表的最小值和最大值可以在一次传递中计算,在这种情况下M=(max-min)**2
起作用。通过以下方式定义从c[i]
到c[j]
的距离d[i,j]
:
d(i,j) = 0 if i == j else M - (c[i]-c[j])**2
很容易看出,对于任何循环置换,该置换的成本(根据d
计算)是n*M-差平方和
,因此,当且仅当差平方和最大化时,该成本最小化
有很多方法可以解决TSP问题。尽管它是NP难的,但在实践中,最先进的方法在解决实践中出现的问题方面表现得非常出色。此外,好的启发式方法通常可以达到最优解的百分之一以内
您的特定问题是TSP的一个特例。因此,这种特殊情况可能更容易,而且实际上有一个多项式时间解,但我对此表示怀疑。我猜想它也是NP难的,但没有证据。而且——即使它是NP难的,也可能有一个解决方案(也许是一个整数规划公式)比将其简化为上面提到的TSP更有效
关于编辑:根据Dave Gavin的评论和@SergeBallesta的回答,我现在认为多项式时间算法是可能的。我将保留这个答案,如果没有其他原因,除非多项式时间算法有效,那么这个问题将是一个很好的例子,表明TSP的某些子类有更简单的解决方案 如果您试图以循环方式最大化连续元素之间差异的平方,我会说您应该尝试使最大元素接近最小元素,因为从概念上讲a²+b²>=2*((a+b)/2)²
。这就是你用蛮力在范围(4)
中发现的
我认为可以通过归纳法来说明这一点,但这一部分应该问得更好,但我敢打赌,解决方案只是:
- 对列表排序
- 取最大元素,将其放入结果列表的索引0中
- 取最小的一个放在索引1上
- 取剩余的最小值并将其放在索引-1上
- 取剩余的最大值并将其放在索引2上
并在右边和左边交替迭代一次剩余元素的最大值和最小值
你的结局是:
- O(n*log(n))使用快速排序或合并排序的排序统计,或O(n²/2)仅使用冒泡排序
- 用于构建结果数组的线性
我想我们可以有一个O(n)解决方案
解决这个问题的关键是为循环群生成第一个种子。考虑到我们应该配对元素,其中成对平方差和最大,如果我们将一个元素与其最远的邻居配对,这是可能的
也就是说,如果hi是第i个最大的数,那么hi的邻居是(hn-i-1,hn-i+1)。由于序列是循环的,因此数字将围绕负指数,即h-1=h0
这将生成第一个种子列表作为
[0,6,2,4,3,5,1,7]
通过交换每个奇数索引对,即[(a1,an-1),(a3,an-3),…]可以很容易地生成该序列
随后的序列可以通过生成一个奇异的顺序旋转,然后反射旋转后的序列来生成
下面是一个示例实现
def maximal_difference_reorder1(x):
def maximal_difference_seeder(x):
for i in range(1, len(x) / 2):
x[i:len(x) - i] = x[i:len(x) - i][::-1]
return x
def rotate_left(x):
start = x
while True:
x = x[1:] + x[0:1]
if x == start: break
yield x
x = maximal_difference_seeder(x)
rotated = [x] + (list(rotate_left(x)) if len(x) > 1 else [])
reflected = [e[::-1] for e in rotated] if len(x) > 2 else []
return map(tuple, rotated + reflected)
样本运行
def display(lst, width = 80):
it_lst = iter(lst)
try:
print '[',
while True:
for _ in range(80/(len(lst[0])*3 + 2)):
print "{},".format(next(it_lst)),
print '\n ',
except StopIteration:
print ']'
display(maximal_difference_reorder1(range(10)))
[ (0, 8, 2, 6, 4, 5, 3, 7, 1, 9), (8, 2, 6, 4, 5, 3, 7, 1, 9, 0),
(2, 6, 4, 5, 3, 7, 1, 9, 0, 8), (6, 4, 5, 3, 7, 1, 9, 0, 8, 2),
(4, 5, 3, 7, 1, 9, 0, 8, 2, 6), (5, 3, 7, 1, 9, 0, 8, 2, 6, 4),
(3, 7, 1, 9, 0, 8, 2, 6, 4, 5), (7, 1, 9, 0, 8, 2, 6, 4, 5, 3),
(1, 9, 0, 8, 2, 6, 4, 5, 3, 7), (9, 0, 8, 2, 6, 4, 5, 3, 7, 1),
(9, 1, 7, 3, 5, 4, 6, 2, 8, 0), (0, 9, 1, 7, 3, 5, 4, 6, 2, 8),
(8, 0, 9, 1, 7, 3, 5, 4, 6, 2), (2, 8, 0, 9, 1, 7, 3, 5, 4, 6),
(6, 2, 8, 0, 9, 1, 7, 3, 5, 4), (4, 6, 2, 8, 0, 9, 1, 7, 3, 5),
(5, 4, 6, 2, 8, 0, 9, 1, 7, 3), (3, 5, 4, 6, 2, 8, 0, 9, 1, 7),
(7, 3, 5, 4, 6, 2, 8, 0, 9, 1), (1, 7, 3, 5, 4, 6, 2, 8, 0, 9),
]
注意假设数据已排序。如果不是,那么对其进行排序就很简单了,其中解决方案的复杂性为O(nlog n)以下是我在评论中建议的贪婪算法:
贪婪算法怎么样?在每一步中,预加或附加数字以最大程度地增加分数(这将形成一个先增加后减小振幅的锯齿波)。尝试从最小的数字或最大的数字开始
研究贪婪算法不是最优算法的例子会很有趣
# https://stackoverflow.com/questions/34154324/reordering-a-list-to-maximize-difference-of-adjacent-elements?s=2|0.0000
import itertools
def score(x):
return sum((a-b)**2 for a,b in zip(x, x[1:]))
assert score([0, 2, 5]) == 4 + 9
def maximise(x):
x = sorted(x)
candidates = [greedy(x[:1], x[1:]), greedy(x[-1:], x[:-1])]
return max(candidates, key=score)
def greedy(current, remaining):
while remaining:
i, j = max(itertools.product((0, -1), repeat=2), key=lambda pair: score((current[pair[0]], remaining[pair[1]])))
current.insert(i, remaining.pop(j))
return current
def cyclic_score(x):
return sum((a-b)**2 for a,b in zip(x, x[1:] + x[:1]))
assert cyclic_score([0, 2, 5]) == 4 + 9 + 25
序列0,n-1,1,n-2,2,n-3,…,不保证范围(n)的最大平方差吗。。。?例如,你的0,3,1,2序列,例如0,4,1,3,2(5)等。我认为他在谈论一个随机数的列表。比如[n,2n+1,35n+1,x,y…]。然后你会得到66分,最大值实际上是77分。问题是,如果你能重复/省略不同的数字,那么你是围绕最大的数字还是最小的数字。。。我说我指的是范围(n),就像你在例子中使用的那样。@barny是的,这对sp是正确的
# https://stackoverflow.com/questions/34154324/reordering-a-list-to-maximize-difference-of-adjacent-elements?s=2|0.0000
import itertools
def score(x):
return sum((a-b)**2 for a,b in zip(x, x[1:]))
assert score([0, 2, 5]) == 4 + 9
def maximise(x):
x = sorted(x)
candidates = [greedy(x[:1], x[1:]), greedy(x[-1:], x[:-1])]
return max(candidates, key=score)
def greedy(current, remaining):
while remaining:
i, j = max(itertools.product((0, -1), repeat=2), key=lambda pair: score((current[pair[0]], remaining[pair[1]])))
current.insert(i, remaining.pop(j))
return current
def cyclic_score(x):
return sum((a-b)**2 for a,b in zip(x, x[1:] + x[:1]))
assert cyclic_score([0, 2, 5]) == 4 + 9 + 25