It does not slow down the generation of permutations, but searches the list.
For a start it is worth replacing the list with a hash or tree. The complexity will immediately fall from O (N! Log (N!)) To ~ O (N!) (In the case of a successful hash) or ~ O (N! Loglog (N)) in the case of a tree. In practice, the time should be reduced at least 10-20 times already for N = 8.
An example of a hash implementation.
A couple of notes:
- As-is code. There are no complaints about optimality.
- The hash function is honestly lent, the difference in quality for the table is minimal.
- The hashes are written for little-ending architecture.
- The dimensions of the table can be made round, simple, made them in the first pairs ...
- At the same time, it will be possible to get rid of modulo division when hashing.
Code:
#include <stdint.h> #include <stdbool.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <assert.h> //#define HEAP_PROFILE //turn on collission counting #define LOG2l(X) ((unsigned) ( 8*sizeof (long) - __builtin_clzl((X)) - 1)) #define COUNT_OF(arr) (sizeof(arr)/sizeof(arr[0])) #define SWAP(a, b) do { \ typeof (a) _a = (a); \ a = b;\ b=_a;} while (0) #define SIZE_RING 10 typedef int16_t permutInt_t; typedef permutInt_t permut_t[SIZE_RING]; static const permut_t ring = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; //static const permut_t ring = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; static size_t hashPrimes[]={ 61l, 127l, 251l, 509l, 1021l, 2039l, 4093l, 8191l, 16381l, 32749l, 65521l, 131071l, 262139l, 524287l, 1048573l, 2097143l, 4194301l, 8388593l, 16777213l, 33554393l, 67108859l, 134217689l }; // primes number for hash size typedef struct spectrNode_t { permut_t spectVector; struct spectrNode_t *next; } spectrNode_t; typedef struct spectrHash_t { size_t sz; spectrNode_t **tbl; size_t num; uint8_t size_cnt; uint32_t seed; #ifdef HEAP_PROFILE size_t collisions; #endif // HEAP_PROFILE } spectrHash_t; int spectrHash_rehash (spectrHash_t *hash); inline uint32_t murmur3_32(const uint8_t* key, size_t len, uint32_t seed) { uint32_t h = seed; if (len > 3) { const uint32_t* key_x4 = (const uint32_t*) key; size_t i = len >> 2; do { uint32_t k = *key_x4++; k *= 0xcc9e2d51; k = (k << 15) | (k >> 17); k *= 0x1b873593; h ^= k; h = (h << 13) | (h >> 19); h = (h * 5) + 0xe6546b64; } while (--i); key = (const uint8_t*) key_x4; } if (len & 3) { size_t i = len & 3; uint32_t k = 0; key = &key[i - 1]; do { k <<= 8; k |= *key--; } while (--i); k *= 0xcc9e2d51; k = (k << 15) | (k >> 17); k *= 0x1b873593; h ^= k; } h ^= len; h ^= h >> 16; h *= 0x85ebca6b; h ^= h >> 13; h *= 0xc2b2ae35; h ^= h >> 16; return h; } inline uint32_t hashFNV (const void *arr, size_t sz) { // FNV-1a hash uint32_t rv = 0x811c9dc5; while(sz--) { rv = (rv*16777619) ^ *(uint8_t*)arr++; } return rv; } inline uint32_t spectrHash_hashVal (const void *val, spectrHash_t* hash) { return murmur3_32 (val, sizeof(permutInt_t)*SIZE_RING, hash->seed) % hash->sz; //return hashFNV (val, sizeof(permutInt_t)*SIZE_RING) % hash->sz; } int spectrHash_init (spectrHash_t* hash) { hash->size_cnt = 0; hash->sz = hashPrimes[hash->size_cnt]; hash->num = 0; #ifdef HEAP_PROFILE hash->collisions = 0; #endif // HEAP_PROFILE hash->seed = hash->sz-2; //< should actually be random hash->tbl = malloc ( hash->sz*sizeof(hash->tbl[0]) ); if (hash->tbl) { memset (hash->tbl, 0, hash->sz*sizeof(hash->tbl[0])); return 0; } else { return 1; } } /** * Tries to find a node with given value in the table. If the value is not in the table * insert it. * @returns a pointer to either the found or inserted value. */ spectrNode_t *spectrHash_findOrInsert (spectrHash_t *hash, permut_t val) { uint32_t valHash = spectrHash_hashVal (val, hash); spectrNode_t *node = hash->tbl [valHash], *last; if (node) { do { if (memcmp (val, node->spectVector, sizeof(*val)*SIZE_RING) == 0) { // we found the node; return ir return node; } last = node; node = node->next; } while (node); // we haven't found the node; insert to the last node last->next = malloc (sizeof(spectrNode_t)); node = last->next; #ifdef HEAP_PROFILE hash->collisions++; #endif // HEAP_PROFILE } else { // hash key is empty; insert directly to the table hash->tbl [valHash] = malloc (sizeof(spectrNode_t)); node = hash->tbl [valHash]; } assert (node); memcpy (node->spectVector, val, sizeof(*val)*SIZE_RING); node->next = 0; if (4*++hash->num > 3*hash->sz) { spectrHash_rehash (hash); } return node; } int spectrHash_rehash (spectrHash_t *hash) { hash->size_cnt++; size_t old_sz = hash->sz; if (hash->size_cnt < COUNT_OF(hashPrimes)-1) { hash->sz = hashPrimes[hash->size_cnt]; } else { hash->sz = old_sz<<1; } spectrNode_t **new_tbl = malloc (hash->sz*sizeof(new_tbl[0])); assert (new_tbl); memset (new_tbl, 0, hash->sz*sizeof(new_tbl[0])); #ifdef HEAP_PROFILE hash->collisions = 0; #endif // HEAP_PROFILE hash->seed = hash->sz-2; //< should be random for (size_t i=0; i<old_sz; i++) { for (spectrNode_t *node = hash->tbl[i], *next; node; node=next) { next = node->next; node->next = 0; uint32_t valHash = spectrHash_hashVal (node->spectVector, hash); if (!new_tbl[valHash]) { new_tbl[valHash] = node; } else { #ifdef HEAP_PROFILE hash->collisions++; #endif // HEAP_PROFILE spectrNode_t *tail; for (tail = new_tbl[valHash]; tail->next; tail=tail->next) {} tail->next = node; } } } free ( hash->tbl ); hash->tbl = new_tbl; return 0; } void countSpectVector(spectrHash_t *hash, permut_t perm) { permut_t intSpectr; int i = 0; permutInt_t diffTmp = 0, betta = 0; memset(intSpectr, 0, SIZE_RING * sizeof(intSpectr[0])); for (i = 0; i < SIZE_RING; i++){ betta = ring[i]; diffTmp = perm[( 1 + betta ) % SIZE_RING] - perm[betta]; if (diffTmp < 0) diffTmp += SIZE_RING; intSpectr[diffTmp]++; } spectrHash_findOrInsert(hash, intSpectr); } void generatePerms(permut_t arr, spectrHash_t *hash) { permut_t c; int i; i = 0; memset(c, 0, SIZE_RING * sizeof(c[0])); while (i < SIZE_RING) { if (c[i] < i) { if (i % 2 == 0) SWAP(arr[0], arr[i]); else{ SWAP(arr[c[i]], arr[i]); } countSpectVector(hash, arr); c[i] += 1; i = 0; } else { c[i] = 0; i += 1; } } } int main() { permut_t perm; spectrHash_t hash; spectrHash_init (&hash); for (size_t i = 0; i < SIZE_RING; i++) perm[i] = i; generatePerms(perm, &hash); #ifdef HEAP_PROFILE int collisionCounters[36]; memset (collisionCounters, 0, sizeof(collisionCounters)); for (size_t i=0; i<hash.sz; i++) { if(hash.tbl[i]) { size_t col = 0; for (spectrNode_t *head = hash.tbl[i]; head->next; head=head->next) { col++; } if(col) { switch(col) { case 0: case 1: case 2: case 3: case 4: collisionCounters[col-1]++; break; default: collisionCounters[LOG2l(col)+2]++; // smart-ass for sort array to the buckets there each bucket contains element <2^N break; } } } } printf("size = %zd, num = %zd collision = %zd\n", hash.sz, hash.num, hash.collisions); long i; bool is_print_started = false; for(i=COUNT_OF (collisionCounters)-1; i > 4; i--) { if (is_print_started || collisionCounters[i]) { printf ("%4d collisions with depth <%lu\n", collisionCounters[i], 1l<<(i-2)); is_print_started = 1; } } for(; i>=0; i--) { if (is_print_started || collisionCounters[i]) { printf ("%4d collisions with depth %ld\n", collisionCounters[i], i+1); is_print_started = 1; } } #endif // HEAP_PROFILE return 0; } extern inline uint32_t hashFNV (const void *arr, size_t sz); extern inline uint32_t murmur3_32(const uint8_t* key, size_t len, uint32_t seed); extern inline uint32_t spectrHash_hashVal (const void *val, spectrHash_t* hash);