2011-02-06 10 views
5

szukam mały, szybki (w obu kierunkach) bijective mapowanie pomiędzy poniższej listy liczb całkowitych i podzbioru zakresu 0-127:Efektywne mapowanie dla określonej skończonej liczby całkowitej ustawić

0x200C, 0x200D, 0x200E, 0x200F, 
0x2013, 0x2014, 0x2015, 0x2017, 
0x2018, 0x2019, 0x201A, 0x201C, 
0x201D, 0x201E, 0x2020, 0x2021, 
0x2022, 0x2026, 0x2030, 0x2039, 
0x203A, 0x20AA, 0x20AB, 0x20AC, 
0x20AF, 0x2116, 0x2122 

oczywistym rozwiązaniem jest:

y = x>>2 & 0x40 | x & 0x3f; 
x = 0x2000 | y<<2 & 0x100 | y & 0x3f; 

Edit: mi brakuje niektórych wartości, zwłaszcza 0x20Ax, które nie współpracują z powyższych.

Innym oczywistym rozwiązaniem jest tablica przeglądowa, ale bez konieczności niepotrzebnego powiększania, tabela przeglądowa i tak wymagać będzie nieco przegrupowania i podejrzewam, że całe zadanie można lepiej wykonać przy prostym przegrupowaniu bitów.

Dla ciekawskich, te magiczne liczby są jedynymi "dużymi" kodami kodowymi Unicode, które pojawiają się na starszych stronach kodowych ISO-8859 i Windows.

+0

http://en.wikipedia.org/wiki/Quine%E2%80%93McCluskey_algorithm –

+0

okazji, bijective odwzorowywania podzbioru jest wywoływana za pomocą wstrzyknięć;) – Christoph

Odpowiedz

1

wiem, że jest brzydki, ale z wyjątkiem ostatniej wartości wszyscy inni są już unikatowe jeśli wziąć pod uwagę najniższe 6 bitów, więc można po prostu zbudować i odwrotna mapa:

int ints[] = {0x200C, 0x200D, 0x200E, 0x200F, 
       0x2013, 0x2014, 0x2015, 0x2017, 
       0x2018, 0x2019, 0x201A, 0x201C, 
       0x201D, 0x201E, 0x2020, 0x2021, 
       0x2022, 0x2026, 0x2030, 0x2039, 
       0x203A, 0x20AA, 0x20AB, 0x20AC, 
       0x20AF, 0x2116, 0x2122}; 

int invmap[64]; 

void mkinvmap() 
{ 
    for (int i=0; i<26; i++) 
     invmap[ints[i]&63] = ints[i]; 
    invmap[0] = 0x2122; 
} 

Po tym odwrotność mapy obliczeń dwa przekształcić funkcje

int direct(int x) { return x==0x2122 ? 0 : (x & 63); } 
int inverse(int x) { return invmap[x]; } 

funkcja direct(x) powróci liczbę od 0 do 63, a funkcja inverse(x) podano liczbę między 0 a 63 powróci liczbę całkowitą. Dla wszystkich 27 wartości na liście inverse(direct(x)) == x.

1

Poszedłem na jakąś prostą (i tanią) funkcję skrótu f, którą wybrałeś z rodziny f0, f1, ... takich funkcji, które odwzorowują na wartości 0..255, powiedzmy. Jeśli twoja funkcja hash będzie losowa, przez paradoks urodzin będziesz miał pewne kolizje dla wartości, które cię interesują, ale nie wiele.

Teraz prosty skrypt perl (dowolnego) pozwoli na wstępne przetworzenie danych o stałej wartości w celu zmniejszenia (lub nawet wyeliminowania) kolizji poprzez wybranie odpowiedniej funkcji ze swojego zestawu.

Takie podejście ma tę zaletę, że można odnowić przebieg wstępny, jeśli okaże się, że nie pamiętasz wartości (jak już zrobiłeś) lub jakiś dziwny kraj decyduje się na mapowanie dziwnych znaków Unicode, takich jak €, na zestaw znaków 8-bitowych.

I, BTW, myślę, że ilość znaków specjalnych, które są w niektórych z iso-8859-? zestawy muszą być znacznie większe niż to, co masz, tutaj, nie? Wziąłbym je wszystkie.

Edit: Po wykonaniu kilku eksperymentów trochę skrypt perl mówi mi, że wszystkie 577 Unicode punkty kodowe, które pojawiają się w jednym z kodowaniem iso-8859 map do różnych pozycjach, gdy zmniejszona modulo 10007 lub 10009.

Edit: W poniższej tabeli robi trick, dla ograniczonego zestawu:

wchar_t const uniqTable[91] = { 
[0x7] = L'\u2116' /* № */, 
[0xD] = L'\uFFFD' /* � */, 
[0xE] = L'\u200C' /* ‌ */, 
[0xF] = L'\u200D' /* ‍ */, 
[0x10] = L'\u200E' /* ‎ */, 
[0x11] = L'\u200F' /* ‏ */, 
[0x13] = L'\u2122' /* ™ */, 
[0x15] = L'\u2013' /* – */, 
[0x16] = L'\u2014' /* — */, 
[0x17] = L'\u2015' /* ― */, 
[0x19] = L'\u2017' /* ‗ */, 
[0x1A] = L'\u2018' /* ‘ */, 
[0x1B] = L'\u2019' /* ’ */, 
[0x1C] = L'\u201A' /* ‚ */, 
[0x1E] = L'\u201C' /* “ */, 
[0x1F] = L'\u201D' /* ” */, 
[0x20] = L'\u201E' /* „ */, 
[0x22] = L'\u2020' /* † */, 
[0x23] = L'\u2021' /* ‡ */, 
[0x24] = L'\u2022' /* • */, 
[0x28] = L'\u2026' /* … */, 
[0x32] = L'\u2030' /* ‰ */, 
[0x3B] = L'\u2039' /* ‹ */, 
[0x3C] = L'\u203A' /* › */, 
[0x51] = L'\u20AA' /* ₪ */, 
[0x52] = L'\u20AB' /* ₫ */, 
[0x53] = L'\u20AC' /* € */, 
[0x56] = L'\u20AF' /* ₯ */, 
}; 
+0

większości znaków w izo-8859- * i okien strony kodowe są w zakresy dla ich odpowiednich alfabetów (cyrylica, grecki, hebrajski, rozszerzona łacina, ...), ale używałem o wiele większych tabel niż to konieczne, aby pomieścić kilka rzadkich kodów U + 2xxx tu i tam (znak euro, znak towarowy, smart cytaty itp.) –

+0

Ok, widzę. Ale wciąż, zamiast powtarzać różne zestawy znaków, wybrałem ogólne rozwiązanie, aby je wszystkie uchwycić. Jeśli spojrzysz na tabelę w https://secure.wikimedia.org/wikipedia/en/wiki/ISO/IEC_8859, nie ma ich zbyt wiele. Ale być może trzeba by było włożyć je w coś nieco większego niż myślałem, 10 bitowe powinno całkiem dobrze. –

+0

Rzeczywiście 10 bitów na wpis jest wystarczające dla większości starszych zestawów znaków, z wyjątkiem brzydkich przypadków U + 2xxx. 0-127 w moim pytaniu pochodzi z faktu, że żadne wysokie bajty nie mogą być mapowane na ASCII, więc mogę ponownie użyć liczb w tym zakresie jako przekierowań dla znaków U + 2xxx. –

0

Metodą prób & błędów doszedłem do następującego algorytmu:

#include <assert.h> 
#include <stdio.h> 

static const unsigned CODES[] = { 
    0x200C, 0x200D, 0x200E, 0x200F, 
    0x2013, 0x2014, 0x2015, 0x2017, 
    0x2018, 0x2019, 0x201A, 0x201C, 
    0x201D, 0x201E, 0x2020, 0x2021, 
    0x2022, 0x2026, 0x2030, 0x2039, 
    0x203A, 0x20AA, 0x20AB, 0x20AC, 
    0x20AF, 0x2116, 0x2122 
}; 

static unsigned enc(unsigned value) 
{ 
    return (value & 0x3F) + (value & 0x180)/4; 
} 

static unsigned dec(unsigned value) 
{ 
    return 0x2000 + value + ((value & 0x40) >> 6) * 3 * 
     (0x20 + (value & 0x10) * 2 + (value & 0x20)); 
} 

int main(void) 
{ 
    const unsigned *const END = CODES + sizeof CODES/sizeof *CODES; 
    const unsigned *current = CODES; 
    for(; current < END; ++current) 
    { 
     printf("%04x -> %02x -> %04x\n", 
      *current, enc(*current), dec(enc(*current))); 

     assert(enc(*current) < 0x80); 
     assert(dec(enc(*current)) == *current); 
    } 

    return 0; 
} 

Czasami uderzeń evolution inteligentna konstrukcja nawet przy pisaniu kodu;)

+0

Dane wyjściowe 'enc' są dużo większe niż 127. –

+0

@R ..: zastąpiony algorytm ... – Christoph

3

Ta metoda wykorzystuje mnożenie w skończonym fie LD:

#define PRIME 0x119 
#define OFFSET1 0x00f 
#define OFFSET2 0x200c 
#define OFFSET3 (OFFSET2 - OFFSET1) 
#define MULTIPLIER 2 
#define INVERSE 0x8d 

unsigned map(unsigned n) 
{ 
    return ((n - OFFSET3) * MULTIPLIER) % PRIME; 
} 

unsigned unmap(unsigned m) 
{ 
    return ((m * INVERSE) + PRIME - OFFSET1) % PRIME + OFFSET2; 
} 

map() przekształca unikodowym punktów unikalnych 7 bitowych liczb i unmap() nie odwrotnie. Zauważ, że gcc przynajmniej jest w stanie skompilować to do kodu x86, który nie używa żadnych operacji dzielenia, ponieważ moduł jest stałą.

+0

Czy opracowałeś to ręcznie lub masz narzędzie, aby to zrobić? Jest to z pewnością najbardziej elegancka odpowiedź na moje pytanie zadane, chociaż mogę skończyć, robiąc coś w rodzaju Jensa i obsługiwać * wszystkie * postacie w tych zestawach z dwupoziomową mapą. –

+0

@R .: Wybrałem '0x119' jako pierwszą liczbę pierwszą większą niż' 0x2122 - 0x200c', a następnie napisałem krótki program w języku C, aby zaimportować wartości 'OFFSET1' i' MULTIPLIER', które dały najwęższy zakres. Ponieważ ten zakres był mniejszy niż "0x7f", zatrzymałem się tam i obliczyłem odwrotność mnożenia '2' mod' 0x119'. Jeśli '0x119' nie zadziałałbym, poszedłbym do następnego wyższego poziomu. – caf

+0

ładne, czyste podejście do problemu; co dziwne, mój algorytm ad-hoc wydaje się przewyższać twoją, nawet jeśli moja funkcja dekodowania wygląda naprawdę paskudnie ... – Christoph

Powiązane problemy