Превратить цикл в математическое уравнение? - PullRequest
16 голосов
/ 25 декабря 2008

В моей программе есть два простых цикла 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). Любая помощь будет оценена.

Ответы [ 4 ]

36 голосов
/ 25 декабря 2008

Можно доказать, что верно следующее:

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.)

Умный способ доказать это : Код добавляет или вычитает кратные b от a до a в [-b/2,b/2), которые можно просмотреть как сложение или вычитание целых чисел из a/b до a/b в [-1/2,1/2), то есть до (a/b+1/2) (назовите его x) в [0,1). Поскольку вы меняете его только целыми числами, значение x не меняется mod 1, то есть оно переходит к его модулю остатка 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 равно: 0, если x> 0, и «потолок (x) -1» в противном случае. Если первый цикл вообще выполнялся, то это было ≤ -b / 2 как раз перед тем, как последний раз цикл был выполнен, так что теперь это ≤ -b / 2 + b, то есть ≤ b / 2. В зависимости от того, является ли оно точно b / 2 или нет (т. Е. Было ли x, когда вы начинали, было точно неположительным целым числом или нет), второй цикл выполняется ровно 1 раз или 0, и c является либо потолком (x) или потолок (х) -1. Так что это решает проблему для случая, когда первый цикл действительно работал.

Если первый цикл не выполнялся, то значение c в конце второго цикла:

  • 0, если: a a-b / 2 <0 </li> 1, если: b / 2 ≤ a <3b / 2 <=> 0 ≤ a-b / 2 0 ≤ y <1 </li> 2, если: 3b / 2 ≤ a <5b / 2 <=> b ≤ a-b / 2 <2b <=> 1 ≤ y <2 и т. Д., </li>

, где y = (a-b/2)/b, поэтому c равно 0, если y <0, и 1 + floor (y) в противном случае. [И <code>a сейчас, безусловно, Таким образом, вы можете написать выражение для 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). Так что с = floor(x) во всех случаях.

2 голосов
/ 25 декабря 2008
c = (int)((a - (b / 2)) / b + 1);
a -= c * b;

Тестовый набор при http://pastebin.com/m1034e639

1 голос
/ 25 декабря 2008

Я думаю, вы хотите что-то вроде этого:

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

Это должно соответствовать вашим циклам, за исключением некоторых случаев, когда b нечетно, поскольку диапазон от -b / 2 до b / 2 меньше, чем b, когда b нечетно.

0 голосов
/ 25 декабря 2008

Предполагая, что b положительно, abs (c) = этаж ((abs (a) - b / 2) / b). Затем примените знак a к c.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...