【问题标题】:Turn while loop into math equation?将while循环变成数学方程式?
【发布时间】:2010-09-28 09:18:40
【问题描述】:

我的程序中有两个简单的 while 循环,我觉得应该是数学方程,但我正在努力转换它们:

float a = someValue;
int b = someOtherValue;
int c = 0;

while (a <= -b / 2) {
    c--;
    a += b;
}
while (a >= b / 2) {
    c++;
    a -= b;
}

此代码按原样工作,但我觉得它可以简化为数学方程式。这里的想法是此代码采用偏移量 (someValue) 并调整坐标 (c) 以最小化与瓦片中心的距离(大小为 someOtherValue)。任何帮助将不胜感激。

【问题讨论】:

  • 很好的洞察力——看到这可以浓缩为一个简单的公式。
  • 有人能告诉我这两个循环是做什么的吗?我一辈子都想不通
  • 想想一个 'b' 分开的平铺: ...|....|....|....|... 其中的平铺被编号,第 0 个平铺以 0 为中心(从 -b/2 到 b/2)。这段代码想要找出“a”在哪个图块中。第一个循环移动 a 直到它不在中心图块的左边,第二个循环直到它不在右边。
  • @litb:这可能会有所帮助:想想你将如何仅使用加法和减法而不使用内置的 % 或分工。

标签: c++ c algorithm math


【解决方案1】:

可以证明以下是正确的:

c = floor((a+b/2)/b)
a = a - c*b

注意 floor 表示向下舍入,朝向负无穷:而不是朝向 0。(例如 floor(-3.1)=-4。floor() 库函数将执行此操作;请确保不要只转换为 int,这通常会向 0 舍入。)

大概b 是严格正数,否则两个循环都不会终止:添加b 不会使a 变大,减去b 不会使a 变小。有了这个假设,我们可以证明上面的代码有效。 (而且 paranoidgeek 的代码也几乎是正确的,只是它使用强制转换为 int 而不是 floor。)

巧妙的证明方法: 该代码从a 中添加或减去b 的倍数,直到a[-b/2,b/2) 中,您可以将其视为从a/b 中添加或减去整数,直到a/b[-1/2,1/2),即直到(a/b+1/2)(称为x)在[0,1) 中。因为你只是用整数改变它,x 的值不会改变mod 1,即它转到它的 remainder mod 1,即x-floor(x)。所以你做的有效减法数(即c)是floor(x)

繁琐的证明方式

在第一个循环结束时,c 的值是循环运行次数的负数,即:

  • 0 如果:a > -b/2 a+b/2 > 0
  • -1 如果:-b/2 ≥ a > -3b/2 0 ≥ a+b/2 > -b 0 ≥ x > -1
  • -2 如果:-3b/2 ≥ a > -5b/2 -b ≥ a+b/2 > -2b -1 ≥ x > -2 等等,

其中x = (a+b/2)/b,所以c 是:如果x>0 则为0,否则为“ceiling(x)-1”。如果第一个循环完全运行,那么在 最后一次执行循环之前它是 ≤ -b/2,所以现在它是 ≤ -b/2+b,即 ≤ b/2 .根据它是否恰好是 b/2(即,您开始时 x 是否恰好是一个非正整数),第二个循环恰好运行 1 次或 0,并且 c 是上限(x)或天花板(x)-1。这样就解决了第一个循环运行时的情况。

如果第一个循环没有运行,那么第二个循环结束时 c 的值为:

  • 0 如果:a a-b/2
  • 1 如果:b/2 ≤ a 0 ≤ a-b/2 0 ≤ y
  • 2 如果:3b/2 ≤ a b ≤ a-b/2 1 ≤ y

其中y = (a-b/2)/b,因此 c 为:如果 ya 现在肯定是

所以你可以为c写一个表达式为:

x = (a+b/2)/b
y = (a-b/2)/b
c = (x≤0)*(ceiling(x) - 1 + (x is integer))
   +(y≥0)*(1 + floor(y))                

当然,接下来你会注意到(ceiling(x)-1+(x is integer))floor(x+1)-1 相同,即floor(x),而y 实际上是x-1,所以(1+floor(y))=floor(x),至于条件:
当x≤0时,不可能是(y≥0),所以c只是floor(x)的第一项,
当 0 c 是 0,
当 1 ≤ x 时,只有 0≤y,所以 c 只是第二项,又是 floor(x)。 所以 c = floor(x) 在所有情况下。

【讨论】:

  • 优秀的答案。它运行良好,但我需要阅读几次才能完全理解它。
  • 我的荣幸 :) 实际上我想它可能会更简单一些......我只是按照我第一次解决它的方式编写它。如果有什么令人困惑的地方,请指出,我会再看一遍。
  • 您的版本绝对不等同于 OP 的代码,因为 OP 的代码可能包含无限循环,而您的总是终止。
  • 您的分析是错误的,因为它假设浮点数遵守代数规则。他们没有。特别是a+b==a 可能是b!=0
  • 试试a=0x10000000b=1。然后假设 IEEE 浮点数,a-b==a。我同意您的回答与 OP 可能想要的一致,但重要的是要意识到浮点运算通常不会给您想要的东西。你的代码版本,而不是翻译,更像是一个错误修复。
【解决方案2】:
c = (int)((a - (b / 2)) / b + 1);
a -= c * b;

测试用例http://pastebin.com/m1034e639

【讨论】:

  • 使用 floor 而不是通过将小数部分转换为整数来删除小数部分,使其适用于负值。
【解决方案3】:

我想你想要这样的东西:

c = ((int) a + b / 2 * sign(a)) / b

除了 b 为奇数的某些情况外,这应该与您的循环匹配,因为当 b 为奇数时,从 -b/2 到 b/2 的范围小于 b。

【讨论】:

  • “sin”,不是“sign”,但是是的,你想要一个 sin 波函数。
  • 我认为 Dave 的真正意思是 sign(),定义为: int sign(x) { return x > 0 ? 1 : x
  • 不正确:举个简单的例子,取 a = b = 10,然后两个循环都不会运行(所以 c=0),但这个答案会说 c=1。
  • Shreevatsar:这不是真的。如果a = b = 10,那么a >= b/2,所以后面的循环会运行一次,c = 1。
  • 哦,对不起,我没有想清楚。对于 a=b=10,这表示 c=((int) 10+5)/10,实际上是 1.5 :-) 但是如果 a 和 b 是整数,那么“/”表示整数除法,那么对于正数 a ,它是 floor(a+b/2)/b,所以对,对于负 a,它是 ceil(ab/2)/b=floor((a+b/2)/b) except i> 当 b|(ab/2).
【解决方案4】:

假设 b 为正,abs(c) = floor((abs(a) - b/2) / b)。然后,将 a 的符号应用到 c。

【讨论】:

    猜你喜欢
    • 2023-03-08
    • 2018-03-05
    • 1970-01-01
    • 2022-06-14
    • 2021-04-06
    • 2015-12-17
    • 2022-11-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多