鉴于我们有不同类型的视频集合(比如类型A,B和C,......),我们正在寻找一种有效的算法来将这些对象整理成一个播放列表,以便我们获得最大的分散。也就是说,我们希望确保来自A的两个视频不会背靠背放置,如果可以避免的话。 播放列表将重复播放(它会在播放结束时重新播放。因此也应考虑此方面)。
什么是一个有效的算法可以执行上述良好的分散?
示例输入:
输出 - 不是最佳
A1,B1,A2,B2,A3,B3,A4,A5
这不是最佳的,因为在播放A4之后,A5播放然后播放列表循环播放并播放A1。现在我们连续播放了A型的3个视频。
最佳输出
A1,B1,A2,A3,B2,A4,B4,A5
这是最佳选择,因为我们只有2个相同类型的视频背靠背播放。
请注意,该算法适用于不同类型和视频。
答案 0 :(得分:3)
这类似于我几年前遇到的一个问题:混合液体以避免分层。我们的想法是,如果您将液体A,B和C混合到一个容器中,您不希望将它们一个接一个地倒入容器中。相反,你想以相对比例添加一些A,一些B,一些C等。
与在列表中均匀分配项目的问题相同。
假设您有30个A类型,20个B类型和10个C类型,总共60个视频。然后,每个其他视频必须是A.每三个视频都是一个B,每六个视频就是一个C.
所以A是0,2,4,6,8等。 B&#39,6 / 12,12等。 C&C在0,6,12,18等等。
显然,您必须解决冲突。
我这样做的方法是构建一个包含视频类型及其频率的最小堆,以及从频率/ 2开始的当前位置。所以堆包含:{A,2,1},{B,3,1},{C,6,3}
。
要生成列表,请从堆中删除最低项并将其添加到列表中。然后,将其频率添加到当前位置,并将其放回堆中。因此,在第一次完成后,您输出了A,而您的堆现在包含:{B,3,1},{A,2,2},{C,6,3}
。
输出B,然后将其添加回来,为您提供{A,2,2},{C,6,3},{B,3,4}
您当然也希望保留每个项目的计数,每次输出该项目时都会减少,如果计数变为0,则不会将其添加回堆中。
一年前我在我的博客中写了很长篇篇章。请参阅Evenly distributing items in a list。
就效率而言,算法具有复杂度O(n log k),其中n
是视频总数,k
是视频类型的数量。
答案 1 :(得分:3)
这里的算法适用于任意数量的类型,而不仅仅是2:
伪码中的算法:
var size = 0;
for_each (T)
size += N(T);
var output = array(size); // Initialised to null, to mean gap (no item)
var gapsRemaining = size;
for_each (T)
{
var itemsRemaining = N(T);
var i = 0;
var limit = itemsRemaining / gapsRemaining;
while (itemsRemaining > 0)
{
if (itemsRemaining / (gapsRemaining - i) >= limit)
{ output[{i}th_gap] = next_item_of_type(T)
gapsRemaining--;
itemsRemaining--;
}
else
i++;
}
}
{i} th_gap从零开始,就像数组索引一样。
如果你能在恒定时间内完成{i} th_gap(可以通过使用另一个计数器来完成),那么算法是线性时间,即O(大小) O(大小* numTypes)。
对于您的示例,它提供输出a b a b a a b a
。
修改强>
重新思考:如果你只维护每种类型的计数,它就不会那么复杂。
使用JS代码(http://js.do/code/96801)
var numItems = [5,3]; // for AAAAABBB
var numItems = [6,3,5]; // for AAAAAABBBCCCCC
var totalNumItems = 0;
for (i=0; i<numItems.length; i++)
totalNumItems += numItems[i];
var limits = [];
for (i=0; i<numItems.length; i++)
limits[i] = numItems[i] / totalNumItems;
var numGaps = totalNumItems;
var output = [];
for (i=0; i<totalNumItems; i++)
{ var bestValue = 0;
var bestType;
for (j=0; j<numItems.length; j++)
{ var value = numItems[j] / numGaps - limits[j];
if (value >= bestValue)
{ bestValue = value;
bestType = j;
} }
output[i] = bestType;
numItems[bestType]--;
numGaps--;
}
for (i=0; i<totalNumItems; i++)
document.writeln(output[i]);
document.writeln("<br>");
但正如@Jim所说,它是O(n * k),其中n是totalNumItems
,k是numItems.length
。所以他的O(n log k)解决方案具有更好的复杂性。
修改2
调整以更好地打破关系,因此更喜欢更频繁的项目。 [10,1,1]的先前代码输出为caaabaaaaaaa
,现为abaaaaacaaaa
。
var numItems = [10,1,1];
var totalNumItems = 0;
for (i=0; i<numItems.length; i++)
totalNumItems += numItems[i];
var limits = [];
for (i=0; i<numItems.length; i++)
limits[i] = numItems[i] / totalNumItems;
var numGaps = totalNumItems;
var output = [];
for (i=0; i<totalNumItems; i++)
{ var bestValue = 0;
var bestNumItems = 0;
var bestType;
for (j=0; j<numItems.length; j++)
{ var value = numItems[j] / numGaps - limits[j];
if (value >= bestValue && numItems[j] > bestNumItems)
{ bestValue = value;
bestNumItems = numItems[j];
bestType = j;
} }
output[i] = bestType;
numItems[bestType]--;
numGaps--;
}
for (i=0; i<totalNumItems; i++)
document.writeln(output[i]);
document.writeln("<br>");
答案 2 :(得分:1)
假设你有A1,A2,......,An和B1,B2,...,Bm。
如果n> m,那么将至少播放2个A项(如果播放列表是循环的[不断重复全部])。
您应该首先将A项放在一个循环上。然后在每两个连续的A项之间放置一个B项。这将分隔下一个到下一个A项。然后放下剩余的B项,如果有剩余的话。
如果你想确保第一个和最后一个项目都不是A,那么在开头放置一个A项目,如果有足够的B项目,则在最后放置一个B项目。
作为一种计算算法,为每个A项指定数字(双重类型以允许它们为有理数)并将这些数字从最小到最大排序。然后为每个B项分配连续A项的平均值。例如:
A1 = 3 A2 = 5 A3 = 10
ArrayA(0)= 3
ArrayA(1)= 5
ArrayA(2)= 10
然后假设你有4个B项。
On Error Resume Next
For n=0 to 3
ArrayB(n)=(ArrayA(n)+ArrayA(n+1))/2
Loop
这个循环将尝试调用ArrayA(3)并给出错误,我们将跳过错误恢复接下来。然后,您可以将随机数分配给未分配的B项。
最后,组合两个数组,对它们进行排序。你会得到排序的数字。按这些数字,以最佳排序的方式回拨物品。
答案 3 :(得分:1)
这个问题似乎并不容易,因为组合的数量很大。如果我是对的,对于三种类型的Na
,Nb
和Nc
个视频,有(Na+Nb+Nc-1)!/Na!Nb!Nc!
种可能性。 (分子中的-1
来自于彼此的循环排列的序列被认为是相同的事实。)
对组合结构没有清楚的了解,我会尝试如下:
例如
A1, B1, A2, B2, A3, B3, A4, A5
给出
2+2+2+2+2+4+1+1 = 16
和
A1, B1, A2, A3, B2, A4, B4, A5
给出
2+3+1+2+2+2+3+1 = 16
(这可能是一个效率不高的指标,短距离应该更加严厉。)
对于小N
,可以进行详尽的试验。
<强>更新强>:
很明显,我建议的简单指标给出了一个恒定的值!
答案 4 :(得分:0)
划分最大的视频数组,并在分割点插入其他元素。
视频类型A :( An = 5)[A1,A2,A3,A4,A5]
视频类型B:(Bn = 3)[B1,B2,B3]
1. Choose the Video type having maximum number of instances, in this
case A.
2. Divide: (An=5)[A1, A2, A3, A4, A5] / 2 = 2, (An=2)[A1, A2](An=3)[A3, A4, A5]
3. Now insert one instance of B at the point of division as per step 1,
i.e (An=2)[A1, A2](Bn=1)[B1](An=3)(A3, A4, A5)
4. Now repeat step 2, 3 with (An=2)[A1, A2] and (An=3)[A3, A4, A5] and so forth like we do in binary search.
Final arrangement: (An=1)[A1](Bn=1)[B2](An=1)[A2](Bn=1)[B1](An=2)[A3, A4](Bn=1)[B3](An=1)[A5]
答案 5 :(得分:0)
以下是Java程序,用于排列数组,以使两个相邻的数字都不相同。
public int[] rearrangeArray(int[] arr) {
int n = arr.length;
// Store frequencies of all elements
// of the array
int[] count = new int[1000];
int[] visited = new int[1000];
for (int i = 0; i < n; i++)
count[arr[i]]++;
// Insert all characters with their frequencies
// into a priority_queue
PriorityQueue<RandomKey> pq = new PriorityQueue<>(11, new KeyComparator());
// Adding high freq elements in descending order
for (int i = 0; i < n; i++) {
int val = arr[i];
if (count[val] > 0 && visited[val] != 1)
pq.add(new RandomKey(count[val], val));
visited[val] = 1;
}
// 'result[]' that will store resultant value
int[] result = new int[n];
// work as the previous visited element
// initial previous element will be ( '-1' and
// it's frequency will also be '-1' )
RandomKey prev = new RandomKey(-1, -1);
// Traverse queue
int l = 0;
while (pq.size() != 0) {
// pop top element from queue and add it
// to result
RandomKey k = pq.peek();
pq.poll();
result[l] = k.num;
// If frequency of previous element is less
// than zero that means it is useless, we
// need not to push it
if (prev.freq > 0)
pq.add(prev);
// make current element as the previous
// decrease frequency by 'one'
(k.freq)--;
prev = k;
l++;
}
return result;
}
public class RandomKey {
int freq;
int num;
RandomKey(int freq, int num) {
this.freq = freq;
this.num = num;
}
}
class KeyComparator implements Comparator<RandomKey> {
// Overriding compare()method of Comparator
public int compare(RandomKey k1, RandomKey k2) {
if (k1.freq < k2.freq)
return 1;
else if (k1.freq > k2.freq)
return -1;
return 0;
}
}