Рассчитайте количество раз, чтобы разделить на два - PullRequest
5 голосов
/ 20 декабря 2010

Привет.

У меня есть метод Java, который я считаю дорогим, и я пытаюсь заменить некоторые вызовы на него математическим выражением. Проблема в том, что я сосу математику. Я имею в виду действительно сосать.

Следующее должно объяснить шаблон, который я пытаюсь использовать.

f(x)   -> y
f(x*2) -> f(x)+1

То есть всякий раз, когда я удваиваю значение для x, значение для y будет на 1 больше, чем для x / 2. Вот несколько примеров вывода:

f(5)   -> 6
f(10)  -> 7
f(20)  -> 8
f(40)  -> 9
f(80)  -> 10
f(160) -> 11
f(320) -> 12

Мой нынешний подход - грубая сила. Я зацикливаюсь на X и проверяю, сколько раз я могу уменьшить его вдвое, прежде чем достигну 5, и, наконец, добавляю 6. Это работает и работает быстрее, чем вызов исходного метода. Но я искал более «элегантное» или потенциально более дешевое решение.

Принятый ответ направляется тому, кто мне помогает, не указывая, насколько я глуп:)

(название, вероятно, отстой, потому что я не знаю, что я ищу)

Ответы [ 7 ]

6 голосов
/ 20 декабря 2010

Рассматривали ли вы, что то, на что вы смотрите, по существу делит на пять, находит, какая у вас сила двух, и прибавляет 6 к этой силе?

Общий подход к «дано Y узнать, что это за X, это сила» состоит в использовании логарифмов . С помощью калькулятора попробуйте разделить лог 64 на лог 2 и увидите, что вы получите 6.

Итак - разделите на пять, возьмите журнал, разделите на журнал два и добавьте шесть.

3 голосов
/ 20 декабря 2010

Вы ищете логарифм (основание 2)

, если основание x равно 5, а основание y равно 6, тогда log2 (320/5) + 6 = 12

В Java (Math.log(320 / x) / Math.log(2)) + y

Где x и y - исходные значения (в данном примере f(5) = 6)

1 голос
/ 21 декабря 2010

Прежде всего, ваш вопрос не является корректным.Это пример того, как не следует задавать вопрос (без обид).Я возьму, что ваш вопрос таков: если задано положительное целое число x, найдите такие целые числа m, n, что x = m * 2 ^ n, где m нечетно, затем верните y = f (x) = n + g (m).Поскольку вы не указали, как вычисляется f (x) для нечетного x, я предполагаю, что g (.) Задано.

Если в вашей задаче n не велико, использование большегосложный алгоритм, чем простой цикл (наивный алгоритм).Я представлю алгоритм, который полезен для случая, когда n велико (скажем, несколько сотен, несколько тысяч).Я не знаю Java, поэтому я представляю алгоритм в общем виде (используя синтаксис в стиле Python).

Если вы берете двоичное представление x (b_ {N-1} ... b_1 b_0, b_i = 0 или 1), тогда ваша проблема сводится к нахождению первого бита 1 справа (т.е. младшего значащего бита 1).Скажем, позиция этого бита k (0 <= k <= N-1), тогда n = k и m = x >> k.Я считаю, что лучшее, что вы можете сделать, чтобы найти k, это использовать бинарный поиск, который приводит к алгоритму O (log N).Алгоритм выглядит следующим образом (<< и >> - операторы сдвига):

Algorithm: Input: x -> Outputs: (m, n)

if x is odd or x == 0: return (x, 0)  # x is odd when x & 1 == 1 or x % 2 == 1
N = ceil(log(x, 2))  # ceil: smallest integer larger than the argument
n = 0
orig_x = x

while x & 1 == 0:   # if bit 0 is 1 (i.e. x is odd), stop the search
  half = int(N/2)
  lowerhalf = x & ((1 << half) - 1)  # obtain the lower half
  if lowerhalf == 0:   # all zeros
    n += half
    x = x >> half
    N -= half
  else:
    x = lowerhalf
    N = half

return (orig_x >> n, n)

Например, если N = 1000 (x - 1000-битное целое число), этот алгоритм занимает не более 10 итераций.(в то время как наивный алгоритм требует 1000 итераций в худшем случае).Однако фактическое время выполнения зависит от того, как реализованы битовые операции и оператор == для целых чисел длиной 1000 бит.

1 голос
/ 20 декабря 2010

Это не ответ, но я не мог сделать это комментарием.Рассмотрим рекурсивную функцию:

int someFunc(int n, int times) {
  if(n == 0) return 0;
  if( n % 2 == 0) return someFunc(n/2, times+1);
  else return n+times;
}

Делает ли она то, что вы хотите?

someFunc(1, 0) -> 1
someFunc(2, 0) -> 2
someFunc(3, 0) -> 3
someFunc(5, 0) -> 5
someFunc(10, 0) -> 6
...
1 голос
/ 20 декабря 2010

то, что вы ищете, это число цифр в двоичном представлении, которое (для числа 10 и логарифмов 10) определяется как log (x) / log (2)

0 голосов
/ 20 декабря 2010

Попробуйте это:

f(x) = log<sub>2</sub>(x/5) + 6
0 голосов
/ 20 декабря 2010

ОК, во-первых, давайте заметим, что все входы кратны 5, поэтому мы извлекаем коэффициент 5 во входах;и мы замечаем, что выходы начинаются с 6, поэтому мы вытягиваем масштабирование на 6 в выходах.Я назову эту новую функцию g:

g(1)      0
g(2)      1
g(4)      2
g(8)      3
g(16)     4
g(32)     5
g(64)     6

Теперь эта функция, надеюсь, намного более знакома - g(x) является простым 2 в степени x.И чтобы сделать это (в Java), мы можем просто использовать java.lang.Math.pow(2, x).

Все, что остается, - это получить f из g.Но это просто:

  • с учетом входных данных для f
  • , деленных на 5
  • , используемых в качестве входных данных для g
  • add6

Я оставлю это для вас.

...