Voor een stuk rekencode dat ik aan het uitwerken ben moet ik heel veel verschillende combinaties van een stel producten doorrekenen.
Die combinaties (van max 31 producten) representeer ik in Java met een bitmap dmv een int.
Een en ander bestaat uit twee lagen, waarvan de tweede laag ook nog eens recursief werkt. Die eerste laag heeft echter niet zoveel invloed op de executietijd, dus die laat ik hier buiten beschouwing.
Voor de tweede laag begin ik met een deelverzameling van die producten, dat kan dus bijvoorbeeld 0100101 zijn. Vervolgens wil ik daarvan alle mogelijke deelverzamelingen, behalve 0, genereren. Bij dit voorbeeld is dat dus:
0100101, 0000101, 0100001, 0100100, 0000001, 0000100 en 0100000
De enige manier die ik heb kunnen verzinnen/vinden om die combinaties te genereren is door domweg te tellen hoeveel bits er zij (3 hier) en dan een nieuwe bitmap te maken met die grootte. Zodat je uiteindelijk met een simpele for-loop alle (23 - 1) combinaties kan maken door domweg van 1 t/m 7 te tellen in dit voorbeeld.
En zodra je zo'n combinatie "gegenereerd" hebt, moet je de originele bitmap weer zo vertalen dat de gekozen bits daarvan in een nieuwe bitmap verschijnen. Dus dat het getal 5 uit de kleine bitmap (101) vertaald wordt naar 0100001 uit de grote bitmap. Ik weet overigens vooraf niet hoeveel of welke bits er in het origineel geset zijn.
Ik heb daarvoor imho een redelijk efficient algoritme voor uitgewerkt, maar aangezien ik nog steeds vrij weinig bitmagic beheers, denk ik wellicht nog te veel in losse bits. Hieronder staat mijn code om een bovenstaande tekst in code-vorm nog een keertje weer te geven. Momenteel zit zo'n 20% van mijn totale executietijd (volgens een sampling profiler) in de bovenste functie en nog eens 60% in de onderste.
Deze code is uiteraard niet alles wat er is, maar wel alle statements die ik atm doe met de bitmaps. Momenteel is de methode die het grootste deel van mijn executie tijd (in het algemeen dus) opslokt de 'alternativeBitmap'-methode. Ironisch genoeg kan de hotspot-optimizer van Java het als losse methode beter optimaliseren dan in-line in de generateCombinations(), maar als de inhoud van die methode tot een of enkele statements gereduceerd kan worden, dan is dat wellicht weer een ander verhaal.
Dus is er een nog efficientere methode om die varianten te genereren?
Die combinaties (van max 31 producten) representeer ik in Java met een bitmap dmv een int.
Een en ander bestaat uit twee lagen, waarvan de tweede laag ook nog eens recursief werkt. Die eerste laag heeft echter niet zoveel invloed op de executietijd, dus die laat ik hier buiten beschouwing.
Voor de tweede laag begin ik met een deelverzameling van die producten, dat kan dus bijvoorbeeld 0100101 zijn. Vervolgens wil ik daarvan alle mogelijke deelverzamelingen, behalve 0, genereren. Bij dit voorbeeld is dat dus:
0100101, 0000101, 0100001, 0100100, 0000001, 0000100 en 0100000
De enige manier die ik heb kunnen verzinnen/vinden om die combinaties te genereren is door domweg te tellen hoeveel bits er zij (3 hier) en dan een nieuwe bitmap te maken met die grootte. Zodat je uiteindelijk met een simpele for-loop alle (23 - 1) combinaties kan maken door domweg van 1 t/m 7 te tellen in dit voorbeeld.
En zodra je zo'n combinatie "gegenereerd" hebt, moet je de originele bitmap weer zo vertalen dat de gekozen bits daarvan in een nieuwe bitmap verschijnen. Dus dat het getal 5 uit de kleine bitmap (101) vertaald wordt naar 0100001 uit de grote bitmap. Ik weet overigens vooraf niet hoeveel of welke bits er in het origineel geset zijn.
Ik heb daarvoor imho een redelijk efficient algoritme voor uitgewerkt, maar aangezien ik nog steeds vrij weinig bitmagic beheers, denk ik wellicht nog te veel in losse bits. Hieronder staat mijn code om een bovenstaande tekst in code-vorm nog een keertje weer te geven. Momenteel zit zo'n 20% van mijn totale executietijd (volgens een sampling profiler) in de bovenste functie en nog eens 60% in de onderste.
Deze code is uiteraard niet alles wat er is, maar wel alle statements die ik atm doe met de bitmaps. Momenteel is de methode die het grootste deel van mijn executie tijd (in het algemeen dus) opslokt de 'alternativeBitmap'-methode. Ironisch genoeg kan de hotspot-optimizer van Java het als losse methode beter optimaliseren dan in-line in de generateCombinations(), maar als de inhoud van die methode tot een of enkele statements gereduceerd kan worden, dan is dat wellicht weer een ander verhaal.
Dus is er een nog efficientere methode om die varianten te genereren?
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
| private Some generateCombinations(int origBitmap, params...) { // Use a smaller bitmap to get all possible combinations from the original. final int comboLimit = (1 << bitCount(origBitmap)); for(int combo = 1; combo < comboLimit; combo++) { int subBitCount = bitCount(combo); if(subBitCount not ok) continue; // Get the bit-variant int newBitmap = alternativeBitmap(origBitmap, comboLimit, combo); if(newBitmap == origBitmap) // do a little work else if(we can continue) { // Unset all the bits already taken in the origBitmap int leftOverBitmap = origBitmap & ~newBitmap; // And recurse and do a bit of work } } return currentEntry; } private static int alternativeBitmap(int origBitmap, int comboLimit, int combo) { int newBitmap = 0; for(int combobit = 1; combobit < comboLimit; combobit <<= 1) { // Get the lowest bit set (see Integer.lowestOneBit(int)). int lowestBit = origBitmap & -origBitmap; // Unset this bit in the bitmap origBitmap &= ~lowestBit; // See if this new combination actually requires the combobit' bit if((combo & combobit) > 0) { newBitmap |= lowestBit; } } return newBitmap; } |