分段组成平均流

时间:2011-09-26 18:04:13

标签: algorithm

我有一个n浮点流列表,每个浮点流都有不同的大小 可以使用以下规则将流组合在一起:
您可以在任何时间点开始播放流(在开始之前为零)。您可以使用相同的流几次(它可以重叠,甚至可以在相同位置重复几次),并且您可以根本不使用某个流。
例如: -
输入流:

1 2 3 4
2 4 5 6 7
1 5 6

可以像:

组成
  1 2 3 4
1 5 6
        1 5 6

在展示位置之后,输出流由每个输出浮点数等于每个项的平方和的平方根的规则组成。
例如: -
如果某个位置的流是:

1
2
3

输出结果为:

sqrt(1*1 + 2*2 + 3*3) = sqrt(14) = 3.74...

所以对于示例组合:

  1 2 3 4
1 5 6
        1 5 6

输出结果为:

1 5.09 6.32 3 4.12 5 6

我所拥有的是输出流和输入流。我需要计算导致该输出的组合。确切的成分不一定存在 - 我需要尽可能接近输出的成分(最小的累积差异) 例如: -
输入:
流模仿:

1 5.09 6.32 3 4.12 5 6

和一个清单:

1 2 3 4
2 4 5 6 7
1 5 6

预期产出:

Stream 0 starting at 1,
Stream 2 starting at 0,
Stream 2 starting at 4.

这似乎是一个NP问题,有什么快速的方法可以解决这个问题吗?它可能有点蛮力(但不完全,它不是理论上的问题),只要它足够接近它就不会给出最好的答案。

该算法通常与流一起用于模拟非常长的长度(可能是几兆字节),而它将有大约20个流组成,而每个流将大约为千字节长。

2 个答案:

答案 0 :(得分:1)

我认为你可以加快贪婪搜索的速度。首先,对所涉及的所有流中的每个元素进行平方。然后你正在寻找一个平方流的总和,看起来很像平方目标流。假设“它看起来像”是平方流之间的欧氏距离,被视为向量。

然后我们有(a-b)^ 2 = a ^ 2 + b ^ 2 - 2a.b.因此,如果我们能够快速找到两个向量的点积,并且我们知道它们的绝对大小,我们就可以快速找到距离。但是使用FFT和http://en.wikipedia.org/wiki/Convolution_theorem,我们可以得出a.b_i,其中a是目标流,b_i是i的某个偏移处的流b,通过使用FFT来卷积反转版本的b - 用于在a上进行FFT的成本,在反向b上进行FFT,对结果进行FFT,我们得到每个偏移i的a.b_i。

如果我们进行贪婪搜索,第一步是找到使(a-b_i)^ 2最小的b_i并从a中减去它。然后我们正在寻找一个流c_j,它使(a-b_i-c_j)^ 2尽可能小。但这是一个^ 2 + b_i ^ 2 + c_j ^ 2 - 2a.b_i - 2a.c_j + 2b_i.c_j,我们已经计算了上述步骤中除b_i.c_j之外的所有内容。如果b和c是较短的流,计算b_i.c_j会很便宜,我们可以像以前一样使用FFT。

所以我们有一个不太可怕的方法来做一个贪婪的搜索 - 在每个阶段从调整后的目标流中减去流,到目前为止使残差最小(被认为是欧几里德空间中的向量),并从那里继续。在某个阶段,我们会发现没有任何可用的流使残差变得更小。我们可以在那里停下来,因为上面的计算表明我们一次使用两个流也无济于事 - 这是因为b_i.c_j> = 0,因为b_i的每个元素都是> = 0,因为它是一个方。

如果您进行贪婪的搜索并且不满意,但有更多的cpu需要刻录,请尝试使用有限差异搜索。

答案 1 :(得分:0)

如果我可以使用C#,LINQ& Rx框架的System.Interactive扩展然后工作:

首先 - 为允许的数组定义一个锯齿状数组。

int[][] streams =
    new []
    {
        new [] { 1, 2, 3, 4, },
        new [] { 2, 4, 5, 6, 7, },
        new [] { 1, 5, 6, },
    };

需要在整数上使用无限迭代器来表示每个步骤。

IEnumerable<int> steps =
    EnumerableEx.Generate(0, x => true, x => x + 1, x => x);

需要随机数生成器随机选择要添加到每个步骤的流。

var rnd = new Random();

在我的LINQ查询中,我使用了这些运算符:

  • 扫描^ - 在生成序列的序列上运行累加器函数 每个输入值的输出值
  • 其中 - 根据谓词过滤序列
  • 空 - 返回空序列
  • Concat - 连接两个序列
  • 跳过 - 跳过序列中指定数量的元素
  • Any - 如果序列包含任何元素,则返回true
  • 选择 - 使用选择器功能
  • 投射序列
  • Sum - 对序列中的值进行求和

^ - 来自Rx System.Interactive库

现在为LINQ查询做了所有艰苦的工作。

IEnumerable<double> results =
    steps
        // Randomly select which streams to add to this step
        .Scan(Enumerable.Empty<IEnumerable<int>>(), (xs, _) =>
            streams.Where(st => rnd.NextDouble() > 0.8).ToArray())
        // Create a list of "Heads" & "Tails" for each step
        // Heads are the first elements of the current streams in the step
        // Tails are the remaining elements to push forward to the next step
        .Scan(new
        {
            Heads = Enumerable.Empty<int>(),
            Tails = Enumerable.Empty<IEnumerable<int>>()
        }, (acc, ss) => new
        {
            Heads = acc.Tails.Concat(ss)
                .Select(s => s.First()),
            Tails = acc.Tails.Concat(ss)
                .Select(s => s.Skip(1)).Where(s => s.Any()),
        })
        // Keep the Heads only
        .Select(x => x.Heads)
        // Filter out any steps that didn't produce any values
        .Where(x => x.Any())
        // Calculate the square root of the sum of the squares
        .Select(x => System.Math.Sqrt((double)x.Select(y => y * y).Sum()));

每一步都很好的懒惰评价 - 虽然吓人......