我有一个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个流组成,而每个流将大约为千字节长。
答案 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查询中,我使用了这些运算符:
true
^ - 来自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()));
每一步都很好的懒惰评价 - 虽然吓人......