我必须列出这样的名单:
a = ["1a","2a","3a","4a","5a","6a","7a","8a","9a","10a","11a","12a","13a","14a"]
b = ["1b","2b","3b","4b","5b","6b","7b","8b","9b","10b","11b","12b","13b","14b"]
我想要的是将它们组合在一起,以便 a 中的 n 元素与 n 元素之间至少存在差异。 b 中的相应元素。
例如,如果我的 n 是10,那么" 3a"在3号位和" 3b"位于第5位,这不是一个解决方案,因为这些对应元素之间的距离只有2。
我已经通过强力方法解决了这个问题:将两个数组的并集洗牌并查看是否满足约束条件;如果没有,再次洗牌等等......毋庸置疑,对于14个元素数组,有时会有5到10秒的计算产生一个最小距离为10的解决方案。尽管这样做还不错对于我正在寻找的东西,我很好奇如何以更优化的方式解决这个问题。
我目前正在使用Python,但是对任何语言(或伪代码)的代码都非常欢迎。
编辑:此问题的背景类似于问号,预计约有100名参与者加入。因此,我不一定对所有感兴趣解决方案,但更像是前100个。
感谢。
答案 0 :(得分:1)
对于您的特定情况,您可以使用随机方法 - 尽管不像您已尝试过的那样随意。像这样:
在每次迭代中改变整个列表的差异(如在你的方法中)是在每次迭代中,排列只能变得更好,直到满意的解决方案是找到。
每次运行此算法时,结果都会略有不同,因此您可以针对100种不同的解决方案运行100次。当然,这个算法并不能保证找到一个解决方案(更不用说所有这样的解决方案)了,但它应该足够快,以便你可以在它失败的情况下重新启动它。
在Python中,这看起来有点像(稍微简化,但仍然有效):
def shuffle(A, B):
# original positions, i.e. types of questions
kind = dict([(item, i) for i, item in list(enumerate(A)) + list(enumerate(B))])
# get positions of elements of kinds, and return sum of their distances
def quality(perm):
pos = dict([(kind[item], i) for i, item in enumerate(perm)])
return sum(abs(pos[kind[item]] - i) for i, item in enumerate(perm))
# initial permutation and quality
current = A + B
random.shuffle(current)
best = quality(current)
# improve upon initial permutation by randomly swapping items
for g in range(1000):
i = random.randint(0, len(current)-1)
j = random.randint(0, len(current)-1)
copy = current[:]
copy[i], copy[j] = copy[j], copy[i]
q = quality(copy)
if q > best:
current, best = copy, q
return current
print shuffle(a, b)
的示例输出:
['14b','2a','13b','3a','9b','4a','6a','1a','8a','5b','12b','11a ','10b','7b','4b','11b','5a','7a','8b','12a','13a','14a','1b','2b', '3b','6b','10a','9a']
答案 1 :(得分:0)
正如我从你的问题中理解的那样,可以通过完全依赖于数组的索引(即纯整数)来执行所有排序,因此可以减少问题以创建(有效)范围而不是分析每个元素。
对于每个a< = total_items-n,有效b = if(a + n == total_items){total_items} else {[a + n,total_items]}
例如:
n = 10; total_items = 15;
表示a = 1 - >有效b = [11,15]
表示a = 2 - >有效b = [12,15]
等。
这将执行4次:向前和向后表示对b的尊重,对于b对b表示相同。
通过这种方式,您可以将迭代次数减少到最小值,并作为输出获得每个位置的一组“解决方案”,而不是一对一的绑定(这就是你所拥有的)现在,不是吗?)。
答案 2 :(得分:0)
如果Python中有等效的.NET列表和LINQ,那么您可以直接转换以下代码。它可以非常快速地生成多达100个列表:我按下“debug”来运行它,然后在不到一秒的时间内弹出一个窗口,结果很短。
' VS2012
Option Infer On
Module Module1
Dim minDistance As Integer = 10
Dim rand As New Random ' a random number generator
Function OkToAppend(current As List(Of Integer), x As Integer) As Boolean
' see if the previous minDistance values contain the number x
Return Not (current.Skip(current.Count - minDistance).Take(minDistance).Contains(x))
End Function
Function GenerateList() As List(Of String)
' We don't need to start with strings: integers will make it faster.
' The "a" and "b" suffixes can be sprinkled on at random once the
' list is created.
Dim numbersToUse() As Integer = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}
Dim pool As New List(Of Integer)
' we need all the numbers twice
pool.AddRange(numbersToUse)
pool.AddRange(numbersToUse)
Dim newList As New List(Of Integer)
Dim pos As Integer
For i = 0 To pool.Count - 1
' limit the effort it puts in
Dim sanity As Integer = pool.Count * 10
Do
pos = rand.Next(0, pool.Count)
sanity -= 1
Loop Until OkToAppend(newList, pool(pos)) OrElse sanity = 0
If sanity > 0 Then ' it worked
' append the value to the list
newList.Add(pool(pos))
' remove the value which has been used
pool.RemoveAt(pos)
Else ' give up on this arrangement
Return Nothing
End If
Next
' Create the final list with "a" and "b" stuck on each value.
Dim stringList As New List(Of String)
Dim usedA(numbersToUse.Length) As Boolean
Dim usedB(numbersToUse.Length) As Boolean
For i = 0 To newList.Count - 1
Dim z = newList(i)
Dim suffix As String = ""
If usedA(z) Then
suffix = "b"
ElseIf usedB(z) Then
suffix = "a"
End If
' rand.Next(2) generates an integer in the range [0,2)
If suffix.Length = 0 Then suffix = If(rand.Next(2) = 1, "a", "b")
If suffix = "a" Then
usedA(z) = True
Else
usedB(z) = True
End If
stringList.Add(z.ToString & suffix)
Next
Return stringList
End Function
Sub Main()
Dim arrangements As New List(Of List(Of String))
For i = 1 To 100
Dim thisArrangement = GenerateList()
If thisArrangement IsNot Nothing Then
arrangements.Add(thisArrangement)
End If
Next
'TODO: remove duplicate entries and generate more to make it up to
' the required quantity.
For Each a In arrangements
' outputs the elements of a with ", " as a separator
Console.WriteLine(String.Join(", ", a))
Next
' wait for user to press enter
Console.ReadLine()
End Sub
End Module