你如何将一组数字转换成大部分数字?

时间:2014-05-28 05:38:01

标签: c++ opengl floating-point double discrete-mathematics

少量背景:我正在开发一个转换器,它在XML输出的地图制作工具(Tiled)与输入lua表的引擎(Angel2D)之间架起桥梁。大部分都是直截了当的

然而,Tiled输出像素偏移(绝对值的整数),而Angel2D输入OpenGL单位(相对值的浮点数);需要这两者之间的转换因子(例如,32px = 1gu)。由于OpenGL单元是抽象的,并且如果对象太小或太大,相机可以放大或缩小,实际的转换因子并不重要;我可以使用随机数,用户只需放大或缩小。

但最好是选择转换因子使得输出的大多数数字都是小的和整数(或小整数的一小部分),因为这样可以更容易使用(并且OpenGL单元的整个点是他们很容易合作。)

如何可靠地找到这样的转换因子?

我的第一次尝试是使用给出的最小数字;这导致没有低于1的分数,但往往导致很多小数位,其中因素没有排列。

然后我尝试了序列的模式,这可能导致最大数量的1,但通常会导致背景图像的浮点很长。

我目前的方法获得整个序列的GCD,当它工作时,效果很好,但很容易被一个坏苹果抛弃。

请注意,虽然我可以很容易地传递给定的数字,或者选择一些固定因子,或者使用我上面指定的转换之一,但我正在寻找一种方法可靠地将此整数列表缩小为小,整数或简单分数,因为这对最终用户来说很可能不足为奇;这不是一次性转换。

最终用户倾向于使用1.0作为他们的基础"对于操作(因为它简单明了),所以实体的大小更有意义围绕它进行聚类。

5 个答案:

答案 0 :(得分:2)

“最大数字”是多少百分之几的因素'。 所以GCD是最大的数字,是100%的因子。的价值观。 你可以选择最大的数字,这个数字是60%的值。我不知道它是否是一个技术术语,但如果不是一个精确的GCD,它就是一种粗糙的GCD。

您可能需要执行跟踪和错误才能找到它(可能是二进制搜索)。但你也可以考虑采样。即如果您有一百万个数据点,只需随机选择100或1000即可找到一个数字,该数字均匀分配到您的样本集的目标百分比,这可能已经足够了。

一些蹩脚的伪C。

/** return percent of values in sampleset for which x is a factor */
double percentIsFactorOf(x, sampleset) {
  int factorCount = 0;

  for (sample : sampleset) 
     if (sample%x == 0) factorCount++;

  return (double)factorCount/sampleset.size;    
}

/** find largest value which is a factor of goalPercentage of sampleset */
double findGoodEnoughCommonFactor(sampleset, goalPercentage) {
   // slow n^2 alogrithm here - add binary search, sampling, or something smarter to improve if you like
   int start = max(sampleset);
   while (percentIsFactorOf(start, sampleset)< goalPercent)
     start--;
}

答案 1 :(得分:2)

我不知道你在和#34; GL单位&#34;。

谈论什么

在最抽象的层面上,GL没有单位。顶点坐标最初位于对象空间中,并在最终生成具有熟悉单位(像素)的坐标(窗口空间)之前经过六个用户定义的变换。

你绝对正确,即使在窗口空间,坐标仍然不是整数。实际上你不会想要这个,或者三角形会跳到整个地方,如果它们的顶点位置被捕捉到整数像素坐标,通常就不会像三角形一样。

相反,GL会将子像素精度投入到混合中。坐标最终仍然最终被量化为整数值,但是给定8位子像素精度,每个整数可以覆盖像素的1/256 th 。如您所见,像素覆盖测试在子像素级别完成:

http://i.msdn.microsoft.com/dynimg/IC520311.png

GL从不试图像你正在讨论的那样找到任何转换因子,它只是将像素坐标的数字空间拆分为积分和分数之间的固定除法...... 定点 换句话说。你可能会考虑做同样的事情。

答案 2 :(得分:2)

如果您的输入是N ^ 2(场上的二维空间是自然数,即非负整数),则需要输出到R ^ 2(实数域上的二维空间,其中在这种情况下,将用浮点数表示/近似。

忘记缩放一分钟,让输出与输入的比例相同。第一步是认识到输入坐标&lt; 0,0&gt;不表示&lt; 0,0&gt;在输出中,它表示像素的中心<0.5f,0.5f>。类似地,输入&lt; 2,3&gt;变为&lt; 2.5,3.5&gt;。通常,转换可以像这样执行:

float x_prime = (float)x + 0.5f;
float y_prime = (float)y + 0.5f;

接下来,您可能想要选择缩放因子,如您所述。我总是发现选择一些真实世界的单位很有用,通常是米。通过这种方式,您可以推断出您正在尝试建模的其他物理方面,因为它们具有单位;即速度,加速度,现在可以以米/秒为单位,或以每秒平方米为单位。你正在做多少米高或宽?一个像素多少米?选择有意义的东西,然后你的公式就变成了这个:

float x_prime = ((float)x + 0.5f) * (float)units_per_pixel;
float y_prime = ((float)y + 0.5f) * (float)units_per_pixel;

您可能不希望所有输出坐标都在正象限中;也就是说,您可能希望原点位于对象的中心。如果这样做,您可能希望起始坐标系的字段包含负整数,或者为真实中心提供一些偏移。假设您提供了一个像素偏移到真正的中心。然后你的转换成为:

float x_prime = ((float)x + 0.5f - (float)x_offset) * (float)units_per_pixel;
float y_prime = ((float)y + 0.5f - (float)y_offset) * (float)units_per_pixel;

答案 3 :(得分:2)

丢弃您的背景信息,我了解您要解决的基本问题如下:

给定有限数量的(正)整数{x_1,... x_N}找到一些(有理数)f,使得所有x_i / f都“很好”。

如果你坚持“漂亮”意味着整数并且尽可能小,那么f = GCD就是这个问题的(数学上)精确答案。如果GCD是1,那就没有什么“更好”,运气不好。

如果“好”应该用小分子和分母来表示理性,那么问题会变得更有趣,并且取决于“小”意味着什么,在小绝对值(f = max)和小分母(f)之间找到你的权衡= GCD)。但是请注意,小分子/分母并不意味着小的浮点表示,例如基数为10的1/3 = 0.333333 ....

如果你想要短floating points,请确保f是你的基数的幂,即10或2,这取决于这些数字对用户来说应该是短的还是实际上具有合理的机器表示。这是用于scientific representation浮点数的内容,这可能是如何使十进制数字首先看起来很好的问题的最佳答案。

答案 4 :(得分:1)

您可以回收当前用于矢量标准化的代码,将值标准化以适应最大值。值1;例如:

向量的3d归一化公式在这里工作正常 首先得到长度:

|a| = sqrt((ax * ax) + (ay * ay) + (az * az))

然后,您需要将每个组件的值除以长度:

x = ax/|a|
y = ay/|a|
z = az/|a|

现在所有x, y, z值都将落入-1到1的最大值,与OpenGL基本坐标系相同。

我知道这并不会产生你想要的整数系统,但它确实给这个范围带来了更小的统一感。

假设您只想将范围限制为整数,只需使用如下函数,它将采用标准化值并将其转换为仅限int的范围值:

#include <algorithm>    // this allows the use of std::min
int maxVal = 256
unsigned char convertToSpread(float floatValueToConvert){
    return (unsigned char) (std::min((maxVal-1), (int) (floatValueToConvert * maxVal)));
}

以上内容会将您的值扩展到0到25​​5之间,只需将maxVal的值增加到您需要的值,并将unsigned char更改为适合您需要的数据类型。 因此,如果您想要1024个值,只需将maxVal更改为1024并unsigned char to unsigned int`

希望这有帮助,但是,如果您还需要更多信息,请告诉我,我可以详细说明:)