算法最小化播放列表而不改变播出

时间:2010-11-28 21:16:12

标签: algorithm list reduce

我正在寻找一种算法来减少有序但不唯一的项目的列表(播放列表)。 搜索集合理论但尚未发现任何合适的东西

实施例

[a, b, b, c] -> [a, b, b, c] Cannot be reduced. 
[a, b, b, a, b, b] -> [a, b, b]. 
[b, b, b, b, b] -> [b]. 
[b, b, b, b, a] -> [b, b, b, b, a] Cannot be reduced. 

考虑获取所有现有的子列表并计算每个实例。 如果存在子列表长度等于原始列表的计数次数的子列表,请选择符合此条件的最短子列表。

这似乎有点蛮力,必须有更简单/更快的解决方案。

4 个答案:

答案 0 :(得分:3)

对于初学者,您不需要检查所有子列表 - 只需要那些长度为 factor 的完整列表长度的子列表。

如果你主要关注的是简单编码而不是原始速度,那么让正则表达式引擎解决问题:

/^(.+?)\1+$/

Abigail's awesome Perl regex to find prime numbers上的变体。

答案 1 :(得分:2)

对于每个n <= N(其中N是列表的长度),如果nN的因子。如果是,则检查重复第一个n字符的子列表是否生成原始列表。如果确实如此,那么你找到了一个可能的答案(答案是最短的)。这应该会降低到O(N^2)以下,但在最坏的情况下仍然与蛮力相同。

你可以做一些修剪,注意如果例如长度为2的子列表成功生成前4个字符而不是完整列表,则长度为4的子列表将失败。你可以将所有这些子列表长度的列表保存到 not 检查,这将减少一些计算。

答案 2 :(得分:0)

使用素数对每个元素进行编码。

例如:

a -> 2
b -> 3
c -> 5

现在,您需要再维护两个列表。

第一个列表是素数,第二个列表是指数。

这个想法是;当你偶然发现一个元素时,记录它的素数和它连续出现的次数。

对于[a, b, b, c],你得到了这个:

[2, 3, 3, 5]

可记录为:

[2, 3^2, 5]

或更准确地说:

[2^1, 3^2, 5^1]

并保留两个列表:

[2,3,5]  // primes in succession - list [p]
[1,2,1]  // exponents - list [e]

现在,通过检查第一个元素[p] ^ [e]是否与最后一个元素相同,从末尾到中间迭代这两个列表;如果是,那么倒数第二,依此类推......如果所有这些都相同,你的清单就可以减少。

在此示例中,您检查2^1*5^1 == 3^2*3^2;因为它不是,所以不能减少。

让我们试试[a, b, b, a, b, b]

编码为

[2^1, 3^2, 2^1, 3^2]

,或者

[2, 3, 2, 3]  // primes
[1, 2, 1, 2]  // exponents

现在,我们检查2^1 * 3^2 == 3^2 * 2^1(第一个素数,第一个指数乘以最后一个素数,最后一个指数,然后与倒数第二个相比)

由于这一点,它可以减少。

让我们试试[b, b, b, b, b]

这可以编码为

[3^5]

,或者

[3]  // primes
[5]  // exponents

这是一种特殊情况:如果您有1个元素列表,那么您的原始列表可以减少。

让我们试试[b, b, b, b, a]

这可以编码为

[3^4, 2^1]

,或者

[3, 2]  // primes
[4, 1]  // exponents

我们检查是否3^4 == 2^1,因为它不是,所以您的列表不可缩减。

让我们试试[a, b, a, b, a, b]

这可以编码为

[2^1, 3^1, 2^1, 3^1, 2^1, 3^1]

,或者

[2, 3, 2, 3, 2, 3]
[1, 1, 1, 1, 1, 1]

尝试上述过程有效,因为2^1 * 3^1 == 3^1 * 2^1 == 2^1 * 3^1

所以,算法将是这样的:


将所有数字编码为素数。

遍历您的列表,制作两个列表并按照描述填充它们

既然您有两个列表pe,它们都有n长度:

var start = p[0]^e[0] * p[n-1]^e[n-1]
var reducible = true;

for (int i = 0; i < n/2, ++i) :
   if ( (p[i]^e[i] * p[n-i]^e[n-i]) != start ) :
       reducible = false;
       break;

注意:我没有真正编写此算法的代码,并尝试将其用于各种输入。这只是一个想法。 此外,如果列表可以从n的长度和长度缩小,那么查看如何将原始列表缩减为基本格式应该不会太难。

第二个注意事项:如果有人发现上述错误,请纠正我。有可能这一切都没有用,因为它已经很晚了,而且我的注意力并不是最佳的。

答案 3 :(得分:0)

这里有一些简单的代码应该在接近线性的时间内运行(最差的是O(n lg lg n)我认为,依赖于更高的数学运算)。

f(x) {
  i = 1;
  while (i <= size(x) / 2) {
    if (size(x) % i != 0) { i++; continue;}
    b = true;
    for (j = 0; j + i < x.size(); j++) {
      if (x[i] != x[j]) {
        b = false;
        break;
      }
    }
    if (b) return i;
    i = max(i + 1, j / i * i / 2); // skip some values of i if j is large enough
  }
  return -1;
}

基本上,上面执行了朴素算法,但是跳过了一些周期性因素,这些周期性因为早期的“近乎未命中”而已知是不可能的。例如,如果您尝试5周期并看到“aaaabaaaabaaaabaaaabab”,您可以安全地跳过6,7,...,10,因为我们看到4个循环的5次重复然后失败。

最终,你最终做了线性工作量加上一些线性的sigma(n)工作量,n的除数之和,以O(n lg lg n)为界。

*请注意,证明此跳过的正确性非常微妙,我可能在细节上犯了错误 - 欢迎评论。