如何简单地循环自定义比例

How to simply loop a custom scale

本文关键字:循环 自定义 何简单 简单      更新时间:2023-10-16

这一定是一个非常简单的数学,但我不知道如何解决它。

我需要写一个函数,返回一个从1到3的数字,如下所示:

input: -5 output: 1
input: -4 output: 2
input: -3 output: 3
input: -2 output: 1
input: -1 output: 2
input: 0  output: 3
input: 1  output: 1
input: 2  output: 2
input: 3  output: 3
input: 4  output: 1
input: 5  output: 2
input: 6  output: 3
input: 7  output: 1

以下函数适用于正数

static const int getOnScaleOneToThree(int input)
{
    int x = ceil(input / 3);
    return abs((input - (x * 3)));
}

但它(当然(与负输入相反,如下所示:

input: -6 output: 3
input: -5 output: 2
input: -4 output: 1
input: -3 output: 3
input: -2 output: 2
input: -1 output: 1
input: 0  output: 3
input: 1  output: 1
input: 2  output: 2
input: 3  output: 3

有什么想法吗?谢谢

这应该有效,并避免了负数模运算的依赖于实现的结果。

if(input >= 0)
{
    return (1 + ((input + 2) % 3)); 
}
else
{
    return (3 - ((-input) % 3));
}

此外,这里还有一个更通用(详细(的解决方案:

static const int getOnScale(int input, int minVal, int maxVal, int zeroVal)
{
    if(input == 0)
    {
        //Might as well return this right away.
        return zeroVal;
    }
    else
    {
        if(maxVal >= minVal)
        {
            int divisor = maxVal - minVal + 1;
            if(input > 0)
            {
                int dividend = input + zeroVal - minVal;
                return (minVal + (dividend % divisor));
            }
            else
            {
                int dividend = maxVal - zeroVal - input;
                return (maxVal - (dividend % divisor));
            }
        }
        else
        {
            //Reverse scale
            int divisor = minVal - zeroVal + 1;
            if(input > 0)
            {
                int dividend = minVal - zeroVal + input;
                return (minVal - (dividend % divisor));
            }
            else
            {
                int dividend = -input + zeroVal - maxVal;
                return (maxVal + (dividend % divisor));
            }
        }
    }
}

在这个问题的情况下,这个函数将被称为:

getOnScale(input, 1, 3, 3);

您可以使用LUT(查找表(。这里有一些伪代码:

int output[] = { 3, 2, 1, 3, 2, 1, 3, 1, 2, 3 };
assert(input >= -6 && input <= 3);
int index = input+6;
int value = output[index];

LUT速度极快,易于维护,非常适合在运行时难以计算的奇怪映射。

当输入范围很大时,它们构成了一个挑战——内存需求显然可以达到平流层。有一些解决方案,例如:

  1. 使用线性函数的近似(将输入范围映射到线性函数(
  2. 多项式逼近
  3. 测距图
static const int getOnScaleOneToThree(int input)
{
    return (int)((unsigned int)(input + 0x80000000)%3 + 1);
}

如果int是32位,则此操作有效。

尝试:

int out( int in )
{
    return ( in >= 0 ) ? ( ( in + 2 ) % 3 ) + 1 : 3 - ( -1 * in % 3 ); }
}

被除数低于零且除数为正时,实现定义的行为的优点。

好吧,我已经考虑了一段时间,但基于此:

https://meta.stackexchange.com/q/213976/171816

我只是想把这个作为一个额外的答案发布,参考另一个问题的答案。


另一个答案的解决方案比我的第一个答案更优雅:https://stackoverflow.com/a/1082938/982107

通过将模函数写入

static int mod(int x, int m)
{
    int r = x%m;
    return r<0 ? r+m : r;
}

即使x为负数,你也会得到这个问题的正确答案。

这将允许您编写这样的函数:

static int getOnScaleOneToThree(int input)
{
    return (mod((input + 2), 3) + 1);
}

这里有一个通用的解决方案,这次将代码量保持在最低限度:

static int getOnScale(int input, int minVal, int maxVal, int zeroVal)
{
    int dividend = input;
    if(minVal > maxVal)
    {
        //Alter values for reverse scale
        std::swap(minVal, maxVal);
        dividend *= -1;
    }
    dividend += (zeroVal - minVal);
    int divisor = maxVal - minVal + 1;
    return (mod(dividend , divisor) + minVal);
}

重要的是不要卷入过早的优化中,因此以下内容可能就足够了。

long get_remainder(long n) {
    long * remainders = new[(unsigned int)0xffffffff];
    long remainder = 1;
    for ( long i = LONG_MIN; i < LONG_MAX; ++i ) {
        remainders[i + -LONG_MIN] = remainder++;
        if ( remainder > 3 )
            remainder = 1;
    }
    int rtn = remainders[n + -LONG_MIN];
    delete[] remainders;
    return rtn;
}

像这样使用:

for ( int i = -10; i < 10; ++i )
{
    int remainder = get_remainder(i);
}