如何检查一个范围内的值是否是另一个范围内的值的倍数?

How to check if a value within a range is multiple of a value from another range?

假设我有一个系统将 8820 个值分配为 96 个值,使用 Banker's Round 进行舍入(称它们为 pulse)。公式为:

pulse = BankerRound(8820 * i/96), with i[0,96[

因此,这是脉冲列表:

0
92
184
276
368
459
551
643
735
827
919
1011
1102
1194
1286
1378
1470
1562
1654
1746
1838
1929
2021
2113
2205
2297
2389
2481
2572
2664
2756
2848
2940
3032
3124
3216
3308
3399
3491
3583
3675
3767
3859
3951
4042
4134
4226
4318
4410
4502
4594
4686
4778
4869
4961
5053
5145
5237
5329
5421
5512
5604
5696
5788
5880
5972
6064
6156
6248
6339
6431
6523
6615
6707
6799
6891
6982
7074
7166
7258
7350
7442
7534
7626
7718
7809
7901
7993
8085
8177
8269
8361
8452
8544
8636
8728

现在,假设系统不直接向我发送这些脉冲。相反,它在第 8820 次发送这些脉冲(称它们为 tick):

tick = value * 1/8820

我得到的价格变动列表变成:

0
0.010430839
0.020861678
0.031292517
0.041723356
0.052040816
0.062471655
0.072902494
0.083333333
0.093764172
0.104195011
0.11462585
0.124943311
0.13537415
0.145804989
0.156235828
0.166666667
0.177097506
0.187528345
0.197959184
0.208390023
0.218707483
0.229138322
0.239569161
0.25
0.260430839
0.270861678
0.281292517
0.291609977
0.302040816
0.312471655
0.322902494
0.333333333
0.343764172
0.354195011
0.36462585
0.375056689
0.38537415
0.395804989
0.406235828
0.416666667
0.427097506
0.437528345
0.447959184
0.458276644
0.468707483
0.479138322
0.489569161
0.5
0.510430839
0.520861678
0.531292517
0.541723356
0.552040816
0.562471655
0.572902494
0.583333333
0.593764172
0.604195011
0.61462585
0.624943311
0.63537415
0.645804989
0.656235828
0.666666667
0.677097506
0.687528345
0.697959184
0.708390023
0.718707483
0.729138322
0.739569161
0.75
0.760430839
0.770861678
0.781292517
0.791609977
0.802040816
0.812471655
0.822902494
0.833333333
0.843764172
0.854195011
0.86462585
0.875056689
0.88537415
0.895804989
0.906235828
0.916666667
0.927097506
0.937528345
0.947959184
0.958276644
0.968707483
0.979138322
0.989569161

不幸的是,在这些报价之间它也发送给我 fake ticks,它们不是原始 pulses 的乘积。例如 0,029024943,它是 256 的乘积,它不在脉冲列表中。

我如何从这个列表中找到哪些报价是 valid 哪些是 fake? 我没有在过程中比较的脉冲列表,因为 8820 会在此期间发生变化,所以我没有逐步比较的列表。我需要从每次迭代的滴答声中推断出它。

最好的数学方法是什么?也许只在 tick 而不是 pulse 中推理。

我想找到最近的整数脉冲和 prev/next 刻度之间更接近的误差。 Here 在 C++ 中:

double pulse = tick * 96.;
double prevpulse = (tick - 1/8820.) * 96.;
double nextpulse = (tick + 1/8820.) * 96.;

int pulseRounded=round(pulse);
int buffer=lrint(tick * 8820.);

double pulseABS = abs(pulse - pulseRounded);
double prevpulseABS = abs(prevpulse - pulseRounded);
double nextpulseABS = abs(nextpulse - pulseRounded);

if (nextpulseABS > pulseABS && prevpulseABS > pulseABS) {
    // is pulse
} 

但是例如 tick 0.0417234 (pulse 368) 失败,因为上一个 tick 错误似乎比它更接近:prevpulseABS error (0.00543795) 更小比 pulseABS 错误 (0.0054464).

我猜这是因为这种比较不关心四舍五入。

我假设您使用 int i 作为循环变量在 for 循环中初始化您的脉冲;那么问题是这一行:

BankerRound(8820 * i/96);

8820 * i / 96 是一个全整数运算,结果再次为整数,去掉余数(因此实际上,总是已经向零四舍五入),而 BankerRound 实际上不再需要四舍五入了。试试这个:

BankerRound(8820 * i / 96.0);

如果您尝试计算上一个和下一个脉冲,同样的问题也适用,因为您实际上减去并添加了 0(同样,1/8820 都是整数,结果为 0)。

编辑:

根据我从评论中读到的内容,'system' 是不可修改的——正如我之前假设的那样。实际上,它以

n / 96.0, n &#x220a [0, 96) in ℕ
的形式计算报价 然而,包括某种表面上独立于采样频率的内部舍入,因此 n/96.0 的真实值存在一些差异,并且滴答乘以 96 并不能准确地提供 [0, 96) 中的整数值(感谢 KarstenKoop)。而且部分送样根本无效...

因此任务是检测 tick * 96 是否足够接近整数值以被接受为有效。

所以我们需要检查:

double value = tick * 96.0;
bool isValid
    =  value - floor(value) < threshold
    || ceil(value) - value  < threshold;

具有一些适当定义的阈值。假设这些值确实计算为

double tick = round(8820*i/96.0)/8820.0;

那么最大偏差将略大于 0.00544(请参阅下文了解更精确的值),0.006、0.0055、0.00545 等大小的阈值可能是一个选择。

四舍五入可能是内部使用的传感器值位数的问题(如果我们有 13 位可用,滴答实际上可以计算为 floor(8192 * i / 96.0) / 8192.0,其中 8192 为 1 << 13 &ndash 和 floor计算整数除法;只是猜测...)。

最大偏差的精确值,使用 8820 作为因子,与 double 表示的一样精确,是:
0.00544217687075132516838493756949901580810546875

乘以96其实是没有必要的,可以直接和threshold除以96比较,就是:
0.0000566893424036596371706764330156147480010986328125

玩弄 Excel,我认为您想乘以(应该是)整数而不是寻找最接近的脉冲。

Tick                Pulse                   i   Error                           OK
                Tick*8820       Pulse*96/8820   ABS( i - INT( i+0.05 ) )    Error < 0.01
------------    ------------    -------------   ------------------------    ------------
0.029024943     255.9999973     2.786394528     0.786394528                 FALSE
0.0417234       368.000388      4.0054464       0.0054464                   TRUE
0                 0             0               0                           TRUE
0.010430839      91.99999998    1.001360544     0.001360544                 TRUE
0.020861678     184             2.002721088     0.002721088                 TRUE
0.031292517     275.9999999     3.004081632     0.004081632                 TRUE
0.041723356     367.9999999     4.005442176     0.005442176                 TRUE
0.052040816     458.9999971     4.995918336     0.004081664                 TRUE
0.062471655     550.9999971     5.99727888      0.00272112                  TRUE
0.072902494     642.9999971     6.998639424     0.001360576                 TRUE
0.083333333     734.9999971     7.999999968     3.2E-08                     TRUE

table 显示了您的两个 "problem" 案例(真正的错误值,256,以及您的代码出错的一个,368),然后是前几个 "good" 值。

如果两个8820同时变化,那么很明显它们会相互抵消,i就是Tick*96.

Error项是计算出的i与最接近的整数之差;如果这小于 0.01,那么它是一个 "good" 值。

注意:0.050.01 值的选择有些随意(也就是根据数字启发第一次猜测):如果需要,请进行调整。虽然我只显示了前几行,但您提供的所有 96 个 "good" 值都显示为 TRUE。

代码(完全未经测试)类似于:

double pulse = tick * 8820.0 ;
double i = pulse * 96.0 / 8820.0 ;
double error = abs( i - floor( i + 0.05 ) ) ;
if( error < 0.05 ) {
    // is pulse
}

新 POST:

好的。根据我现在的理解,这是我修改后的答案。

您拥有构建良好价值列表所需的信息。每次切换到新曲目时:

vector<double> good_list;
good_list.reserve(96);
for(int i = 0; i < 96; i++)
    good_list.push_back(BankerRound(8820.0 * i / 96.0) / 8820.0);

然后,每次要验证输入时:

auto iter = find(good_list.begin(), good_list.end(), input);
if(iter != good_list.end()) //It's a match!
    cout << "Happy days! It's a match!" << endl;
else
    cout << "Oh bother. It's not a match." << endl;

BankerRound() 函数在数学上确定正确脉冲的问题是,您输入的值越高,该函数就会引入越来越大的误差。然后,您将需要一个公式的公式,而这已经超出了我的驾驶室。或者,您可以跟踪连续值之间的差异。他们中的大多数都是一样的。您只需检查两个可能的错误。但是,如果您可以跳曲目或在一个曲目中跳来跳去,那就不可行了。

旧 POST:

如果我对问题的理解正确,那么您获得的唯一信息应该以 (p/v = y) 的形式出现,其中您知道 'y'(这是列表中的每个元素您从设备获得的滴答数)并且您知道 'p' 是脉冲,'v' 是每拍值,但您不知道它们是什么。因此,从您的 post 中提取一个数据点,您可能会得到这样的等式:

p/v = 0.010430839

'v',在您迄今为止使用的所有示例中,是 8820,但据我了解,该值不是一个有保证的常量。下一个问题是:在开始获取所有这些十进制值之前,您是否有办法确定 'v' 是什么?如果这样做,您可以从数学上算出最小误差是多少 (1/v),然后将您的小数信息乘以 'v',四舍五入到最接近的整数,然后检查两者之间的差异它的四舍五入形式和非四舍五入形式落在你计算误差的范围内,如下所示:

double input; //let input be elements in your list of doubles, such as 0.010430839
double allowed_error = 1.0 / values_per_beat;
double proposed = input * values_per_beat;
double rounded = std::round(proposed);
if(abs(rounded - proposed) < allowed_error){cout << "It's good!" << endl;}

但是,如果您不能能够提前确定values_per_beat,那么这就变成了一个统计问题。您必须积累足够的数据样本,移除异常值(少数与常态不同的值)并使用该数据。但这种方法不会是实时的,考虑到您一直在使用的术语(每拍值、bpm、值 44100),听起来实时可能就是您所追求的。