如何包装 last/first 元素进行建筑插值?
How to wrap last/first element making building interpolation?
我有这段代码可以迭代一些样本并在点之间建立一个简单的线性插值:
foreach sample:
base = floor(index_pointer)
frac = index_pointer - base
out = in[base] * (1 - frac) + in[base + 1] * frac
index_pointer += speed
// restart
if(index_pointer >= sample_length)
{
index_pointer = 0
}
使用"speed"等于1,游戏结束。但是如果 index_pointer
不同于 1(即有小数部分),我需要包装 last/first 元素以保持翻译一致。
你会怎么做?双索引?
这是我的价值观的一个例子。假设 in
4 个值的数组:[8, 12, 16, 20].
它将是:
1.0*in[0] + 0.0*in[1]=8
0.28*in[0] + 0.72*in[1]=10.88
0.56*in[1] + 0.44*in[2]=13.76
0.84*in[2] + 0.14*in[3]=16.64
0.12*in[2] + 0.88*in[3]=19.52
0.4*in[3] + 0.6*in[4]=8 // wrong; here I need to wrapper
最后一点是错误的。 [4] 将是 0,因为我没有 [4],但第一部分需要处理 0.4 和第一个样本的权重(我认为?)。
只需环绕索引:
out = in[base] * (1 - frac) + in[(base + 1) % N] * frac
,其中 %
是模运算符,N
是输入样本的数量。
此过程为您的样本数据生成以下行(虚线是插值样本点,圆圈是输入值):
我想我现在明白了这个问题(答案只有在我真的明白的情况下才适用...):
您以标称速度 sn 对值进行采样。但实际上您的采样器以实际速度 s 进行采样,其中 s != sn。现在,您想要创建一个函数来对系列进行重新采样,以速度 s 进行采样,因此它会产生一个系列,就好像它是通过 2 个相邻样本之间的线性插值以速度 sn 进行采样一样。或者,您的采样器抖动(在实际采样时有时间差异,即 sn + Noise(sn)
)。
这是我的方法 - 一个名为“re-sample”的函数。它获取样本数据和所需的重新采样点列表。
对于任何会在原始数据之外建立索引的重新采样点,它 returns 各自的边界值。
let resample (data : float array) times =
let N = Array.length data
let maxIndex = N-1
let weight (t : float) =
t - (floor t)
let interpolate x1 x2 w = x1 * (1.0 - w) + x2 * w
let interp t1 t2 w =
//printfn "t1 = %d t2 = %d w = %f" t1 t2 w
interpolate (data.[t1]) (data.[t2]) w
let inter t =
let t1 = int (floor t)
match t1 with
| x when x >= 0 && x < maxIndex ->
let t2 = t1 + 1
interp t1 t2 (weight t)
| x when x >= maxIndex -> data.[maxIndex]
| _ -> data.[0]
times
|> List.map (fun t -> t, inter t)
|> Array.ofList
let raw_data = [8; 12; 16; 20] |> List.map float |> Array.ofList
let resampled = resample raw_data [0.0..0.2..4.0]
并产生:
val resample : data:float array -> times:float list -> (float * float) []
val raw_data : float [] = [|8.0; 12.0; 16.0; 20.0|]
val resampled : (float * float) [] =
[|(0.0, 8.0); (0.2, 8.8); (0.4, 9.6); (0.6, 10.4); (0.8, 11.2); (1.0, 12.0);
(1.2, 12.8); (1.4, 13.6); (1.6, 14.4); (1.8, 15.2); (2.0, 16.0);
(2.2, 16.8); (2.4, 17.6); (2.6, 18.4); (2.8, 19.2); (3.0, 20.0);
(3.2, 20.0); (3.4, 20.0); (3.6, 20.0); (3.8, 20.0); (4.0, 20.0)|]
现在,我仍然无法理解您问题的“环绕”部分。最后,插值 - 与外推相反,仅针对 [0..N-1] 中的值定义。因此,由您决定函数是否应该产生 运行 时间错误,或者简单地使用边缘值(或 0)作为超出原始数据数组范围的时间值。
编辑
事实证明,它也是关于如何为此使用循环(环形)缓冲区的。
这里是 resample
函数的一个版本,使用循环缓冲区。以及一些操作。
update
将新样本值添加到环形缓冲区
read
读取环形缓冲区元素的内容,就好像它是一个普通数组,从 [0..N-1] 索引。
initXXX
以各种形式创建环形缓冲区的函数。
length
其中 returns 环形缓冲区的长度或容量。
环形缓冲区逻辑被分解到一个模块中,以保持一切清洁。
module Cyclic =
let wrap n x = x % n // % is modulo operator, just like in C/C++
type Series = { A : float array; WritePosition : int }
let init (n : int) =
{ A = Array.init n (fun i -> 0.);
WritePosition = 0
}
let initFromArray a =
let n = Array.length a
{ A = Array.copy a;
WritePosition = 0
}
let initUseArray a =
let n = Array.length a
{ A = a;
WritePosition = 0
}
let update (sample : float ) (series : Series) =
let wrapper = wrap (Array.length series.A)
series.A.[series.WritePosition] <- sample
{ series with
WritePosition = wrapper (series.WritePosition + 1) }
let read i series =
let n = Array.length series.A
let wrapper = wrap (Array.length series.A)
series.A.[wrapper (series.WritePosition + i)]
let length (series : Series) = Array.length (series.A)
let resampleSeries (data : Cyclic.Series) times =
let N = Cyclic.length data
let maxIndex = N-1
let weight (t : float) =
t - (floor t)
let interpolate x1 x2 w = x1 * (1.0 - w) + x2 * w
let interp t1 t2 w =
interpolate (Cyclic.read t1 data) (Cyclic.read t2 data) w
let inter t =
let t1 = int (floor t)
match t1 with
| x when x >= 0 && x < maxIndex ->
let t2 = t1 + 1
interp t1 t2 (weight t)
| x when x >= maxIndex -> Cyclic.read maxIndex data
| _ -> Cyclic.read 0 data
times
|> List.map (fun t -> t, inter t)
|> Array.ofList
let input = raw_data
let rawSeries0 = Cyclic.initFromArray input
(resampleSeries rawSeries0 [0.0..0.2..4.0]) = resampled
我有这段代码可以迭代一些样本并在点之间建立一个简单的线性插值:
foreach sample:
base = floor(index_pointer)
frac = index_pointer - base
out = in[base] * (1 - frac) + in[base + 1] * frac
index_pointer += speed
// restart
if(index_pointer >= sample_length)
{
index_pointer = 0
}
使用"speed"等于1,游戏结束。但是如果 index_pointer
不同于 1(即有小数部分),我需要包装 last/first 元素以保持翻译一致。
你会怎么做?双索引?
这是我的价值观的一个例子。假设 in
4 个值的数组:[8, 12, 16, 20].
它将是:
1.0*in[0] + 0.0*in[1]=8
0.28*in[0] + 0.72*in[1]=10.88
0.56*in[1] + 0.44*in[2]=13.76
0.84*in[2] + 0.14*in[3]=16.64
0.12*in[2] + 0.88*in[3]=19.52
0.4*in[3] + 0.6*in[4]=8 // wrong; here I need to wrapper
最后一点是错误的。 [4] 将是 0,因为我没有 [4],但第一部分需要处理 0.4 和第一个样本的权重(我认为?)。
只需环绕索引:
out = in[base] * (1 - frac) + in[(base + 1) % N] * frac
,其中 %
是模运算符,N
是输入样本的数量。
此过程为您的样本数据生成以下行(虚线是插值样本点,圆圈是输入值):
我想我现在明白了这个问题(答案只有在我真的明白的情况下才适用...):
您以标称速度 sn 对值进行采样。但实际上您的采样器以实际速度 s 进行采样,其中 s != sn。现在,您想要创建一个函数来对系列进行重新采样,以速度 s 进行采样,因此它会产生一个系列,就好像它是通过 2 个相邻样本之间的线性插值以速度 sn 进行采样一样。或者,您的采样器抖动(在实际采样时有时间差异,即 sn + Noise(sn)
)。
这是我的方法 - 一个名为“re-sample”的函数。它获取样本数据和所需的重新采样点列表。 对于任何会在原始数据之外建立索引的重新采样点,它 returns 各自的边界值。
let resample (data : float array) times =
let N = Array.length data
let maxIndex = N-1
let weight (t : float) =
t - (floor t)
let interpolate x1 x2 w = x1 * (1.0 - w) + x2 * w
let interp t1 t2 w =
//printfn "t1 = %d t2 = %d w = %f" t1 t2 w
interpolate (data.[t1]) (data.[t2]) w
let inter t =
let t1 = int (floor t)
match t1 with
| x when x >= 0 && x < maxIndex ->
let t2 = t1 + 1
interp t1 t2 (weight t)
| x when x >= maxIndex -> data.[maxIndex]
| _ -> data.[0]
times
|> List.map (fun t -> t, inter t)
|> Array.ofList
let raw_data = [8; 12; 16; 20] |> List.map float |> Array.ofList
let resampled = resample raw_data [0.0..0.2..4.0]
并产生:
val resample : data:float array -> times:float list -> (float * float) []
val raw_data : float [] = [|8.0; 12.0; 16.0; 20.0|]
val resampled : (float * float) [] =
[|(0.0, 8.0); (0.2, 8.8); (0.4, 9.6); (0.6, 10.4); (0.8, 11.2); (1.0, 12.0);
(1.2, 12.8); (1.4, 13.6); (1.6, 14.4); (1.8, 15.2); (2.0, 16.0);
(2.2, 16.8); (2.4, 17.6); (2.6, 18.4); (2.8, 19.2); (3.0, 20.0);
(3.2, 20.0); (3.4, 20.0); (3.6, 20.0); (3.8, 20.0); (4.0, 20.0)|]
现在,我仍然无法理解您问题的“环绕”部分。最后,插值 - 与外推相反,仅针对 [0..N-1] 中的值定义。因此,由您决定函数是否应该产生 运行 时间错误,或者简单地使用边缘值(或 0)作为超出原始数据数组范围的时间值。
编辑
事实证明,它也是关于如何为此使用循环(环形)缓冲区的。
这里是 resample
函数的一个版本,使用循环缓冲区。以及一些操作。
update
将新样本值添加到环形缓冲区read
读取环形缓冲区元素的内容,就好像它是一个普通数组,从 [0..N-1] 索引。initXXX
以各种形式创建环形缓冲区的函数。length
其中 returns 环形缓冲区的长度或容量。
环形缓冲区逻辑被分解到一个模块中,以保持一切清洁。
module Cyclic =
let wrap n x = x % n // % is modulo operator, just like in C/C++
type Series = { A : float array; WritePosition : int }
let init (n : int) =
{ A = Array.init n (fun i -> 0.);
WritePosition = 0
}
let initFromArray a =
let n = Array.length a
{ A = Array.copy a;
WritePosition = 0
}
let initUseArray a =
let n = Array.length a
{ A = a;
WritePosition = 0
}
let update (sample : float ) (series : Series) =
let wrapper = wrap (Array.length series.A)
series.A.[series.WritePosition] <- sample
{ series with
WritePosition = wrapper (series.WritePosition + 1) }
let read i series =
let n = Array.length series.A
let wrapper = wrap (Array.length series.A)
series.A.[wrapper (series.WritePosition + i)]
let length (series : Series) = Array.length (series.A)
let resampleSeries (data : Cyclic.Series) times =
let N = Cyclic.length data
let maxIndex = N-1
let weight (t : float) =
t - (floor t)
let interpolate x1 x2 w = x1 * (1.0 - w) + x2 * w
let interp t1 t2 w =
interpolate (Cyclic.read t1 data) (Cyclic.read t2 data) w
let inter t =
let t1 = int (floor t)
match t1 with
| x when x >= 0 && x < maxIndex ->
let t2 = t1 + 1
interp t1 t2 (weight t)
| x when x >= maxIndex -> Cyclic.read maxIndex data
| _ -> Cyclic.read 0 data
times
|> List.map (fun t -> t, inter t)
|> Array.ofList
let input = raw_data
let rawSeries0 = Cyclic.initFromArray input
(resampleSeries rawSeries0 [0.0..0.2..4.0]) = resampled