Блог пользователя yeputons

Автор yeputons, 14 лет назад, По-русски
Вроде бы простая задача: посчитать количество единичек в числе.
Я знаю четыре способа её решения: "наивный" метод (пробежаться по всем битам), встроенная в Gcc функция __builtin_popcount, предподсчёт для всех чисел < 0xFFFF и метод за log log X.

Написал тестовую программу (с long long'ами): http://pastebin.com/vTUPkK07
Результаты на Core 2 Duo E4500 2.2 GHz (10^7 long long'ов):
__builtin_popcount: 0.211
countPrecalc: 0.129
countLoglog: 0.269
countSimple: 1.240
Самым быстрым оказался способ с предподсчётом - он делает наивный метод в 10 раз, а метод за loglog и встроенную функцию - в два.
Что любопытно, если попытаться сократить количество операций в предподсчёте (не до 2^16, а до 2^22 - чтобы было 3 сложения вместо четырёх), скорость резко падает до 0.6 - т.е. уже её делают в два раза встроенная функция и loglog.

UPD
: при замене int cnt[] на char cnt[] и компиляциис -O2 скорость countPrecalc возврастает в два раза. При компиляции с -O3 - еще в два - до 0.048.
  • Проголосовать: нравится
  • +5
  • Проголосовать: не нравится

14 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится
Время работы countSimple должно оказаться равным . Можно посмотреть на gcc --version?
  • 14 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    gcc.EXE (GCC) 3.4.5 (mingw-vista special r3)
    • 14 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится
      Это какой-то очень старый gcc. В новых операция битового сдвига знаковая (пруфлинк), поэтому с отрицательными числами countSimple не работает.
14 лет назад, # |
  Проголосовать: нравится +5 Проголосовать: не нравится
Если "int cnt" заменить на "char cnt", на моём старом Athlon 3200+ ещё в два раза быстрее.
  • 14 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    Upd: В два — это с некоторыми оптимизациями (-fomit-frame-pointer -ffast-math -O3).

    Просто с -O2 — в полтора.
  • 14 лет назад, # ^ |
      Проголосовать: нравится +5 Проголосовать: не нравится
    Кстати, да. У меня также (0.086 вместо 0.129)
    А -O3 скорость вырастает до 0.048.

14 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится
Integer.bitCount() в Java
14 лет назад, # |
  Проголосовать: нравится +14 Проголосовать: не нравится

Если увеличить размер чисел, то log log n будет укатывать предпросчет.

 

Вот еще козырный алгоритм, считает за время, равное количеству единиц в числе. То есть в среднем случае за log(n). Идея в том, чтобы сбрасывать последнюю единицу в числе до тех пор, пока оно не станет нулем.

 

ans = 0;

while( x ) {

  ++ ans;

  x = ( ( x - 1 ) & x );

}

 

  • 14 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    А куда еще увеличивать long long? Длинка?
    • 14 лет назад, # ^ |
        Проголосовать: нравится +6 Проголосовать: не нравится

      Нет, длинка не подойдет, потому что нам надо, чтобы операции and и rshift выполнялись за константу.

      Но, допустим, если бы были 256-ти разрядные машиы, на них мне кажется log log n бы уже обшустрил бы предпросчет, потому что предпросчет бы стал медленнее в четыре раза, а log log n бы медленнее почти не стал.