#include /* Úlohu šlo řešit mnoha různými způsoby; uvedu několik z nich. Začátek je vždy stejný: provedeme XOR zadaných čísel. Tím úlohu převedeme na úlohu spočítat jedničky ve dvojkovém zápise čísla. Triviálním řešením na spočtení jedniček je prostě jít po bitech, a za každý jedničkový si přičíst jedničku k výsledku. Toto řešení je nejmenší, ale také nejpomalejší, a vzhledem ke své rychlosti nebylo honorováno příliš mnoha body. Toto řešení je O(počet_bitů_v_int). Na reálném počítači 15 sekund. */ int bitcount_trivial(unsigned int a) { int res = 0; while (a) res += a & 1, a >>= 1; return res; } /* Prvním zlepšením je brát bity ne po jednom, ale po nějakých větších skupinách, a počet bitů zjistit podíváním se do tabulky. Toto řešení je stále O(počet_bitů_v_int). Na reálném počítači 2.7 sekundy. */ char table[2048]; void table_init(void) { int i; for (i=0; i<2048; i++) table[i] = bitcount_trivial(i); } int bitcount_table(unsigned int a) { int res = 0; while (a) res += table[a & 0x7ff], a >>= 11; return res; } /* 2.7 sekundy je trochu, moc, a mělo by to jít rychleji. Kompilátor zřejmě nepochopil, že počet průchodů while cyklem je maximálně 3. Tak budu předpokládat 32-bitové slovo a řeknu mu to. 1.7 sekundy. */ int bitcount_table2(unsigned int a) { return table[a & 0x7ff] + table[(a >> 11) & 0x7ff] + table[a >> 22]; } /* Ale ono to samozřejmě jde lépe. Počet bitů nastavených ve dvou bitech se vejde do dvou bitů. Počet bitů nastavených ve čtyřech bitech se vejde do čtyř bitů, a tak dále. Toto pozorování nám umožní sčítat bity paralelně; nejdřív skupiny po dvou, pak po čtyřech, ... Toto řešení je O(log_2(počet_bitů_v_int)), ale stále předpokládá délku slova 32 bitů. 3.4 sekundy. */ int bitcount_smart(unsigned int n) { n = ((n >> 1) & 0x55555555) + (n & 0x55555555); n = ((n >> 2) & 0x33333333) + (n & 0x33333333); n = ((n >> 4) & 0x0f0f0f0f) + (n & 0x0f0f0f0f); n = ((n >> 8) & 0x00ff00ff) + (n & 0x00ff00ff); n = ((n >> 16) & 0x0000ffff) + (n & 0x0000ffff); return n; } /* A teď jedna perlička, o kterou vás nemohu připravit. Přišla od jednoho z řešitelů a je tak rychlá až je nečitelná. Používá stejný algoritmus jako bitcount_smart, ale poslední dva kroky spojí do jednoho násobení. Hezký nápad. A cenná položka do soutěže "co je to". 2.9 sekundy. */ int bitcount_obfuscated(unsigned int n) { n = n - ((n >> 1) & 0x55555555); n = ((n >> 2) & 0x33333333) + (n & 0x33333333); n = (((n >> 4) + n) & 0x0f0f0f0f); n = (n * 0x01010101) >> 24; return n; } /* A teď jedno řešení teoreticky pěkné: v čase O(log_2(počet_bitů_v_int)), a s kódem nezávislým na délce intu. Předpokládá ale, že unsigned int má nějakou velikost tvaru 2^n. Do tabulky array si připraví jednotlivé masky, které potom používá jako v předchozích algoritmech. 8.7 sekundy. */ unsigned int masks[1024]; int size = 0; void theoretical_init(void) { unsigned int mask = ~0U; int i; int numbits = bitcount_trivial(~0U); while (numbits) { mask = mask ^ (mask >> (numbits /= 2)); masks[size++] = ~mask; } size --; } int bitcount_theoretical(unsigned int n) { int i = size - 1; int shift = 1; while (i >= 0) { n = ((n >> shift) & masks[i]) + (n & masks[i]); i--; shift *= 2; } return n; } /* Existuje ještě jeden algoritmus, který s použitím násobení zvládne spočítat bity v konstantním čase bez ohledu na délku slova. Má jediný problém: já detaily neznám a v žádném z odevzdaných řešení nebyl. Takže snad někdy příště. */ #define bitcount bitcount_theoretical void main(void) { #if 1 unsigned int a, b; scanf("%d %d\n", &a, &b); table_init(); theoretical_init(); printf("%d\n", bitcount(a^b)); #else unsigned int i; volatile unsigned int l = 0; table_init(); theoretical_init(); for (i=0; i<2000000000; i+=12) l += bitcount(i); printf("%d\n", l); #endif } /* Reálný počítač byl AMD Athlon 4 na 900MHz, kompiloval jsem gcc 2.95.4 s -O3. */