在C#中是否有任何方法可以在x_min和x_max之间包装给定值x.该值不应该被夹紧,Math.Min/Max而是像float模数一样包裹.
实现这一目标的方法是:
x = x - (x_max - x_min) * floor( x / (x_max - x_min));
Run Code Online (Sandbox Code Playgroud)
但是,我想知道是否有一个算法或C#方法实现相同的功能而没有划分,并且没有可能出现的浮点限制精度问题,当值远离所需范围时可能会出现这种问题.
LSe*_*rni 13
您可以使用两个模运算来包装它,这仍然相当于一个除法.我不认为有一种更有效的方法可以做到这一点而不假设一些事情x.
x = (((x - x_min) % (x_max - x_min)) + (x_max - x_min)) % (x_max - x_min) + x_min;
Run Code Online (Sandbox Code Playgroud)
公式中的附加和和模数用于处理x实际上小于x_min的模数,模数可能变为负数.或者你可以用if一个单一的模块化部门来做到这一点:
if (x < x_min)
x = x_max - (x_min - x) % (x_max - x_min);
else
x = x_min + (x - x_min) % (x_max - x_min);
Run Code Online (Sandbox Code Playgroud)
除非x距离x_min和不远x_max,并且可以通过非常少的总和或减法(也可以考虑错误传播),我认为模数是您唯一可用的方法.
请记住,错误传播可能会变得相关,我们可以通过一个循环执行此操作:
d = x_max - x_min;
if (abs(d) < MINIMUM_PRECISION) {
return x_min; // Actually a divide by zero error :-)
}
while (x < x_min) {
x += d;
}
while (x > x_max) {
x -= d;
}
Run Code Online (Sandbox Code Playgroud)
模运算的使用具有一些统计意义(浮点运算也会有不同的运算).
例如,我们将包含0到5之间的随机值(例如六面骰子结果)包装到[0,1]范围(即硬币翻转)中.然后
0 -> 0 1 -> 1
2 -> 0 3 -> 1
4 -> 0 5 -> 1
Run Code Online (Sandbox Code Playgroud)
如果输入具有平坦的频谱,即每个数字(0-5)具有1/6的概率,则输出也将是平坦的,并且每个项目将具有3/6 = 50%的概率.
但是如果我们有一个五边形的骰子(0-4),或者如果我们有一个0到32767之间的随机数并希望在(0,09)范围内减少它以得到一个百分比,那么输出就不会是平的,有些数字会比其他数字略微(或不那么轻微).在五面骰子到硬币翻盖的情况下,头部与尾部的比例为60%-40%.在32767%至百分比的情况下,低于67的百分比将是CEIL(32767/100)/ FLOOR(32767/100)=比其他人高出0.3%.
因此,如果需要平坦输出,则必须确保(max-min)是输入范围的除数.在32767和100的情况下,输入范围必须在最接近的百位(减一)32699被截断,因此(0-32699)包含32700个结果.只要输入> = 32700,就必须再次调用输入函数以获得新值:
function reduced() {
#ifdef RECURSIVE
int x = get_random();
if (x > MAX_ALLOWED) {
return reduced(); // Retry
}
#else
for (;;) {
int x = get_random();
int d = x_max - x_min;
if (x > MAX_ALLOWED) {
continue; // Retry
}
return x_min + (
(
(x - x_min) % d
) + d
) % d;
}
#endif
Run Code Online (Sandbox Code Playgroud)
当(INPUTRANGE%OUTPUTRANGE)/(INPUTRANGE)显着时,开销可能相当大(例如,减少0-197到0-99需要大约两倍的呼叫).
如果输入范围小于输出范围(例如我们有一个硬币鳍状肢,我们想制作一个骰子),使用Horner算法乘以(不要添加)多次,以获得更大的输入范围.硬币翻转范围为2,CEIL(LN(OUTPUTRANGE)/ LN(INPUTRANGE))为3,因此我们需要三次乘法:
for (;;) {
x = ( flip() * 2 + flip() ) * 2 + flip();
if (x < 6) {
break;
}
}
Run Code Online (Sandbox Code Playgroud)
或者从骰子中得到122到221之间的数字(范围= 100):
for (;;) {
// ROUNDS = 1 + FLOOR(LN(OUTPUTRANGE)/LN(INPUTRANGE)) and can be hardwired
// INPUTRANGE is 6
// x = 0; for (i = 0; i < ROUNDS; i++) { x = 6*x + dice(); }
x = dice() + 6 * (
dice() + 6 * (
dice() /* + 6*... */
)
);
if (x < 200) {
break;
}
}
// x is now 0..199, x/2 is 0..99
y = 122 + x/2;
Run Code Online (Sandbox Code Playgroud)
Modulo在浮点上工作正常,那么如何:
x = ((x-x_min) % (x_max - x_min) ) + x_min;
Run Code Online (Sandbox Code Playgroud)
它仍然实际上是一个鸿沟,你需要调整它的值<min ...
当数字远离范围时,您会担心准确性.然而,这与模运算无关,但是它被执行,但是是浮点的属性.如果你取一个介于0和1之间的数字,并为它添加一个大常量,比如将它带到100到101的范围内,它将失去一些精度.
| 归档时间: |
|
| 查看次数: |
11655 次 |
| 最近记录: |