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
这会产生maximal_difference_reorder(range(4))
的以下结果:
[(0, 2, 1, 3),
(0, 3, 1, 2),
(1, 2, 0, 3),
(1, 3, 0, 2),
(2, 0, 3, 1),
(2, 1, 3, 0),
(3, 0, 2, 1),
(3, 1, 2, 0)]
如您所见,所有结果都是循环旋转和彼此反射。如果得分是用差值之和确定的,而不是平方,我相信所有排列都会得到均匀的评分,给出均匀间隔的输入。
暴力强迫效果很好,但是O(n!)很糟糕,所以可以在更小的渐近计算时间内完成这个吗?如果它适用于不均匀的输入网格或其他评分函数,则可获得奖励。
顺便说一下,这不是一个家庭作业或面试问题,尽管它可能会成为一个好问题。相反,我试图为一系列参数化数据生成一系列颜色,我试图避免彼此相似的颜色。
答案 0 :(得分:3)
您的问题是Traveling Salesman Problem的一个稍微伪装的实例。
调用输入列表c
(对于" cities")。选择M
作为(c[i]-c[j])**2
上限的任何M = (max - min)**2
这很容易在线性时间内完成,因为列表的最小值和最大值可以一次计算,在这种情况下d[i,j]
作品。通过以下方式定义从c[i]
到c[j]
的{{1}}距离:
d(i,j) = 0 if i == j else M - (c[i]-c[j])**2
很容易看出,对于任何循环置换,该置换的成本(根据d
计算)是n*M - sum of squares of differences
因此,当且并且仅差异的平方和为最大化。
解决TSP的方法很多。尽管它是NP难的,但在实践中,最先进的方法在解决实践中出现的问题方面非常擅长。此外,良好的启发式方法通常可以达到最佳百分比的一小部分。
您的特定问题是TSP的特例。因此,这种特殊情况可能更容易,实际上有一个多项式时间解决方案,但我对此表示怀疑。我猜想它也是NP难的,但没有证据。另外 - 即使它是NP难的,也许有一个解决方案(可能是一个整数编程公式)比如上所述将它减少到TSP更有效。
On Edit:根据Dave Gavin的评论和@SergeBallesta的回答,我现在认为可以使用多项式时间算法。我将这个答案留下来,如果没有其他原因,如果多项式时间算法工作,那么这个问题将是一个很好的例子,表明TSP的某些子类有更简单的解决方案。
答案 1 :(得分:2)
如果你试图以循环方式最大化连续元素之间差异的平方,我会说你应该尝试让最大元素接近最小元素,因为概念a²+b²>=2*((a+b)/2)²
。这就是你用range(4)
蛮力所发现的。
我认为它可以通过归纳显示出来,但应该在Mathematics上更好地询问这一部分,但我会打赌硬币解决方案只是为了:
并向右和向左迭代一次,交替剩余元素的最大和最小
你结束于:
答案 2 :(得分:1)
我认为我们可以有一个O(n)解决方案
解决此问题的关键是为循环组生成第一个种子。考虑到我们应该配对其中成对的方差差和是最大的元素,如果我们将元素与其最远的邻居配对,那么这是可能的。
这意味着如果h i 是i th 最高数,那么h i 的邻居是(h ni- 1 ,h n-i + 1 )。因为序列是循环的,所以数字会环绕负指数,即h -1 = h 0
这将生成第一个种子列表[0, 6, 2, 4, 3, 5, 1, 7]
这个序列很容易通过交换每个奇数索引对来生成,即[(a 1 ,a n-1 ),(a 3 , n-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)
答案 3 :(得分:1)
这是我在评论中建议的贪婪算法:
贪心算法怎么样?在每个步骤中,前置或附加数字以最大程度地增加分数(这将产生增加然后减小幅度的锯齿波)。尝试以最小数字或最大数字开头
研究贪心算法不是最优的例子会很有趣
# 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