【问题标题】:Mapping a continuous range into discrete bins in C++将连续范围映射到 C++ 中的离散箱
【发布时间】:2021-02-22 12:06:50
【问题描述】:

我继承了一个函数的维护,该函数将065535(含)之间的值作为参数:

MyClass::mappingFunction(unsigned short headingIndex);

headingIndex 可以使用以下公式转换为度数:degrees = headingIndex * 360 / 65536

这个函数的作用是将headingIndex翻译成36个符号中的1个,代表不同的旋转度数,即有一个10度的符号,一个20度的符号等等,最多360度,以10为单位度数。

headingIndex 为 0 将转换为显示 0 (360) 度符号。

该函数执行以下我似乎无法理解的操作:

const int MAX_INTEGER = 65536;
const int NUM_SYMBOLS = 36;
int symbolRange = NUM_SYMBOLS - 1;
int roundAmount = MAX_INTEGER / (symbolRange + 1) - 1;
int roundedIndex = headingIndex + roundAmount;
int symbol = (symbolRange * roundedIndex) / MAX_INTEGER;

我对这里使用的算法感到困惑,特别是在以下方面:

  1. roundAmount 背后的意图?我知道它本质上是将最大输入范围划分为离散的块,但然后将其添加到 headingIndex 似乎是一件奇怪的事情。
  2. roundedIndex 那么原始值现在是偏移还是顺时针方向旋转了一些偏移量?

算法产生如下结果:

headingIndex of 0     --> symbol 0
headingIndex of 100   --> symbol 1
headingIndex of 65500 --> symbol 35

我在想一定有更好的方法来做到这一点?

【问题讨论】:

  • 请注意,symbolRangeroundAmmount 也是常量表达式。 roundAmount 可以简化为 (MAX_INTEGER / NUM_SYMBOLS) - 1。我不确定该值背后的原因是什么,但在我看来它是一个舍入因素。您添加到int 的因子,以便小数截断产生的值等于对原始值进行不同舍入所获得的值。例如,在整数算术中,(x + 9) / 10 在功能上是x / 10,同时向上取整而不是截断。我只是不确定roundAmount 的用途。
  • 尚不清楚 100 的 headingIndex 如何给出 1 的符号。根据您的第一个公式,这将是 100 * 360 / 65536 = ~0.549 度,符号索引为 0。
  • 除非我复制粘贴错误,否则 0-53 映射到 0,54-1925 映射到 1,1926-3798 映射到 2,以此类推。对我来说,这看起来像是一个有问题的映射,一个间隔只有 54 个元素,而其他间隔在 1872 和 1873 之间交替(而 655536/36 = 1820)。它似乎也与您的公式没有明显关系。
  • @molbdnilo 除非我们都复制错了! 0-53 给出 0,然后 54 给出 1。#confirmed。
  • @FrançoisAndrieux 我认为代码可能会混淆。它看起来像是某种“更圆的鞋面”。您添加以使截断四舍五入的东西。 (x+7)/8 截断向上取整!将该值添加到索引中,然后表达式(就像他们所做的那样)然后 (NUM_SYMBOLS*roundAmount)/MAX_INTEGER 将显示一些类似舍入的行为。但它们乘以(NUM_SYMBOLS-1),结果有点好笑,反正我们也不想要一个回合!!!

标签: c++ algorithm math range


【解决方案1】:

显示的代码看起来非常复杂(可能是防止整数溢出)。确定符号编号的更简单的方法是如下代码:

    symbol = (headingIndex * 36u) / 65536u;

然而,如果这个确实存在整数溢出问题,那么计算可以在double精度中完成,在四舍五入后将结果转换回int

    symbol = static_cast<int>( ((headindIndex * 36.0) / 65536.0) + 0.5 ); // Add 0.5 for rounding.

【讨论】:

  • 这给出了0 for headIndex=100,而不是1,所以它不等价。
  • @DavidEisenstat 我同意它不等效,但是恕我直言,它为065535 范围内的任何值提供了 正确 索引,如果那是 线性刻度,其中 65535 对应 360 度,符号 #35。
  • @FrançoisAndrieux 在具有 16 位 int 的平台上是否可能溢出?但我同意这会让const int MAX_INTEGER = 65536; 有点愚蠢!
  • @FrançoisAndrieux 我想我看错了你之前的评论,或者你错过了一个“不”?如:原代码确实没有有溢出的风险,...
  • @AdrianMole 编辑:没关系,我误解了headingIndex的意义。
【解决方案2】:

您有 65536 个可能的输入 (0..65535) 和 36 个输出 (0..35)。这意味着每个输出箱应该代表大约 1820 个输入,如果它们被均分的话。

上面的公式没有做到这一点。只有前 54 个值在 bin 0 中,然后它们在剩余的 35 个 bin 中均分 (MAX_INTEGER/symbolRange)。每箱约 1872 个。

为了说明这一点,求解符号为 1 的标题的最小值。1 * 65536 = 35 * (headingIndex + 1819) 所以 headingIndex == 53。

【讨论】:

  • 我想对此表示赞同。你说的是对的。但你还没有提供答案,所以我不确定我是否应该提供。
  • 不知道公式有没有错。奇数映射可能是系统要求。我只是在解释这个函数的作用,因为直到我写这篇文章时,cmets 都假设了其他东西。
  • 我也不知道是不是错了。这就是为什么我有点想赞成你的答案。塞进去。如果有人想抱怨“规则”,那就让他们来吧。
【解决方案3】:

如果您想保持输出相同但整理一下。走开。 该方法有一些奇怪的功能,可能是也可能不是我们想要的。

headingIndex 的范围为 0 - 53,symbol 的值为 0。这是一个包含 54 个值的桶(AKA bin)。 63717 - 65535 的范围是 35。那个桶是 1819 个值。 所有其他存储桶都是 1872 或 1873 值,所以看起来“很大”。

我们不能有相同大小的存储桶,因为值的数量是 65536,而 65536/36 是 1820,余数是 16。 所以我们需要把16个埋在桶里。它们的大小必须不均匀。

注意常量MAX_INTEGER 是一个红鲱鱼。最大值为 65535。65536 是范围。选择的名称从一开始就具有误导性。

为什么:

int symbolRange = NUM_SYMBOLS - 1;
int roundAmount = MAX_INTEGER / (symbolRange + 1) - 1;

当第二行可能是int roundAmount = MAX_INTEGER / MAX_SYMBOLS - 1;

我所说的只是看起来没有经过深思熟虑。但外表可能具有欺骗性。

让我感到困扰的是其他答案中提出的“明显”方法效果很好!

int symbol=(NUM_SYMBOLS*headingIndex)/(MAX_INTEGER);

为我们提供均匀分布的 1820 或 1821 桶。我会说这是头脑问题的自然解决方案。

那么为什么是当前的方法呢?它是某些测量设备的人工制品吗? 我会把钱的最大值设置为 65535,因为这是一个无符号 16 位整数的最大值。

想知道溢出是正确的。但如果你在 16 位工作,它已经坏了。所以我想知道一个记录 16 位的设备。这很现实。

这类似于我所知的“分期付款问题”。 我们希望客户在 36 个月内支付 655.36 英镑。他们是否每月支付 18.20 英镑,总计 655.20 英镑,而我们忘记了 16 便士?他们不会支付 18.21 英镑(总计 655.56 英镑)并多付 20 便士。更大的首付款 18.36 英镑,然后是 18.20 英镑的 35? 人们与这个搏斗。企业的答案是“拿到钱”——更大的首付款。如果他们欠你钱(最后一笔大款)而忘记了便士(都一样 - 我们比几便士还大!),请避免投诉。

在测量(例如度数)的算术术语中,我会说所提供的撒播方法是最自然、最均匀的,并且可以均匀地分布异常。

但这不是唯一的答案。由你决定。提示:如果您没有被要求解决这个问题并且只是认为它很丑 - 走开。现在走开。

【讨论】:

    猜你喜欢
    • 2017-08-31
    • 1970-01-01
    • 2021-11-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-03
    • 1970-01-01
    相关资源
    最近更新 更多