【问题标题】:Composing an average stream piecewise分段组成平均流
【发布时间】:2011-09-26 18:04:12
【问题描述】:

我有一个 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 个流组成,而每个流的长度约为千字节。

【问题讨论】:

  • 您说“每个项的 cube 之和的平方根”,但您的等式显示“square 之和的平方根每个术语”——它是什么?
  • 这件事让我想到“在频域中解决它”,尽管没有想到具体的解决方案。您正在处理的是这些音频数据吗?
  • @j_random_hacker: 这是正方形,我的错
  • @AShelly:是的。我需要用非常有限的一组声音组成一个音乐文件。
  • 此外,当您同时播放两个声音时,振幅会加在一起,因此您应该寻找一种声音的排列方式,使它们 总和 到(几乎)所需的每个点的振幅,而不是你此刻正在做的事情,这看起来像是欧几里得距离的奇怪变体。

标签: algorithm


【解决方案1】:

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

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

如果我们进行贪婪搜索,第一步将是找到使 (a-b_i)^2 最小的 b_i 并将其从 a 中减去。然后我们正在寻找使 (a-b_i-c_j)^2 尽可能小的流 c_j。但这是 a^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 需要消耗,请尝试有限差异搜索。

【讨论】:

  • 我不太擅长数学这件事……你能提供伪代码或实现吗?
  • 我没有可以执行此操作的代码,而且我刚刚结束了一天的编写和调试代码,所以我不打算坐下来为其他人编写代码人们。您可能会在 r-project.org 的时间序列库或 Matlab 信号处理工具包中找到可以使用的东西,但我不能全心全意地建议任何人在不了解其内部情况的情况下使用这些东西,特别是如果有任何疑问的话他们所要求的就是他们想要的。
  • 这里有一些很棒的想法,对所有内容进行平方显然是一个很好的起点,使用 FFT 计算所有移位的内积很聪明。不过,没有遵循您对 (a-b_i-c_j)^2 的扩展——我只会计算 a (目标输出序列)的更新版本并重新开始。可能在未来的迭代中可以避免一些计算,因为 FFT 是线性的,因此您只需从 FFT(a) 中减去 FFT(b_i) 即可计算 FFT(a-b_i)。
  • OP 说目标流是兆字节长,次要流大约是千字节长。因此,对于百万个相对偏移中的几千个,b_i.c_j 为零,并且可以以大约 3 2048 点 FFT 为代价来计算。如果对兆字节长度的目标流使用现有的 FFT,则需要在逐点乘法后进行一百万点的逆 FFT。但是,如果我们没有正确的目标,这一切都是学术性的。例如,如果这是合理的,我们不是将流乘以某个参数来改变音量吗?
  • 看看你的意思,但是当你正在寻找第 k 个要添加的流时,你将在 (...)^2 中添加一个术语,这会导致 k 更多这些较短的输入-to-input 需要计算的内积,所以我不希望看到收益,除非很少的输入流足以覆盖输出。我同意 OP 的应用程序的问题表述被打破了,但这仍然是一个有趣的问题:)
【解决方案2】:

如果我可以使用 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 查询中,我使用了这些运算符:

  • Scan^ - 在一个序列上运行一个累加器函数,产生一个 每个输入值的输出值
  • Where - 根据谓词过滤序列
  • 空 - 返回一个空序列
  • 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()));

每一步都不错的懒惰评估 - 虽然很可怕......

【讨论】:

  • 从这段代码中我无法理解你的想法是什么,有什么了不起的部分,最好描述你的想法,然后展示你的代码。
  • @SaeedAmiri - 稍后我会尝试评论它。
  • 随机部分让我觉得是蛮力
  • @Dani - 随机部分只是从streams 锯齿状数组中选择一个样本。这也不是蛮力。只使用迭代器。
  • @Dani - 我已经为我的算法添加了解释。
猜你喜欢
  • 2016-11-29
  • 1970-01-01
  • 2021-09-19
  • 2021-05-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-05
  • 1970-01-01
相关资源
最近更新 更多