Какой самый быстрый способ разделить целое число на 3?

int x = n / 3;  // <-- make this faster

// for instance

int a = n * 3; // <-- normal integer multiplication

int b = (n << 1) + n; // <-- potentially faster multiplication

Ответ 1

Это самый быстрый, поскольку компилятор оптимизирует его, если он может в зависимости от выходного процессора.

int a;
int b;

a = some value;
b = a / 3;

Ответ 2

Парень, который сказал "оставить его компилятору", был прав, но у меня нет "репутации", чтобы модифицировать его или комментировать. Я попросил gcc скомпилировать int test (int a) {return a/3; } для ix86, а затем разобрал вывод. Просто для академического интереса, то, что он делает, грубо умножается на 0x55555556, а затем принимает верхние 32 бита из 64-битного результата. Вы можете продемонстрировать это себе, например:

$ ruby -e 'puts(60000 * 0x55555556 >> 32)'
20000
$ ruby -e 'puts(72 * 0x55555556 >> 32)'
24
$ 

Страница wikipedia на раздел Montgomery трудно прочитать, но, к счастью, ребята из компилятора сделали это, поэтому вам не нужно.

Ответ 3

Существует более быстрый способ сделать это, если вы знаете диапазоны значений, например, если вы делите целое число со знаком на 3, и знаете, что диапазон разделяемого значения составляет от 0 до 768, тогда вы может умножить его на множитель и сдвинуть его налево на мощность 2 до этого коэффициента, деленного на 3.

например.

Диапазон 0 → 768

вы можете использовать сдвиг 10 бит, который умножается на 1024, вы хотите разделить на 3, чтобы ваш множитель должен был 1024/3 = 341,

чтобы теперь вы могли использовать (x * 341) → 10
(Удостоверьтесь, что сдвиг - сдвиг с подписью, если используется целое число со знаком), также убедитесь, что сдвиг - фактически сдвиг, а не бит ROLL

Это эффективно делит значение 3 и будет работать примерно в 1,6 раза быстрее, чем естественное деление на 3 на стандартном процессоре x86/x64.

Конечно, единственная причина, по которой вы можете сделать эту оптимизацию, когда компилятор не подходит, потому что компилятор не знает максимальный диапазон X и поэтому не может выполнить это определение, но вы, как программист, можете.

Иногда может быть даже выгоднее переместить значение в большее значение, а затем сделать то же самое, т.е. если у вас есть int полный диапазон, вы можете сделать его 64-битным значением, а затем умножить и сдвинуть вместо деления на 3.

Мне нужно было сделать это недавно, чтобы ускорить обработку изображений, мне нужно было найти среднее из 3 цветовых каналов, каждый цветной канал с байтом диапазона (0 - 255). красный зеленый и синий.

Сначала я просто использовал:

avg = (r + g + b)/3;

(Таким образом, r + g + b имеет максимум 768 и минимум 0, поскольку каждый канал является байтом 0 - 255)

После миллионов итераций вся операция заняла 36 миллисекунд.

Я изменил строку на:

avg = (r + g + b) * 341 → 10;

И это уложило его до 22 миллисекунд, и это удивительно, что можно сделать с небольшой изобретательностью.

Эта скорость произошла на С#, хотя я включил оптимизацию и запускал программу изначально без отладки информации, а не через IDE.

Ответ 5

В зависимости от вашей платформы и в зависимости от вашего компилятора C, собственное решение, например, с помощью

y = x / 3

Может быть быстрым или может быть ужасно медленным (даже если деление полностью выполняется на аппаратном обеспечении, если это делается с помощью инструкции DIV, эта инструкция примерно в 3-4 раза медленнее, чем умножение на современные процессоры). Очень хорошие компиляторы C с включенными флажками оптимизации могут оптимизировать эту операцию, но если вы хотите быть уверенным, вам лучше оптимизировать ее самостоятельно.

Для оптимизации важно иметь целочисленные числа известного размера. В C int нет неизвестного размера (он может варьироваться в зависимости от платформы и компилятора!), Поэтому вам лучше использовать целые числа C99 фиксированного размера. В приведенном ниже коде предполагается, что вы хотите разделить 32-битное целое число без знака на три и что компилятор C знает о 64-битных целых числах (ПРИМЕЧАНИЕ. Даже в 32-битной архитектуре ЦП большинство компиляторов C могут обрабатывать 64-битные целые числа штраф):

static inline uint32_t divby3 (
    uint32_t divideMe
) {
    return (uint32_t)(((uint64_t)0xAAAAAAABULL * divideMe) >> 33);
}

Как сумасшедший, как это может звучать, но метод выше действительно делит на 3. Все, что нужно для этого, - это однократное 64-битное умножение и сдвиг (как я уже сказал, умножения могут быть в 3-4 раза быстрее, чем подразделения на вашем CPU). В 64-битном приложении этот код будет намного быстрее, чем в 32-битном приложении (в 32-битном приложении, умножающем два 64-разрядных номера, на 3 умножения и 3 дополнения на 32-битные значения), однако он может быть еще быстрее, чем разделение на 32-битной машине.

С другой стороны, если ваш компилятор очень хорош и знает, как оптимизировать целочисленное деление на константу (последний GCC делает, я только что проверил), он будет генерировать код выше в любом случае (GCC будет создавать точно этот код для "/3", если вы включите хотя бы уровень оптимизации 1). Для других компиляторов... вы не можете полагаться или ожидать, что он будет использовать трюки, подобные этому, хотя этот метод очень хорошо документирован и упоминается повсюду в Интернете.

Проблема в том, что она работает только для постоянных чисел, а не для переменных. Вам всегда нужно знать магическое число (здесь 0xAAAAAAAB) и правильные операции после умножения (сдвиги и/или дополнения в большинстве случаев), и оба они различаются в зависимости от числа, которое вы хотите разделить, и оба требуют слишком много времени процессора рассчитать их на лету (это будет медленнее, чем аппаратное разделение). Тем не менее, компилятор легко вычислить их во время компиляции (где одна секунда более или менее времени компиляции играет едва ли роль).

Ответ 6

Что делать, если вы действительно не хотите умножать или делить? Вот приближение, которое я только что придумал. Он работает, потому что (x/3) = (x/4) + (x/12). Но поскольку (x/12) = (x/4)/3, нам просто нужно повторить процесс до тех пор, пока он не станет достаточно хорошим.

#include <stdio.h>

void main()
{
    int n = 1000;
    int a,b;
    a = n >> 2;
    b = (a >> 2);
    a += b;
    b = (b >> 2);
    a += b;
    b = (b >> 2);
    a += b;
    b = (b >> 2);
    a += b;
    printf("a=%d\n", a);
}

Результат равен 330. Его можно было бы сделать более точным, используя b = ((b + 2) → 2); для учета округления.

Если вам разрешено размножаться, просто выберите подходящую аппроксимацию для (1/3) с делителем мощности-2. Например, n * (1/3) ~ = n * 43/128 = (n * 43) → 7.

Этот метод наиболее полезен в Indiana.

Ответ 7

Я не знаю, если это быстрее, но если вы хотите использовать побитовый оператор для выполнения двоичного деления, вы можете использовать метод сдвига и вычитания, описанный в эта страница:

  • Установить значение 0
  • Выровнять самые левые цифры в дивидендах и делителях
  • Повтор:
    • Если эта часть дивиденда выше делителя больше или равна делителю:
      • Затем вычитаем делитель из этой части дивиденда и
      • Конкатенация 1 в правую сторону частного
      • Еще один конкатенат 0 в конец правой части частного лица
    • Сдвиньте делитель на одно место справа
  • До тех пор, пока дивиденд не будет меньше дивизора:
  • quotient правильный, дивиденд - остаток
  • STOP

Ответ 8

Если вы действительно хотите увидеть эту статью в целочисленном делении, но она имеет только академические достоинства... было бы интересным приложением что на самом деле нужно было выполнить, что выиграло от такого трюка.

Ответ 9

Для действительно большого целочисленного деления (например, числа, превышающие 64 бит) вы можете представить свой номер как int [] и выполнить деление довольно быстро, взяв по две цифры за раз и разделив их на 3. Остальная часть будет частью следующие две цифры и т.д.

например. 11004/3 вы говорите

11/3 = 3, остаток = 2 (от 11-3 * 3)

20/3 = 6, остаток = 2 (от 20-6 * 3)

20/3 = 6, остаток = 2 (от 20-6 * 3)

24/3 = 8, остаток = 0

следовательно, результат 3668

internal static List<int> Div3(int[] a)
{
  int remainder = 0;
  var res = new List<int>();
  for (int i = 0; i < a.Length; i++)
  {
    var val = remainder + a[i];
    var div = val/3;

    remainder = 10*(val%3);
    if (div > 9)
    {
      res.Add(div/10);
      res.Add(div%10);
    }
    else
      res.Add(div);
  }
  if (res[0] == 0) res.RemoveAt(0);
  return res;
}

Ответ 10

Простое вычисление... не более n итераций, где n - ваше количество бит:

uint8_t divideby3(uint8_t x)
{
  uint8_t answer =0;
  do
  {
    x>>=1;
    answer+=x;
    x=-x;
  }while(x);
  return answer;
}

Ответ 11

В некоторых архитектурах также будет работать подход с поисковой таблицей.

uint8_t DivBy3LU(uint8_t u8Operand)
{
   uint8_t ai8Div3 = [0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, ....];

   return ai8Div3[u8Operand];
}

Ответ 12

Для 64-разрядных номеров:

uint64_t divBy3(uint64_t x)
{
    return x*12297829382473034411ULL;
}

Однако это не то, что вы можете ожидать. Он работает правильно, если число уже делится на 3, но оно возвращает огромное количество, если оно не является.

Например, если вы запустили его, например, 11, он возвращает 6148914691236517209. Это выглядит как мусор, но на самом деле правильный ответ: умножьте его на 3 и верните 11!

Если вы ищете усекающее деление, просто используйте оператор /. Я очень сомневаюсь, что вы можете получить гораздо быстрее, чем это.

Теория:

64-разрядная арифметика без знака является арифметикой по модулю 2 ^ 64. Это означает, что для каждого целого числа, совпадающего с модулем 2 ^ 64 (по существу все нечетные числа), существует мультипликативный обратный, который можно использовать для умножения вместо деления. Это магическое число может быть получено путем решения уравнения 3*x + 2^64*y = 1 с использованием расширенного евклидова алгоритма.