Knihovna kombinatorických funkcí



Podobné dokumenty
Algoritmizace a programování

Program převod z desítkové na dvojkovou soustavu: /* Prevod desitkove na binarni */ #include <stdio.h>

8 Třídy, objekty, metody, předávání argumentů metod

Paměť počítače. alg2 1

for (int i = 0; i < sizeof(hodnoty) / sizeof(int); i++) { cout<<hodonoty[i]<< endl; } cin.get(); return 0; }

Programování v jazyce C a C++

for (i = 0, j = 5; i < 10; i++) { // tělo cyklu }

Programovací jazyk Pascal

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007

7 Formátovaný výstup, třídy, objekty, pole, chyby v programech

Jednoduché cykly

přetížení operátorů (o)

8. lekce Úvod do jazyka C 3. část Základní příkazy jazyka C Miroslav Jílek

Náplň. v Jednoduché příklady na práci s poli v C - Vlastnosti třídění - Způsoby (algoritmy) třídění

Úvod do jazyka C. Ing. Jan Fikejz (KST, FEI) Fakulta elektrotechniky a informatiky Katedra softwarových technologií

5 Přehled operátorů, příkazy, přetypování

Čtvrtek 8. prosince. Pascal - opakování základů. Struktura programu:

Lekce 6 IMPLEMENTACE OPERAČNÍHO SYSTÉMU LINUX DO VÝUKY INFORMAČNÍCH TECHNOLOGIÍ JAZYK C

Obsah. Předmluva 13 Zpětná vazba od čtenářů 14 Zdrojové kódy ke knize 15 Errata 15

Pole a Funkce. Úvod do programování 1 Tomáš Kühr

Formátové specifikace formátovací řetězce

Jak v Javě primitivní datové typy a jejich reprezentace. BD6B36PJV 002 Fakulta elektrotechnická České vysoké učení technické

Základní pojmy. Úvod do programování. Základní pojmy. Zápis algoritmu. Výraz. Základní pojmy

Úvod do programovacích jazyků (Java)

Data v počítači. Informační data. Logické hodnoty. Znakové hodnoty

Programování v jazyce C pro chemiky (C2160) 3. Příkaz switch, příkaz cyklu for, operátory ++ a --, pole

Vyhledávání. doc. Mgr. Jiří Dvorský, Ph.D. Katedra informatiky Fakulta elektrotechniky a informatiky VŠB TU Ostrava. Prezentace ke dni 21.

Úvod do programování. Lekce 1

Operátory, výrazy. Tomáš Pitner, upravil Marek Šabo

Základy programování (IZP)

Úvod do programování 7. hodina

Pokročilé programování v jazyce C pro chemiky (C3220) Operátory new a delete, virtuální metody

- jak udělat konstantu long int: L long velka = 78L;

2 Základní funkce a operátory V této kapitole se seznámíme s použitím funkce printf, probereme základní operátory a uvedeme nejdůležitější funkce.

Základy programování. Úloha: Eratosthenovo síto. Autor: Josef Hrabal Číslo: HRA0031 Datum: Předmět: ZAP

Základy programování (IZP)

Algoritmizace řazení Bubble Sort

Konstruktory a destruktory

[1] Determinant. det A = 0 pro singulární matici, det A 0 pro regulární matici

7. Datové typy v Javě

1.1 Struktura programu v Pascalu Vstup a výstup Operátory a některé matematické funkce 5

Funkce, podmíněný příkaz if-else, příkaz cyklu for

II. Úlohy na vložené cykly a podprogramy

Programování v C++, 2. cvičení

Základy programování (IZP)

Funkce, intuitivní chápání složitosti

Programování v jazyce C pro chemiky (C2160) 4. Textové řetězce, zápis dat do souboru

1. lekce. do souboru main.c uložíme následující kód a pomocí F9 ho zkompilujeme a spustíme:

Proměnná. Datový typ. IAJCE Cvičení č. 3. Pojmenované místo v paměti sloužící pro uložení hodnoty.

Práce s binárními soubory. Základy programování 2 Tomáš Kühr

Algoritmizace prostorových úloh

Základní datové typy, proměnné - deklarujeme předem - C je case sensitive rozlišuje malá a velká písmena v názvech proměnných a funkcí

Pointery II. Jan Hnilica Počítačové modelování 17

Lineární spojový seznam (úvod do dynamických datových struktur)

Rekurze. Jan Hnilica Počítačové modelování 12

Pole a kolekce. v C#, Javě a C++

Algoritmizace a programování

2 Datové typy v jazyce C

Cykly a pole

Algoritmizace a programování

Pokročilé programování v jazyce C pro chemiky (C3220) Třídy v C++

Standardní algoritmy vyhledávací.

Vyučovací hodina. 1vyučovací hodina: 2vyučovací hodiny: Opakování z minulé hodiny. Procvičení nové látky

Poslední nenulová číslice faktoriálu

Matice. Modifikace matic eliminační metodou. α A = α a 2,1, α a 2,2,..., α a 2,n α a m,1, α a m,2,..., α a m,n

- znakové konstanty v apostrofech, např. a, +, (znak mezera) - proměnná zabírá 1 byte, obsahuje kód příslušného znaku

Maturitní otázky z předmětu PROGRAMOVÁNÍ

Vyhledávání. doc. Mgr. Jiří Dvorský, Ph.D. Katedra informatiky Fakulta elektrotechniky a informatiky VŠB TU Ostrava. Prezentace ke dni 12.

Úvod do programovacích jazyků (Java)

Racionální čísla, operátory, výrazy, knihovní funkce

1. lekce. do souboru main.c uložíme následující kód a pomocí F9 ho zkompilujeme a spustíme:

Algoritmizace Dynamické programování. Jiří Vyskočil, Marko Genyg-Berezovskyj 2010

Struktura programu v době běhu

Iterační výpočty. Dokumentace k projektu č. 2 do IZP. 24. listopadu 2004

Ukazatel (Pointer) jako datový typ - proměnné jsou umístěny v paměti na určitém místě (adrese) a zabírají určitý prostor (počet bytů), který je daný

Výrazy a operátory. Operátory Unární - unární a unární + Např.: a +b

Da D to t v o é v ty t py IB111: Datové typy

Programování v C++ 1, 1. cvičení

KAPITOLA 9 - POKROČILÁ PRÁCE S TABULKOVÝM PROCESOREM

Algoritmizace a programování. Ak. rok 2012/2013 vbp 1. ze 44

Lekce 9 IMPLEMENTACE OPERAČNÍHO SYSTÉMU LINUX DO VÝUKY INFORMAČNÍCH TECHNOLOGIÍ JAZYK C

Př. další použití pointerů

Odvozené a strukturované typy dat

Více o konstruktorech a destruktorech

Příklad 1. Řešení 1a Máme vyšetřit lichost či sudost funkce ŘEŠENÉ PŘÍKLADY Z M1A ČÁST 3

Algoritmizace prostorových úloh

Obsah přednášky 7. Základy programování (IZAPR) Přednáška 7. Parametry metod. Parametry, argumenty. Parametry metod.

VÝUKOVÝ MATERIÁL. Bratislavská 2166, Varnsdorf, IČO: tel Číslo projektu

LEKCE 6. Operátory. V této lekci najdete:

MAXScript výukový kurz

1. Programování proti rozhraní

Algoritmizace prostorových úloh

Vzdálenost uzlů v neorientovaném grafu

VÝUKOVÝ MATERIÁL. Bratislavská 2166, Varnsdorf, IČO: tel Číslo projektu

4a. Makra Visual Basic pro Microsoft Excel Cyklické odkazy a iterace Makra funkce a metody

Úvod do programování - Java. Cvičení č.4

Martin Flusser. Faculty of Nuclear Sciences and Physical Engineering Czech Technical University in Prague. October 17, 2016

Funkce a lineární funkce pro studijní obory

Datové struktury 2: Rozptylovací tabulky

Algoritmy I. Číselné soustavy přečíst!!! ALGI 2018/19

Transkript:

MASARYKOVA UNIVERZITA FAKULTA INFORMATIKY Knihovna kombinatorických funkcí BAKALÁŘSKÁ PRÁCE Jiří Smejkal Brno, jaro 2010

Prohlášení Prohlašuji, že tato bakalářská práce je mým původním autorským dílem, které jsem vypracoval samostatně. Všechny zdroje, prameny a literaturu, které jsem při vypracování používal nebo z nich čerpal, v práci řádně cituji s uvedením úplného odkazu na příslušný zdroj. Vedoucí práce: RNDr. Aleš Zlámal ii

Poděkování Na tomto místě bych rád poděkoval vedoucímu této práce panu RNDr. Aleši Zlámalovi za vstřícnost a trpělivost. Dále pak mé rodině za podporu ve studiu a všem, kteří se se mnou podílili o své rady a zkušenosti. iii

Klíčová slova funkce, knihovna, kombinatorika, permutace, variace, kombinace, jazyk C iv

Shrnutí Cílem bakalářské práce je vytvořit kombinatorickou knihovnu pro programovací jazyk C, jejíž funkce mohou sloužit k programování problémů kombinatorické povahy. v

Obsah 1 Úvod............................................. 1 2 Konvence pro pojmenování funkcí v jazyce C..................... 2 2.1 Popis problematiky.................................. 2 2.2 Analýza stávajícího systému pojmenování funkcí................ 3 2.2.1 Trigonometrické funkce........................... 3 2.2.2 Exponenciální a logaritmické funkce.................... 4 2.2.3 Aritmetické funkce.............................. 4 2.3 Tvorba názvů funkcí pro kombinatorickou knihovnu.............. 5 2.3.1 Tvoření názvů podle funkcionality..................... 5 2.3.2 Zkratky pro typy parametrů a návratové hodnoty............ 5 2.3.3 Způsob řazení zkratek za sebe....................... 6 3 Funkce kombinatorické knihovny............................ 8 3.1 Permutace....................................... 8 3.1.1 Alokace permutace.............................. 9 3.1.2 Délka permutace............................... 11 3.1.3 Inicializace permutace typu int....................... 12 3.1.4 Inicializace permutace typu char...................... 12 3.1.5 Typ permutace................................ 13 3.1.6 Tisk permutace................................ 14 3.1.7 Tisk permutace na standardní výstup................... 15 3.1.8 Tisk permutace do proudu......................... 16 3.1.9 Generování následující permutace..................... 17 3.1.10 Získání permutace podle pořadí...................... 22 3.1.11 Kopírování permutace............................ 22 3.1.12 Porovnání permutací............................. 23 3.1.13 Skládání permutací.............................. 24 3.1.14 Cyklický posuv permutace......................... 26 3.1.15 Znaménko permutace............................ 28 3.1.16 Dealokace permutace............................ 29 3.1.17 Bitové permutace............................... 29 3.2 Variace a kombinace................................. 31 3.2.1 Počet všech variací, kombinací....................... 32 3.2.2 Alokace variace, kombinace......................... 33 vi

3.2.3 Parametry variace, kombinace....................... 34 3.2.4 Inicializace variace, kombinace typu int.................. 35 3.2.5 Inicializace variace, kombinace typu char................. 35 3.2.6 Tisk variace, kombinace na standardní výstup.............. 36 3.2.7 Tisk variace, kombinace do proudu.................... 37 3.2.8 Generování následující variace....................... 39 3.2.9 Generování následující kombinace..................... 43 3.2.10 Kopírování variace, kombinace....................... 47 3.2.11 Dealokace variace, kombinace....................... 47 4 Praktické ukázky...................................... 48 4.1 Problém obchodního cestujícího........................... 48 4.2 Výpočet determinantu matice............................ 51 4.3 Šifrování komunikace................................. 52 5 Závěr............................................. 57 Literatura............................................. 58 vii

Kapitola 1 Úvod Knihovny v programovacích jazycích jsou jejich velice důležitou součástí, nebot obsahují velké množství funkcí [1], které jsou programátorovi k dispozici. Ten nemusí vymýšlet algoritmy a funkce, které před ním již někdo vymyslel a implementoval, ale stačí je vyhledat v knihovně a nahrát do svého programu. Samotné nahrání je velice snadné, vystačíme si jediným příkazem a potřebné funkce jsou následně volně přístupné k použití. Mezi standardní knihovny zatím nebyla zařazena kombinatorická knihovna. Dávám si tedy za cíl vytvořit knihovnu, která bude ulehčovat práci s permutacemi, variacemi, kombinacemi a bude obsahovat některé funkce potřebné k programování úloh kombinatorické povahy. Za pomoci známých algoritmů implementuji funkce, které uživatel může použít a u nichž bude mít zaručenou jejich korektnost, nebot naprogramované funkce budou otestovány. Ve druhé kapitole se nachází popis již existujícího modelu standardní knihovny jazyka C, hlavně konvence pro pojmenování funkcí. V novějších jazycích je povoleno přetěžování funkcí (funkce dělající významově totéž, ale s jinými typy parametrů mající stejný název). Uživatel použije jeden název pro funkci, již volá a překladač rozhodne, který typ funkce se zavolá. Rozhodnutí je založeno v tomto případě na typech parametrů. V jazyce C je takováto shoda jmen nepřípustná [2]. Vyskytuje se tedy problém, jak utvořit názvy významově podobných funkcí. Název funkcí vznikne spojením zkratek z následujících věcí: funkcionality, vstupních parametrů a návratové hodnoty funkce. Zřetězením těchto zkratek dává někdy nejasný význam. Největším problémem je však nejednotnost systému, například jednou je typ vstupního parametru na začátku slova, kdežto jindy na konci. Proto zde analizuji starý systém pro kombinatorickou knihovnu a navrhuji nový, který je jednotný a zároveň se příliš neodlišuje od toho, na co už jsou uživatelé zvyklí. Tento kompromis je v ní použit. Třetí kapitola obsahuje výčet všech funkcí, které se nachází v naší kombinatorické knihovně. U každé z funkcí je popsáno k čemu slouží, jaké jsou parametry funkce a návratové hodnoty. Použití funkce je názorně ukázáno na krátkém programu využívající její funkcionalitu. Ve čtvrté kapitole se věnuji ukázkám, jak lze funkce z naší knihovny využít na praktických příkladech a závěr obsahuje celkové hodnocení knihovny s jejím možným rozšířením. 1

Kapitola 2 Konvence pro pojmenování funkcí v jazyce C 2.1 Popis problematiky Funkce v programovacích jazycích mají jména, která označují pamět ová místa a pomocí nichž uživatel k daným funkcím přistupuje, aniž by musel používat jejich adresy. V mnohých moderních jazycích je možné definovat několik funkcí se stejným jménem, pokud mají rozdílnou množinu parametrů (alespoň co se jejich množství a typů týče). Této vlastnosti se říká přetěžování funkcí. Když je zavolána přetížená funkce, překladač vybere správnou funkci podle počtu, typů a pořadí argumentů volané funkce [3]. Příklad přetížení funkcí: int fun(int a, int b) return 1; int fun(int a) return 2; int fun(double a) return 3; potom při volání funkce fun tímto způsobem: int x = fun(2, 3); se použije první definice fun kvůli volání funkce s dvěma argumenty, oběma typu int a při volání int x = fun(4.3); se použije třetí definice funkce fun, protože argument přetížené funkce je typu double. V jazyce C je takovéto kolizní pojmenování funkcí nepřípustné a proto je nutné názvy funkcí od sebe odlišit přidáním nějakých znakových řetězců, aby bylo zajištěno jejich různé pojmenování. Předchozí funkce by mohly vypadat v jazyce C třeba takto: int fun2i(int a, int b) return 1; int funi(int a) return 2; int fund(double a) return 3; kde i značí, že argument má být typu int, číslo 2 signalizuje požadavek dvou argumentů, zatímco d značí použití funkce s argumentem typu double. U takto vytvořených názvů funkcí je důležité, aby jejich používání bylo intuitivní a proto by měl v pojmenování funkcí být určitý systém. 2

2.2. ANALÝZA STÁVAJÍCÍHO SYSTÉMU POJMENOVÁNÍ FUNKCÍ Při vytváření názvů funkcí se budeme řídit těmito pravidly: vytvoření názvů podle funkcionality určení zkratky pro datové typy argumentů umístění zkratek označující datové typy argumentů zvolení implicitního typu argumentů 2.2 Analýza stávajícího systému pojmenování funkcí Cílem není vytvořit úplně nový a odlišný systém pojmenování, ale aby byl jednotný pro celou knihovnu a přesto se příliš nelišil od stávajících modelů, na které jsou uživatelé používající jazyk C zvyklí. Toho docílíme studií názvů existujících funkcí a odhalením jejich nedostatků se zaměřením hlavně na matematickou knihovnu <math.h>. 2.2.1 Trigonometrické funkce Ve standardní knihovně jazyka C existují funkce sin, cos, tan, acos, asin, atan, atan2. Všechny jsou pouze ve verzích s datovým typem double. Jejich rozšíření o práci s datovými typy float a long double jsou funkce z knihovny glibc. double sin (double x) Z názvu funkce je na první pohled zřejmé, že funkce počítá sin úhlu x vyjádřeného v radiánech. Výchozí typ parametru a návratové hodnoty je double, pokud není specifikováno jinak. float sinf (float x) Přidání f označuje, že se jedná o funkci pracující s datovým typem float. Analogicky výskyt zkratky l značí práci s datovým typem long double. Zkratka f je přidána na konec názvu funkce. 3

2.2. ANALÝZA STÁVAJÍCÍHO SYSTÉMU POJMENOVÁNÍ FUNKCÍ 2.2.2 Exponenciální a logaritmické funkce double exp (double x) Zkratka exp je běžně používána v matematické literatuře pro označení exponenciální funkce. Výchozí typ parametru a návratové hodnoty je double, takže zkratka doznačující datový typ double se neuvádí. float expf (float x) Zkratka f značí float a je umístěn na konci názvu, nic se tedy neliší od předchozí funkce sin. float logf (float x) Funkce logaritmu, která pracuje s typem float, což je specifikováno taktéž na konci. 2.2.3 Aritmetické funkce Ve standardní knihovně nalezneme pro výpočet absolutní hodnoty funkce: abs, labs a fabs pracující s typy int, long int a double, chybí pouze verze s float. int abs (int number) Funkce vrací absolutní hodnotu čísla zadaného jako parametr, proto zkratka abs (absolute). Výchozím typem je int. long int labs (long int number) Zkratka l značí long int právě kvůli výchozímu typu int, což je změna oproti předchozím funkcím, které měli výchozí typ double a proto l znamenalo long double. Výchozím typem je int. double fabs (double number) Oproti ostatním funkcím, které označují f jako float, zde f značí typ double. Tím nastává problém, jak označit float, viz následující funkce. Řešení: dabs nebo raději absd. 4

2.3. TVORBA NÁZVŮ FUNKCÍ PRO KOMBINATORICKOU KNIHOVNU float fabsf (float number) Kvůli nevhodnému použití f pro double u funkce fabs je přidáno ještě jedno f na konec. Takovéto řešení není příliš uživatelsky příjemné, bylo by vhodnější použít absf pro float a zároveň absd pro double. Příznak typu je lepší umístit za významové slovo abs, protože například u funkce cabsf, s prototypem float cabsf (complex float z), má tento příznak také na konci. 2.3 Tvorba názvů funkcí pro kombinatorickou knihovnu Po zanalyzovaní funkcí z matematické knihovny a vypozorování určitých vzorů, můžeme nyní pojmenovat funkce naší kombinatorické knihovny, čemuž se věnujeme v této podkapitole. 2.3.1 Tvoření názvů podle funkcionality Z názvu funkce musí být na první pohled patrný její význam, skládá se proto ze zkratky významového slova. Neuvádí se celý název, aby programátor nebyl nucen vypisovat dlouhý název funkce, ale pouze zkratka vystihující danou funkcionalitu. Podle prefixu každé funkce poznáme, zda funkce pracuje s permutacemi, variacemi nebo kombinacemi. U variací je zvolena předpona var (variation), která může být chápána jako proměnná (variable). Předpona var však nikdy nebude samotná, takže k záměně s variable nedojde. Permutace Variace Kombinace Faktoriál perm var comb fact Tabulka 2.1: Tabulka základních zkratek funkcí 2.3.2 Zkratky pro typy parametrů a návratové hodnoty K jednoznačnému označení funkce se do názvu přidávají informace identifikující datový typ, s kterým daná funkce pracuje. Permutace, variace nebo kombinace mohou obsahovat hodnoty s typem int, short int, long int, char a k nim ještě bez znaménkově chápané datové typy, jejichž hodnoty nenabývají záporných hodnot (unsigned int, unsigned short int, unsigned long int, 5

2.3. TVORBA NÁZVŮ FUNKCÍ PRO KOMBINATORICKOU KNIHOVNU Inicializace Alokace paměti Dealokace paměti Kopírování Výpis Zjištění velikosti Následující n-tice init alloc free copy print sizeof next Tabulka 2.2: Tabulka zkratek pro specifikace funkcí unsigned char). Reálné datové typy (float, double, long double) nebudeme v kombinatorické knihovně vůbec používat, vystačíme s typy celočíselnými. Jako intuitivní řešení se nabízí vzít počáteční písmena z datového typu. Například pro char by byla zkratka c, pro unsigned int zkratka ui a pro unsigned long int zkratka uli. Pokud jsou operandy typu char nebo int uvedeny bez typového specifikátoru signed nebo unsigned, předpokládá se použití typu signed char, resp. signed int. V případě kompilátoru ANSI C je potřeba ověřit, zda implicitně reprezentuje typ char jako signed char nebo unsigned char [4]. V naší kombinatorické knihovně je vhodnější použít všechny typy implicitně jako unsigned, jelikož permutace, variace nebo kombinace jsou v této knihovně inicializovány bud od nuly nebo od jedné a žádné záporné hodnoty tak nemohou obsahovat. Pro úplnost jsou implementovány i funkce se znaménkovými typy, které obsahují ve zkratce navíc písmeno s od klíčového slova signed. Z důvodu přehlednosti se vynechává uvádění implicitního datového typu int. Stejně jako se neuvádí u trigonometrické funkce sin zkratka pro double, nebo u aritmetické funkce pro výpočet absolutní hodnoty zkratka pro int, tak i zde se vynechává písmeno i pro funkce pracující s datovým typem int. V kombinatorické knihovně se přidávají do názvu funkce zkratky, které jsou znázorněny v tabulce 2.3. 2.3.3 Způsob řazení zkratek za sebe Otázkou je, jak seřadit zkratky za sebe, aby takto vytvořený název byl přehledný a bylo na první pohled zřejmé o jaký druh funkce se jedná. Ke zvýraznění a oddělení zkratek je možné použít způsob zápisu tzv. camelcase, který je používán jako konvence psaní různých identifikátorů v jazyce Java, PHP nebo Ruby. V jazyce C ovšem není tento způsob tvoření názvů používán, stejně jako použití podtržítka. Výsledný název tedy vznikne zřetězením významového slova (viz tabulka 2.1), bližší 6

2.3. TVORBA NÁZVŮ FUNKCÍ PRO KOMBINATORICKOU KNIHOVNU Datový typ Zkratka Poznámka int s signed (int) unsigned int short int sh signed short (int) unsigned short int h long int sl signed long (int) unsigned long int l char sc signed char unsigned char c Tabulka 2.3: Tabulka datových typů a jejich zkratek specifikace funkce (tabulka 2.2) a zkratky pro typ parametru funkce (tabulka 2.3). Například inicializace permutace tvořené bezznaménkovými znaky bude vypadat takto: perm + init + c. 7

Kapitola 3 Funkce kombinatorické knihovny V této kapitole jsou popsány všechny funkce kombinatorické knihovny, která má název comb.c. Funkce jsou děleny do tří sekcí: funkce pro permutace, variace a kombinace. Většina funkcí je naprogramována v 8 variantách, liší se datovým typem prvků v permutaci, které mohou být typu unsigned int, int, unsigned char, char, unsigned short, short, unsigned long a nebo long. Při použití každé z funkcí si můžeme vybrat mezi objektově orientovanou variantou nebo její alternativou v podobě klasického přístupu. Objektový přístup má uschovanou velikost permutace před samotnou permutací a přístup k ní je řešen funkcí permsizeof, kdežto u klasického přístupu je velikost permutace zadána jako další parametr funkce a do názvu funkce je pro odlišení přidáno písmeno n, které značí nutnost zadání délky permutace (například permninit). Popis funkce se skládá z obecného popisu, kde je uvedeno k čemu funkce slouží, případně obsahuje technické údaje k objasnění, jak je funkce implementována. Dále je uveden prototyp funkce, z něhož je patrný název, jsou popsány parametry a návratová hodnota funkce. K lepšímu pochopení je uvedena názorná ukázka v podobě krátkého programu s jeho výstupem. Při práci s permutacemi, variacemi nebo kombinacemi v jejich objektové variantě je nutné nejprve přidělit pamět funkcemi permalloc, varalloc a comballoc, dále inicializovat vzniklý objekt pomocí funkcí perminit, varinit, případně combinit a teprve potom s nimi lze manipulovat. 3.1 Permutace Permutací z n-prvkové množiny M nebo n-člennou permutací nazýváme každou uspořádanou n-tici navzájem různých prvků, vytvořenou z množiny prvků M. Počet všech permutací z n prvků: P(n) = 1. 2. 3..... n = n! [5]. 8

3.1. PERMUTACE Nebo můžeme permutace obecněji definovat tímto způsobem: Permutací množiny M nazýváme každé prosté zobrazení množiny M na množinu M. Faktoriálem čísla n nazýváme funkci F na množině všech nezáporných celých čísel, definovanou takto: F(0) = 1, F(n + 1) = (n + 1) F(n) (n N 0 ) Místo F(n) píšeme n!. Pro n 1 je n! = 1. 2... (n - 1) n [5]. 3.1.1 Alokace permutace Permutace je v paměti reprezentována jako jednorozměrné pole, k jehož alokaci využíváme funkce ze standardní knihovny jazyka C calloc. Alokovaný blok funkcí permalloc má velikost o jedna větší než je požadovaná velikost permutace zadaná jediným vstupním parametrem. První prvek pole je vyhrazen pro uložení velikosti permutace a všechny ostatní pro samotnou permutaci. Vrácen je ukazatel na první prvek permutace, který ve skutečnosti ukazuje až na druhý prvek alokovaného pole. Všechny bity alokovaného pole jsou inicializovány na nulu, jen první prvek pole obsahuje velikost permutace. U funkce permnalloc je pouze dynamicky alokováno pole s velikostí rovnou n. Velikost alokované permutace je omezena datovým typem prvků, které obsahuje, což znázorňuje tabulka 3.1, která udává ke každé funkci maximální počet prvků permutace. U permutací typu unsigned int nebo unsigned long je ještě snížen rozsah na polovinu kvůli použití vstupního parametru typu int, resp long. Alokovat větší permutace stejně není zatím fyzicky možné, protože takovéto permutace by vyžadovaly až 17 GB operační paměti. Při požadavku o alokaci větší permutace než je povoleno, nedochází k přidělení paměti, ale dojde k nastavení externí proměnné errno [6] na hodnotu ERANGE. Název funkce: unsigned int *permalloc(int n) unsigned int *permnalloc(int n) Parametry: Parametr n je celočíselná hodnota, která určí velikost alokované permutace. 9

Návratová hodnota: 3.1. PERMUTACE Ukazatel na blok alokované paměti, typ ukazatele záleží na použité funkci (například permalloc vrací ukazatel na unsigned int, permallocsl zase ukazatel na long int). Není-li z nějakého důvodu možné pamět přidělit, funkce vrací nulový ukazatel. #include<stdio.h> #include "comb.h" int main() unsigned int *permutace, n = 10, size = 0; permutace = permalloc(n); size = permsizeof(permutuce); printf("velikost permutace je %d a zabira v pameti %d B.\n", size,1+size*sizeof(unsigned int)); permfree(permutace); return 0; Příklad 3.1.1: Příklad použití funkce permalloc Funkce Prvky datového typu Maximální počet prvků permutace permalloc unsigned int 32 767 nebo 2 147 483 647 1 permallocs int 32 767 nebo 2 147 483 647 1 permallocc unsigned char 256 permallocsc char 128 permallocl unsigned long 2 147 483 647 permallocsl long int 2 147 483 647 permalloch unsigned short 65 536 permallocsh short int 32 768 permnalloc unsigned int 32 767 nebo 2 147 483 647 1 permnallocs int 32 767 nebo 2 147 483 647 1 permnallocc unsigned char 256 permnallocsc char 128 1. podle architektury 2B nebo 4B 10

3.1. PERMUTACE Funkce Prvky datového typu Maximální počet prvků permutace permnallocl unsigned long 2 147 483 647 permnallocsl long int 2 147 483 647 permnalloch unsigned short 65 536 permnallocsh short int 32 768 Tabulka 3.1: Omezení velikosti permutace datovým typem 3.1.2 Délka permutace Funkce permsizeof slouží k zjištění velikosti permutace. Velikost je uložena před prvním prvkem permutace, takže je možné délku permutace zpřístupnit indexem -1. Tento přístup se nedoporučuje kvůli reprezentaci největší možné velikosti permutace uložené na tomto místě. Všechny typy bez znaménka používají přímou binární notaci, bez ohledu na to, zda typy bez znaménka používají dvojkovou doplňkovou notaci, jedničkovou doplňkovou notaci nebo přímou notaci. Znaménkový bit se považuje za normální datový bit. To znamená, že slovo o n bitech může reprezentovat celá čísla od 0 do 2 n -1 [6]. Pro uložení velikosti permutace 2 n tam, kde je povoleno zapsat slovo maximálně o velikosti 2 n -1 se využije hodnoty 0, která je funkcí permalloc nastavena pouze v případě hodnoty 2 n. Funkce permsizeof nulu automaticky reprezentuje jako číslo o jedna větší než je horní hranice rozsahu daného typu. Například pole datového typu char může obsahovat pouze hodnoty v rozmezí 0 až 255. Pro velikost permutace 256, kterou není možné v proměnné daného typu zobrazit, značí nula v poli na indexu -1 právě velikost permutace rovnou 256. Název funkce: int permsizeof(unsigned int *perm) Parametry: Parametr perm je ukazatel na první prvek permutace. Návratová hodnota: Funkce permsizeof vrací počet prvků permutace, která byla vytvořena funkcí permalloc. Pokud má permutace nulovou délku nebo vstupní parametr má hodnotu nulového ukazatele, je vrácena 0. 11

3.1. PERMUTACE 3.1.3 Inicializace permutace typu int Alokované místo v paměti funkcí permalloc obsahuje prvky pole rovné nule. K nastavení počátečních hodnot permutace slouží funkce perminit, která vytvoří identickou permutaci. Pro matematické účely je k dispozici funkce perminit1, ta nastaví identickou permutaci od jedné, kdežto u funkce perminit je počátečním prvkem permutace nula. Pro inicializaci permutace od nuly lze použít její modifikaci perminitex (jako exclusive), u které dojde k vytknutí vybraného prvku permutace a k celkovému zmenšení velikosti o jedna. Funkce má dva parametry, první je ukazatel na permutaci a druhý je vyjmutý prvek. 0 1 2 3 4 n 1 0 1 2 3 4 n 1 1 2 3 4 5 n 1 2 3 4 5 n Název funkce: void perminit(unsigned int *perm) void permninit(unsigned int *perm, int n) void perminit1(unsigned int *perm) void permninit1(unsigned int *perm, int n) void permninitex(unsigned int *perm, int t) void permninitex(unsigned int *perm, int t, int n) Parametry: Parametr perm je ukazatel na permutaci, která má být inicializována. Parametr t je prvek, který bude z permutace vytknut. Návratová hodnota: Žádná. 3.1.4 Inicializace permutace typu char Permutace obsahující znaky lze nastavit pomocí libovolného znaku. Ten pak bude prvním prvkem permutace a na ostatních pozicích permutace budou znaky s ordinální hodnotou o jedna větší než má jeho předchůdce. Název funkce: void perminitc(unsigned char *perm, char init) void permninitc(unsigned char *perm, char init, int n) 12

3.1. PERMUTACE Parametry: Parametr perm je ukazatel na permutaci, která má být inicializována. Znaková konstanta init je počátečním prvkem permutace. Návratová hodnota: Žádná. #include<stdio.h> #include "comb.h" int main() unsigned char *permutace, n = 8; permutace = permallocc(n); printf("znakova permutace zacinajici znakem a : \n"); perminitc(permutace, a ); permprintc(permutace); printf("\nznakova permutace zacinajici znakem H : \n"); perminitc(permutace, H ); permprintc(permutace); permfreec(permutace); return 0; Příklad 3.1.2: Příklad použití funkce perminitc Výstup ukázkového příkladu: Znakova permutace zacinajici znakem a : [a, b, c, d, e, f, g, h] Znakova permutace zacinajici znakem H : [H, I, J, K, L, M, N, O] 3.1.5 Typ permutace Tato pomocná funkce nám zjistí hodnotu nejmenšího prvku, který se v dané permutaci vyskytuje. Po alokovaní místa v paměti pro uložení permutace a následném nastavení 13

3.1. PERMUTACE hodnot, může permutace obsahovat prvky od jedné nebo od nuly. Typem permutace se zde rozumí právě toto nastavení. Název funkce: int permtype(unsigned int *perm) int permntype(unsigned int *perm, int n) Parametry: Parametr perm je ukazatel na první prvek permutace. Návratová hodnota: Funkce permtype vrací hodnotu 0, pokud je permutace inicializována od nuly, hodnotu 1 v případě inicializace permutace od jedné. V ostatních případech vrací funkce hodnotu -1. 3.1.6 Tisk permutace Permutaci lze vypsat přímo na standardní výstup nebo je možné ji zapsat do specifikovaného proudu. Formát výpisu číselné permutace je dán jednou ze tří funkcí: permprint2line, permprint nebo permprintcycle. Pro výpis znakové permutace existuje pouze jedna varianta, která vypisuje permutaci jako sekvenci prvků (funkce permprintc). Zápis permutace tabulkou: Funkce permprint2line zobrazuje permutaci konečné množiny S jako tabulku se dvěma řádky tak, jak se to obvykle v matematice píše. V prvním je seznam elementů množiny S a na druhém se nachází obrazy každého z elementů. 0 1 2 3 4 5 6 7 0 7 1 6 4 5 3 2 Permutace reprezentovaná polem: Alternativní zobrazení permutace je zjednodušením předchozího výpisu, kde jsou vypsány pouze obrazy elementů (druhý řádek tabulky). Tato reprezentace permutace znázorňuje způsob uložení elementů permutace v poli. První řádek tabulky (vzory elementů permutace) je skryt pod indexy prvků pole. [ ] 0 7 1 6 4 5 3 2 14

3.1. PERMUTACE Zápis permutace pomocí nezávislých cyklů: Další způsob zápisu permutací je pomocí nezávislých cyklů. Cykly jsou uzavřeny v kulatých závorkách a vzniknou následujícím způsobem: začneme výběrem některého elementu x z S takového, že π(x) x a zapisujeme sekvenci (x π(x) π(π(x))...) následných obrazů π, dokud obraz nebude x. Množina zapsaných hodnot tvoří orbit elementu x a uzávorkovaný výraz nám udává odpovídající cyklus. Dále pokračujeme výběrem elementu y z S, který nebyl doposud zapsán v žádném z orbitů a zároveň π(y) y, zapíšeme odpovídající cyklus, a tak dále dokud všechny elementy z množiny S nepatří do nějakého cyklu, nebo nejsou pevnými body 1. Definice. Každou permutaci π S n lze rozložit na součin nezávislých cyklů délky alespoň 2. Tento rozklad je až na pořadí činitelů jednoznačný. Zápis výše uvedené permutace pomocí nezávislých cyklů: ( ) ( 1 7 2 ) 3 6 Poznámka: v cyklu nejdříve vypisujeme jeho minimální prvek. 3.1.7 Tisk permutace na standardní výstup Zapíše permutaci v daném formátu na standardní výstup (stdout). Název funkce: void permprint2line(unsigned int *perm) void permnprint2line(unsigned int *perm, int n) void permprint(unsigned int *perm) void permnprint(unsigned int *perm, int n) void permprintcycle(unsigned int *perm) void permnprintcycle(unsigned int *perm, int n) Parametry: Parametr perm je ukazatel na permutaci, která má být vytištěna na standardní výstup. Návratová hodnota: Žádná. 1. elementy permutace, které zůstávají na místě (π(x) = x) 15

3.1. PERMUTACE #include<stdio.h> #include "comb.h" int main() unsigned int *permutace, n = 8; permutace = permget(n,4433); // vytvoreni permutace: [0,7,1,6,4,5,3,2] printf("vypis permutace tabulkou: \n"); permprint2line(permutace); printf("\nzjednoduseny vypis permutace: \n"); permprint(permutace); printf("\npermutace vypsana pomoci nezavislych cyklu: \n"); permprintcycles(permutace); permfree(permutace); return 0; Příklad 3.1.3: Příklad použití funkce permprint Výstup ukázkového příkladu: Vypis permutace tabulkou: (0 1 2 3 4 5 6 7) (0 7 1 6 4 5 3 2) Zjednoduseny vypis permutace: [0, 7, 1, 6, 4, 5, 3, 2] Permutace vypsana pomoci nezavislych cyklu: (1 7 2)(3 6) 3.1.8 Tisk permutace do proudu Funkce umožňující uživateli zápis permutace do specifikovaného proudu. Mimo permutace lze do proudu zapsat i textový řetězec, který bude umístěn před výpis permutace. 16

3.1. PERMUTACE Název funkce: int permfprint2line(file * stream, char * text, unsigned int *perm) int permfprint(file * stream, char * text, unsigned int *perm) int permfprintcycle(file * stream, char * text, unsigned int *perm) int permnfprint2line(file * stream, char * text, unsigned int *perm, int n) int permnfprint(file * stream, char * text, unsigned int *perm, int n) int permnfprintcycle(file * stream, char * text, unsigned int *perm, int n) Parametry: První parametr stream je ukazatel na objekt FILE, který identifikuje proud pro výpis permutace. Druhý parametr text tvoří uvozovací textový řetězec před výpisem permutace. Parametr perm je ukazatel na permutaci, která má být vytištěna do určeného proudu. Návratová hodnota: Počet znaků, které se posílají do výstupního proudu, pokud nenastane chyba. V případě výskytu chyby je vrácena hodnota EOF, která má ve většině tradičních implementacích hodnotu -1. 3.1.9 Generování následující permutace Funkce permnext změní obsah permutace zadané jako jediný parametr tak, že výsledná permutace je lexikograficky větší. Zároveň neexistuje permutace, která by byla větší než původní, ale menší než nově vytvořená. Tato vlastnost nám umožní generovat všechny možné permutace v případě volání funkce na jednotkovou permutaci. Permutace jsou vytvářeny dokud funkce nevrátí hodnotu nula (neexistuje už žádná větší permutace). Řešením, jak vytvořit všech n! permutací se zabýval Trotter [7], který uspořádal jednotlivé transpozice do sekvence tak, aby každé dva prvky, které mají být transponovány byly sousedící. Jiný přístup zvolil Wells, jeho metoda spočívá ve vytvoření nové permutace z předchozí. Stejného principu využívám i v této práci s tím rozdílem, že vytvořené permutace jsou lexikograficky uspořádané. K vytvoření minimálního lexikografického následníka je potřeba nalézt dva prvky, které zaměníme a seřadit určitou podmnožinu prvků permutace. Časová složitost problému seřazení posloupnosti o n prvcích je Θ(n log n). Když ale využijeme skutečnosti, že 17

3.1. PERMUTACE #include<stdio.h> #include "comb.h" int main() unsigned int *permutace, n = 8; FILE * pfile = NULL; pfile = fopen("permfprint_example.txt","w"); permutace = permalloc(n); perminit(permutace); permfprint(pfile,"identicka permutace: ", permutace); permfprint(pfile,"", permutace); fclose(pfile); permfree(permutace); return 0; Příklad 3.1.4: Příklad použití funkce permfprint řazená posloupnost je již seřazena sestupně, dosáhneme rychlejších výsledků. K obrácenému seřazení totiž stačí provést reverzi, což znamená prohodit první prvek s posledním, druhý s předposledním atd. Tím se nám časová složitost sníží na n/2. Stále je však otázkou, jak nalézt prvky, které mají být prohozeny. Řešení tohoto problému je popsáno v sekci: Popis algoritmu. Název funkce: int permnext(unsigned int *perm) int permnnext(unsigned int *perm, int n) 18

Parametry: 3.1. PERMUTACE Parametr perm je ukazatel na permutaci, ze které se bude vytvářet následující permutace. Návratová hodnota: Pokud existuje lexikograficky větší permutace, než která byla zadána jako vstupní parametr, je vrácena hodnota 1, v opačném případě hodnota 0. Zdrojový kód: int permnext(unsigned int *p) int i, dm, hm, pos, ord = 1; if( p == NULL) return 0; for( pos = permsizeof(p) - 1; pos > 0; pos-- ) for( i = pos - 1; i >= 0 && ord; i-- ) if( p[i] < p[pos] ) /* swap */ p[i] ^= p[pos]; p[pos] ^= p[i]; p[i] ^= p[pos]; /* reverse */ hm = permsizeof(p) - 1; for( dm = i + 1; dm < hm; dm++,hm-- ) p[hm] ^= p[dm]; p[dm] ^= p[hm]; p[hm] ^= p[dm]; return 1; else if( p[i] < p[i+1] ) ord = 0; ord = 1; return 0; 19

3.1. PERMUTACE Použité proměnné: Proměnná pos je index prvku pole, jenž má být prohozen s jiným prvkem tak, aby došlo k přechodu na následující permutaci. Pod indexem i hledáme v poli menší prvek než je na pozici pos. Při nalezení této dvojice jsou prvky s indexy i a pos vzájemně prohozeny. Indexy dm a hm označují počáteční a koncový úsek pole, který bude seřazen vzestupně. Hodnota proměnné ord udává, že posloupnost prvků za pozicí s indexem i je sestupně uspořádaná a tudíž můžeme prvky na indexech i a pos prohodit, aniž by došlo k vzniku nesprávné permutace. Popis algoritmu: K vzniku nové permutace hledáme dvojici prvků, které prohodíme. Aby vznikl lexikografický následník, zaměníme větší prvek umístěný v pozici blíže ke konci, s prvkem menším v pozici blíže začátku. Nalezení dvojice prohazovaných prvků probíhá následujícím způsobem: Začínáme porovnávat od posledního prvku s jeho předchůdci. Při hledání menšího prvku si kontrolujeme, zda se za ním nachází sestupně seřazená posloupnost. Pokud tomu tak není, prohození nelze provést, protože by došlo k přeskočení několika možností a při generování všech n! permutací by výsledkem byla jen určitá podmnožina z celkového počtu. Tuto situaci názorně ilustruje následující obrázek, ve kterém nedochází k prohození prvku 4 s prvním menším prvkem 3 (5 < 6 > 4), nebot bychom přeskočili menší permutace: [1, 2, 3, 6, 4, 5] a [1, 2, 3, 6, 5, 4]. Prvek 3 na této pozici můžeme zaměnit s prvkem 4 až v okamžiku, kdy je splněna podmínka seřazené sekvence prvků (6 > 5 > 4) viz obrázek. 20

3.1. PERMUTACE Pokud nemůžeme provést prohození prvků at už z důvodu neexistence menšího prvku nebo kvůli neseřazené posloupnosti prvků za nalezeným menším prvkem, posuneme se o jednu pozici doleva a opakujeme porovnávání s předchůdci prvku na této nové pozici. Jakmile můžeme prvky bez problému prohodit, učiníme tak a uspořádáme sekvenci následovanou za menším z nalezených dvou prvků (prvek v pozici blíže začátku). Tato sekvence v případě prohození bude vždy sestupně uspořádaná, jinak nelze prvky prohodit. K setřídění posloupnosti tedy stačí provést reverzi prvků a výsledkem je minimální lexikografický následník zadané permutace. Časová složitost: Počet porovnání je v nejhorším případě roven n 1 i=1 (n i) kde n 1. Nejvíce záměn prvků nastane v případě prohození prvku na první pozici s jakýmkoliv jiným prvkem, pak je seřazena celá posloupnost následovaná za prvním prvkem. Počet prohození je tedy: 1 + (n 1) / 2. #include<stdio.h> #include "comb.h" int main() unsigned int *permutace, n = 4; permutace = permalloc(n); perminit1(permutace); do permprint(permutace); while( permnext(permutace) ); return 0; Příklad 3.1.5: Příklad použití funkce permnext Výstup ukázkového příkladu: [1, 2, 3, 4] [2, 1, 3, 4] [3, 1, 2, 4] [4, 1, 2, 3] [1, 2, 4, 3] [2, 1, 4, 3] [3, 1, 4, 2] [4, 1, 3, 2] [1, 3, 2, 4] [2, 3, 1, 4] [3, 2, 1, 4] [4, 2, 1, 3] [1, 3, 4, 2] [2, 3, 4, 1] [3, 2, 4, 1] [4, 2, 3, 1] 21

3.1. PERMUTACE [1, 4, 2, 3] [2, 4, 1, 3] [3, 4, 1, 2] [4, 3, 1, 2] [1, 4, 3, 2] [2, 4, 3, 1] [3, 4, 2, 1] [4, 3, 2, 1] 3.1.10 Získání permutace podle pořadí K získání permutace, která je vytvořena podle lexikografického uspořádání v požadovaném pořadí slouží funkce permget. K výpočtu je využita funkce permnext, která generuje permutace do té doby, než je vytvořena správná permutace. Název funkce: unsigned int *permget(int n, int s) unsigned int *permnget(int n, int s) Parametry: Parametr n udává velikost požadované permutace. Podle parametru s se vygeneruje v pořadí s-tá permutace. Návratová hodnota: Ukazatel na nově vytvořenou permutaci. 3.1.11 Kopírování permutace V případě potřeby vytvořit kopii permutace lze využít funkce permcopy. Při jejím zavolání dojde k alokaci paměti, zkopírování všech prvků původní permutace do nově alokovaného bloku a následně je vrácena adresa zkopírované permutace. Název funkce: unsigned int *permcopy(unsigned int *perm) unsigned int *permncopy(unsigned int *perm, int n) Parametry: Parametr perm je ukazatel na permutaci, z níž se bude vytvářet kopie. Návratová hodnota: Ukazatel na nově vytvořenou kopii původní permutace. 22

3.1. PERMUTACE #include<stdio.h> #include "comb.h" int main() unsigned int *permutace, *kopie, n = 8; permutace = permalloc(n); perminit(permutace); kopie = permcopy(permutace); // zaloha permutace permnext(permutace); // vytvoreni nasledujici permutace printf("nove vytvorena permutace (naslednik puvodni): \n"); permprint(permutace); printf("\npredchudce nove vytvorene permutace: \n"); permprint(kopie); permfree(permutace); permfree(kopie); return 0; Příklad 3.1.6: Příklad použití funkce permcopy Výstup ukázkového příkladu: Nove vytvorena permutace (naslednik puvodni): [0, 1, 2, 3, 4, 5, 7, 6] Predchudce nove vytvorene permutace: [0, 1, 2, 3, 4, 5, 6, 7] 3.1.12 Porovnání permutací Pro porovnání permutací zavedeme pojem lexikografického uspořádání (slovníkové řazení). Jde o matematický pojem z oboru teorie uspořádání, který formalizuje vlastnosti uspořádání "podle abecedy"[8]. Definice: Lexikografické uspořádání na uspořádáné n-tici X n = [a 1,a 2,...,a n ] : a i X je definováno vztahem [a 1,a 2,...,a n ] Le [b 1,b 2,..., b n ] (a 1 b 1 ) (a 1 = b 1 a 2 b 2 ) (a 1 = b 1 a 2 = b 2 a 3 b 3 )... (a 1 = b 1 a 2 = b 2... a n-1 = b n-1 a n b n ) 23

3.1. PERMUTACE Název funkce: int permcmp(unsigned int *p, unsigned int *q) int permncmp(unsigned int *p, unsigned int *q, int n) Parametry: Parametr p a q jsou ukazatele na porovnávané permutace. Návratová hodnota: Číslo -1, 0, 1, je-li p lexikograficky menší, rovno, větší než q. 3.1.13 Skládání permutací Násobení permutací není v obecném případě komutativní operace, proto rozlišujeme mezi skládáním zleva, či zprava. Ke složení dvou permutací zleva slouží funkce permcompl, permutace lze složit i zprava funkcí permcompr. U komutativních permutací nezáleží, kterou z výše uvedených funkcí použijeme. Zjištění komutativity permutací provádí funkce permiscommutative. Název funkce: int *permcompl(unsigned int *l, unsigned int *r) int *permncompl(unsigned int *l, unsigned int *r, int n) int *permcompr(unsigned int *l, unsigned int *r) int *permncompr(unsigned int *l, unsigned int *r, int n) Parametry: Parametry l a r jsou ukazatele na skládané permutace. Návratová hodnota: Ukazatel na permutaci, která vznikla jako součin dvou permutací. Pokud nelze složit zadané permutace, je vrácen nulový ukazatel. 24

3.1. PERMUTACE #include<stdio.h> #include "comb.h" int main() unsigned int *P = NULL, *Q = NULL, n = 8; unsigned int *S_zleva = NULL, *S_zprava = NULL; P = permget(n,9157); Q = permget(n,32859); printf("\np: \n"); permprint2line(p); printf("\nq: \n"); permprint2line(q); S_zleva = permcompl(p, Q); printf("\ns = P * Q : \n"); permprint2line(s_zleva); S_zprava = permcompr(p, Q); printf("\ns = Q * P : \n"); permprint2line(s_zprava); permfree(p); permfree(q); permfree(s_zleva); permfree(s_zprava); return 0; Příklad 3.1.7: Příklad použití funkce permcompl a permcompr Výstup ukázkového příkladu: P: Q: (0 1 2 3 4 5 6 7) (0 1 2 3 4 5 6 7) (1 6 5 2 4 0 7 3) (6 3 4 7 0 2 5 1) S = P * Q : (0 1 2 3 4 5 6 7) (3 5 2 4 0 6 1 7) S = Q * P : (0 1 2 3 4 5 6 7) (7 2 4 3 1 5 0 6) 25

3.1. PERMUTACE 3.1.14 Cyklický posuv permutace Zavoláním funkce permshiftl na permutaci dojde k cyklickému posunu prvků o k pozic doleva. Pro cyklický posuv elementů permutace doprava slouží funkce permshiftr. Obrázek níže ilustruje posouvání prvků v permutaci naší funkcí. Jak už z názvu vyplývá, jde o cyklické posuny. Na obrázku cyklicky posouváme osmiprvkovou posloupnost o dvě místa doprava a při tom potřebujeme vykonat dva nezávislé cyklické posuny. Při každém z nich si do cyklu zakomponujeme pomocnou proměnnou, aby nedošlo ke ztrátě informace o jednom z prvků. Tento algoritmus je efektivnější, než kdybychom si vytvořili celou kopii permutace, a její prvky pak vkládali na ty správné pozice. Vyžadovalo by to dvojnásobnou pamět, n přiřazení do kopírované permutace a následných n vložení zpátky. My zde potřebujeme pouze jednu pomocnou proměnnou, do které přiřazujeme prvky pouze tolikrát, kolik je potřeba vykonat cyklů. U permutací délky 2, 3, 5, 7, 11, 13, atd. (velikosti jsou prvočísla) tvoří posun o libovolný počet vždy jen jeden cyklus a tedy jedno přiřazení navíc, celkový počet přiřazení je v tomto případě rovno n + 1. Název funkce: void permshiftl(unsigned int *perm, int k) void permnshiftl(unsigned int *perm, int k, int n) void permshiftr(unsigned int *perm, int k) void permnshiftr(unsigned int *perm, int k, int n) Parametry: Parametr perm je ukazatel na permutaci, kterou cyklicky posuneme o určitý počet míst doleva, či doprava. Parametr k specifikuje počet míst, o kolik se posune každý prvek permutace. Návratová hodnota: Žádná. 26

3.1. PERMUTACE #include<stdio.h> #include "comb.h" int main() unsigned int *permutace = NULL, n = 8, k = 1; permutace = permalloc(n); perminit1(permutace); printf("posun o %d misto doprava: \n",k); permprint(permutace); permshiftr(permutace, k); permprint(permutace); perminit1(permutace); printf("\nposun o %d misto doleva: \n",k); permprint(permutace); permshiftl(permutace, k); permprint(permutace); permfree(permutace); return 0; Příklad 3.1.8: Příklad použití funkce permshiftl a permshiftr 27

3.1. PERMUTACE Výstup ukázkového příkladu: Posun o 1 misto doprava: [1, 2, 3, 4, 5, 6, 7, 8] [8, 1, 2, 3, 4, 5, 6, 7] Posun o 1 misto doleva: [1, 2, 3, 4, 5, 6, 7, 8] [2, 3, 4, 5, 6, 7, 8, 1] 3.1.15 Znaménko permutace Říkáme, že permutace množiny U n = 1, 2,..., n má inverzi, právě když existují takové indexy i, j (i < j), že k i > k j (i, j U n ). Označíme-li v(p) počet všech inverzí v permutaci P, pak permutaci P nazýváme sudou, resp. lichou, právě když v(p) je sudé, resp. liché číslo. Znaménko [ signum ] permutace P je číslo z(p) = ( 1) v(p) [5]. Příklad: Inverze permutace [3, 1, 2, 5, 4] jsou vyznačeny obloučkem. Název funkce: int permsgn(unsigned int *perm) int permnsgn(unsigned int *perm, int n) Parametry: Parametr perm je ukazatel na permutaci, u které zkoumáme její znaménko. Návratová hodnota: Je-li permutace sudá, je vrácena hodnota 1, v případě lichosti permutace je vrácena hodnota -1. Výstup ukázkového příkladu: Znamenko permutace: [1, 2, 3, 4, 5] je +1. Znamenko permutace: [1, 2, 3, 5, 4] je -1. 28

3.1. PERMUTACE #include<stdio.h> #include "comb.h" int main() unsigned int *permutace = NULL, n = 5; permutace = permalloc(n); perminit1(permutace); printf("znamenko permutace: [1, 2, 3, 4, 5] je %+d.\n", permsgn(permutace)); permnext(permutace); printf("znamenko permutace: [1, 2, 3, 5, 4] je %+d.\n", permsgn(permutace)); return 0; Příklad 3.1.9: Příklad použití funkce permsgn 3.1.16 Dealokace permutace Funkce permfree uvolní pamět, která byla přidělena funkcí permalloc. Při volání obyčejné funkce free na alokovanou permutaci se neuvolní kus paměti (obsahující údaj o velikosti permutace), který je umístěn před polem. Název funkce: void permfree(unsigned int *perm) Parametry: Parametr perm je ukazatel na permutaci, která má být z paměti uvolněna. Návratová hodnota: Žádná. 3.1.17 Bitové permutace Mezi permutace můžeme zařadit i ty bitové, kde dochází k permutaci jednotlivých bitů, kdežto u obyčejných permutací jsou permutovány celé prvky pole. K tomuto účelu slouží funkce bitperm, která bere jako první vstupní parametr prvek, jehož bity budou následně permutovány permutací zadanou jako druhý parametr. Funkce bitpermb nám vrátí permutovaný prvek funkcí bitperm do původní podoby. 29

3.1. PERMUTACE Pro permutování bitů se využívá dvou bitových operátorů, bitového součinu (AND) a bitové nonekvivalence (XOR). Pomocí bitového součinu se namapujeme postupně na každý bit vstupních dat a ten pak bitovou nonekvivalencí změníme, pokud je to možné (nemůže nám vzniknou bitový vzorek s jiným počtem jedniček a nul, než jaký byl u původního vzorku). Bity, které budou změněny, udává rozdíl každé dvojice sousedících prvků ze zadané permutace a pozice jedniček a nul v původním vzorku. Název funkce: int bitperm(int b, int *perm) int bitpermn(int b, int *perm, int n) int bitpermb(int b, int *perm) int bitpermbn(int b, int *perm, int n) Parametry: Parametr b je prvek, jehož bity chceme permutovat. Parametr perm je ukazatel na permutaci, podle které jsou bity prvku b permutovány. Pokud chceme permutovat všechny bity vstupního údaje, musí mít permutace stejné množství prvků jako je počet bitů prvku b. Přístupné velikosti permutace datového typu int jsou: 8, 16, 24 a 32. Podle těchto velikostí dojde k permutaci spodních 8, 16, 24 nebo všech 32 bitů vstupního prvku. Při jiných velikostech nedochází k žádné permutaci bitů. Návratová hodnota: Prvek b po permutaci jeho bitů. Výstup ukázkového příkladu: Permutace p1 = [3, 7, 2, 6, 1, 5, 0, 4] Permutaci bitu cisla 164 (10100100) pomoci p1 vzniklo cislo 52 (00110100). Permutace p2 = [7, 5, 3, 1, 6, 4, 2, 0] Permutaci bitu cisla 164 (10100100) pomoci p2 vzniklo cislo 25 (00011001). 30

3.2. VARIACE A KOMBINACE #include<stdio.h> #include "comb.h" int main() int p1[]=3,7,2,6,1,5,0,4; int p2[]=7,5,3,1,6,4,2,0; unsigned int b, n =8; printf("permutace p1 = "); permnprints(p1,n); b = 164; printf("permutaci bitu cisla %d (10100100) pomoci p1 vzniklo cislo %d (00110100).\n", b, bitpermn(b, p1, n)); printf("\npermutace p2 = "); permnprints(p2,n); printf("permutaci bitu cisla %d (10100100) pomoci p2 vzniklo cislo %d (00011001).\n", b, bitpermn(b, p2, n)); return 0; Příklad 3.1.10: Příklad použití funkce bitpermn 3.2 Variace a kombinace Variace i kombinace jsou k-tice vybírané z n prvků. Jejich struktura v paměti se liší jen nepatrně, u kombinací jde o jednorozměrné pole obsahující k prvků, kdežto u variací je do pole zařazeno i zbylých n-k nevybraných prvků, umístěných v poli až za vybranou k-ticí. Základní operace s nimi mají stejnou implementaci, ale používají rozdílná jména funkcí (prefix var pro práci s variacemi, prefix comb jde-li o kombinace). Dvojí pojmenování je zvoleno kvůli tomu, aby bylo na první pohled zřejmé o jakou formu k-tice jde (zda-li v ní záleží na pořadí, či nikoliv). Variací k-té třídy z n-prvkové množiny nebo variací k-té třídy z n-prvků nazýváme každou uspořádanou k-tici navzájem různých prvků, vytvořenou z n-prvkové množiny, tj. každý prvek z daných n prvků se v jedné variaci může vyskytnout právě jednou. Počet všech variací k-té třídy z n-prvkové množiny: V(k, n) = n(n 1) (n 2)...(n k + 1) = n! / (n k)!. Kombinací k-té třídy z n-prvkové množiny nebo kombinací k-té třídy z n prvků nazýváme každou k-prvkovou podmnožinu z n-prvkové množiny, tj. u kombinace nepřihlížíme k uspořádání prvků a každý prvek z daných n prvků se v jedné kombinaci může vyskytnout nejvýše jednou. 31

3.2. VARIACE A KOMBINACE Počet všech kombinací k-té třídy z n prvků: C(k, n) = n! / k!(n k)! [5]. 3.2.1 Počet všech variací, kombinací Funkce varcount počítá podle definice celkový počet všech variací. Funkce combcount počítá podle definice celkový počet všech variací. Výpočet však neprobíhá přes výpočet faktoriálů tak, že prvně se spočítá n! a poté dojde k dělení jmenovatelem, ale průběžně se krátí zlomek největším společným dělitelem. Kdyby se neprovádělo krácení ihned, už při velikosti n = 13 by došlo k přetečení datového typu long int a výsledky by byly správné jen pro malé vstupní hodnoty n. I tak jsme ale schopni vrátit jen počet možností menší než 2 147 483 648, což je omezení dané návratovým typem long int. Název funkce: long varcount(int k, int n) long combcount(int k, int n) Parametry: Parametr k je celočíselná hodnota, která určuje velikost k-tice. Hodnota n značí velikost množiny, ze které lze vybírat prvky. Návratová hodnota: Celkový počet variací (kombinací) řádu k z n prvků. Výstup ukázkového příkladu: Celkovy pocet vsech kombinaci radu 3 ze 6 prvku je 20. Celkovy pocet vsech variaci radu 3 ze 6 prvku je 120. Celkovy pocet vsech kombinaci radu 5 ze 10 prvku je 252. Celkovy pocet vsech variaci radu 5 ze 10 prvku je 30240. Max. pocet variaci, ktery lze vypocitat: V(10, 13) = 1037836800. Max. pocet kombinaci, ktery lze vypocitat: K(15, 34) = 1855967520. 32

3.2. VARIACE A KOMBINACE #include<stdio.h> #include "comb.h" int main() int k, n; k = 3; n = 6; printf("celkovy pocet vsech kombinaci radu %d ze %d prvku je %ld.\n", k, n, combcount(k,n)); printf("celkovy pocet vsech variaci radu %d ze %d prvku je %ld.\n\n", k, n, varcount(k,n)); k = 5; n = 10; printf("celkovy pocet vsech kombinaci radu %d ze %d prvku je %ld.\n", k, n, combcount(k,n)); printf("celkovy pocet vsech variaci radu %d ze %d prvku je %ld.\n\n", k, n, varcount(k,n)); k = 10; n = 13; printf("max. pocet variaci, ktery lze vypocitat: V(%d, %d) = %ld.\n", k, n, varcount(k,n)); k = 15; n = 34; printf("max. pocet kombinaci, ktery lze vypocitat: K(%d, %d) = %ld.\n", k, n, combcount(k,n)); return 0; Příklad 3.2.1: Příklad použití funkce varcount a combcount 3.2.2 Alokace variace, kombinace Variace k-té třídy z n prvků je v paměti uložena jako pole o velikosti n + 2. U kombinací je velikost alokovaného pole pouze k + 2. Před každou k-ticí jsou umístěny charakteristické informace (počet vybíraných prvků a celková velikost vybírané množiny) a u variací jsou ještě navíc za k-ticí umístěny zbylé prvky, které nebyly zahrnuty do variace. K údajům o velikostech lze přistoupit pomocí funkcí sizeofn a sizeofk s určitým prefixem, které vrací velikost n, resp. velikost k. Název funkce: unsigned int *varalloc(int k, int n) unsigned int *varknalloc(int k, int n) 33

3.2. VARIACE A KOMBINACE unsigned int *comballoc(int k, int n) unsigned int *combknalloc(int k, int n) Parametry: Parametr k je celočíselná hodnota, která určuje velikost k-tice. Hodnota n značí velikost množiny, ze které lze vybírat prvky. Návratová hodnota: Ukazatel na blok alokované paměti. Typ ukazatele záleží na použité funkci (například varalloc vrací ukazatel na unsigned int, varallocsl zase ukazatel na long int). Není-li z nějakého důvodu možné pamět přidělit, funkce vrací nulový ukazatel. 3.2.3 Parametry variace, kombinace Parametry variace se rozumí třída variace (k) a velikost množiny prvků (n), ze které vybíráme její podmnožinu. K zisku velikosti k a n slouží funkce varsizeofk (combsizeofk) resp. varsizeofn (combsizeofn). Parametry k-tic jsou nastaveny při alokaci paměti funkcemi varalloc a comballoc, takže v případě volání funkcí na jinak vytvořené pole budou vráceny nesmyslné výsledky. Název funkce: int varsizeofk(unsigned int *var) int varsizeofn(unsigned int *var) int combsizeofk(unsigned int *comb) int combsizeofn(unsigned int *comb) Parametry: Parametr var (comb) je ukazatel na první prvek k-tice. Návratová hodnota: Funkce varsizeofk a combsizeofk vrací velikost k-tice, neboli počet vybíraných prvků z celkového množství n. Funkce varsizeofn a combsizeofn vrací velikost počáteční množiny n, ze které jsou k-tice vybírány. Pokud má k-tice nulovou délku nebo vstupní parametr má hodnotu nulového ukazatele, je vrácena 0. 34

3.2. VARIACE A KOMBINACE 3.2.4 Inicializace variace, kombinace typu int Kombinace i variace jsou nastaveny tak, že obsahují nejmenších k-prvků, kde výchozí nejmenší prvek je roven nule, při inicializaci od jedné je možné použít funkci varinit1 nebo combinit1. U variací je kromě k-tice inicializováno i nevybraných n-k prvků. Název funkce: void varinit(unsigned int *var) void varkninit(unsigned int *var, int k, int n) void combinit(unsigned int *comb) void combkninit(unsigned int *comb, int k, int n) Parametry: Parametr var (comb) je ukazatel na variaci (kombinaci), která má být inicializována. Návratová hodnota: Žádná. 3.2.5 Inicializace variace, kombinace typu char Variace (kombinace) obsahující znaky lze nastavit pomocí libovolného znaku. Ten pak bude prvním prvkem k-tice a na ostatních pozicích budou znaky s ordinální hodnotou o jedna větší než má jeho předchůdce. Název funkce: void varinitc(unsigned char *var, char init) void varkninitc(unsigned char *var, char init, int k, int n) void combinitc(unsigned char *comb, char init) void combkninitc(unsigned char *comb, char init, int k, int n) Parametry: Parametr var (comb) je ukazatel na variaci (kombinaci), která má být inicializována. Znaková konstanta init je počátečním prvkem variace (kombinace). 35

3.2. VARIACE A KOMBINACE Návratová hodnota: Žádná. #include<stdio.h> #include "comb.h" int main() unsigned char *variace, n = 8, k = 5; variace = varallocc(k,n); printf("znakova variace zacinajici znakem a : \n"); varinitc(variace, a ); varprintc(variace); printf("\nznakova variace zacinajici znakem H : \n"); varinitc(variace, H ); varprintc(variace); varfreec(variace); return 0; Příklad 3.2.2: Příklad použití funkce varinitc Výstup ukázkového příkladu: Znakova variace zacinajici znakem a : [a, b, c, d, e] Znakova variace zacinajici znakem H : [H, I, J, K, L] 3.2.6 Tisk variace, kombinace na standardní výstup Zapíše k-tici na standardní výstup (stdout). Variace jsou vypsány v hranatých závorkách, kdežto prvky kombinace jsou uzavřeny ve složených množinových závorkách, aby se zdůraznilo, že nezáleží na pořadí prvků. Název funkce: void varprint(unsigned int *var) void varknprint(unsigned int *var, int k, int n) void combprint(unsigned int *comb) void combknprint(unsigned int *comb, int k, int n) 36

Parametry: 3.2. VARIACE A KOMBINACE Parametr var (comb) je ukazatel na k-tici, která má být vytištěna na standardní výstup. Návratová hodnota: Žádná. #include<stdio.h> #include "comb.h" int main() unsigned int *variace, *kombinace, k = 5, n = 8; variace = varalloc(k,n); varinit(variace); printf("\nvypis variace, kde zalezi na poradi prvku: \n"); varprint(variace); kombinace = comballoc(k,n); combinit(kombinace); printf("\nvypis kombinace, kde nezalezi na poradi prvku: \n"); combprint(kombinace); varfree(variace); combfree(kombinace); return 0; Příklad 3.2.3: Příklad použití funkce varprint a combprint Výstup ukázkového příkladu: Vypis variace, kde zalezi na poradi prvku: [0, 1, 2, 3, 4] Vypis kombinace, kde nezalezi na poradi prvku: 0, 1, 2, 3, 4 3.2.7 Tisk variace, kombinace do proudu Funkce umožňující uživateli zápis k-tice do specifikovaného proudu. Mimo k-tice lze do proudu zapsat i textový řetězec, který bude umístěn před ní. 37

3.2. VARIACE A KOMBINACE Název funkce: int varfprint(file * stream, char * text, unsigned int *var) int combfprint(file * stream, char * text, unsigned int *comb) int varknfprint(file * stream, char * text, unsigned int *var, int k, int n) int combknfprint(file * stream, char * text, unsigned int *comb, int k, int n) Parametry: První parametr stream je ukazatel na objekt FILE, který určuje proud pro výpis k-tice. Druhý parametr text tvoří uvozovací textový řetězec před výpisem k-tice. Parametr var (comb) je ukazatel na k-tici, která má být vytištěna do určeného proudu. Návratová hodnota: Počet znaků, které se posílají do výstupního proudu, pokud nenastane chyba. V případě výskytu chyby je vrácena hodnota EOF, která má ve většině tradičních implementacích hodnotu -1. #include<stdio.h> #include "comb.h" int main() unsigned int *variace, k = 5, n = 8; FILE * pfile = NULL; pfile = fopen("varfprint_example.txt","w"); variace =varalloc(k,n); varinit(variace); permfprint(pfile,"nejmensi variace: ", variace); permfprint(pfile,"", variace); fclose(pfile); varfree(variace); return 0; Příklad 3.2.4: Příklad použití funkce varfprint 38

3.2. VARIACE A KOMBINACE Obsah souboru varfprint_example.txt: nejmensi variace: [0, 1, 2, 3, 4] [0, 1, 2, 3, 4] 3.2.8 Generování následující variace Funkce varnext vytvoří na adrese, která byla zadaná jako jediný vstupní parametr, minimálního lexikografického následníka k dané variaci. Opakovanou aplikací funkce lze generovat všechny lexikograficky uspořádané variace. Další variace vznikne transpozicí dvou prvků. Bud jsou oba prohazované prvky součástí k-tice a nacházejí se ve variaci, nebo se jeden z nich vyskytuje v k-tici (menší prvek) a druhý se tam nevyskytuje (větší prvek). Pak je menší prvek z variace odstraněn a naopak větší prvek zařazen mezi vybranou k-tici. Prvky, které nebyly zahrnuty do variace se nachází na indexech k až n 1 (dále jen za variací), kdežto vybrané prvky tvořící k-tici jsou v poli na indexech 0 až k 1 (dále jen ve variaci). Samotnou transpozicí vznikne lexikograficky větší variace, minimálního zvětšení dosáhneme pomocí vzestupného seřazení prvků od pozice nalezeného menšího prvku až do konce. Podrobnější popis seřazení i nalezení transpozice se nachází v sekci popis algoritmu. Název funkce: int varnext(unsigned int *var) int varknnext(unsigned int *var, int k, int n) Parametry: Parametr var je ukazatel na variaci, ze které se bude vytvářet její následník. Návratová hodnota: Pokud existuje lexikograficky větší variace, než která byla zadána jako vstupní parametr, je vrácena hodnota 1, v opačném případě je vrácena hodnota 0. 39

3.2. VARIACE A KOMBINACE Zdrojový kód: int varnext(unsigned int *p) int i, pos, k = varsizeofk(p); for( i = k-1; i >= 0 ; i--) /*zvetseni variace o prvek, ktery je za variaci*/ for( pos = k; pos < varsizeofn(p); pos++) if( (p[i] < p[pos]) ) swap_and_sort(p, i, pos); return 1; /* nepodarilo se zvetsit variaci prvkem za variaci */ /* zkousime ji tedy zvetsit prvkem ve variaci */ for( pos = k-1; pos > i; pos--) if( (p[i] < p[pos]) ) swap_and_sort(p, i, pos); return 1; /* zadna dalsi variace neexistuje, je vracena 0 */ return 0; void swap_and_sort(unsigned int *p, int i, int pos) int dm, hm; /* swap */ p[i] ^= p[pos]; p[pos] ^= p[i]; p[i] ^= p[pos]; /* reverse prvku za variaci*/ 40

3.2. VARIACE A KOMBINACE hm = varsizeofn(p)-1; for( dm = varsizeofk(p); dm < hm; dm++,hm--) p[hm] ^= p[dm]; p[dm] ^= p[hm]; p[hm] ^= p[dm]; /* reverse vsech prvku od pozice mensiho prvku az do konce */ hm = varsizeofn(p)-1; for( dm = i+1; dm < hm; dm++,hm--) p[hm] ^= p[dm]; p[dm] ^= p[hm]; p[hm] ^= p[dm]; Použité proměnné: Proměnná pos je index prvku pole, jenž má být prohozen s jiným prvkem tak, aby došlo ke zvětšení variace. Pod indexem i hledáme v poli menší prvek než je na pozici pos. Při nalezení této dvojice jsou prvky s indexy i a pos vzájemně prohozeny. V poli pod indexem k je umístěn první prvek, který nebyl vybrán a tedy se ve variaci nevyskytuje. Indexy dm a hm označují počáteční a koncový úsek pole, který bude seřazen. Popis algoritmu: Nejprve se snažíme zvětšit pozici posledního prvku ve variaci záměnou s vetším prvkem za variací. Prvky za variací jsou uspořádány vzestupně, takže porovnávání probíhá postupně od indexu k do n-1. Pokud nelze provést žádnou záměnu, posuneme se o jednu pozici doleva na předposlední prvek ve variaci a ten opět porovnáváme s prvky za variací. Zde už při neexistenci většího prvku za variací zkoušíme provést záměnu i s prvky nacházející se ve variaci za ním (v tomto případě pouze s posledním prvkem) viz následující obrázek. Tento postup opakujeme dokud nenalezneme dva prvky, které lze prohodit. U maximální variace se už žádné takové dva prvky nenacházejí a proto nedojde ke změně a algoritmus končí. 41

3.2. VARIACE A KOMBINACE Jakmile nalezneme správnou dvojici prvků, prohodíme je a seřadíme sekvenci za menším z nalezených prvků (prvek na indexu i). K tomu slouží pomocná funkce swap_and_sort, jejíž druhý a třetí parametr obsahuje indexy prohazovaných prvků. Řazení probíhá nadvakrát, nejprve seřadíme prvky za variací tak, aby byla sekvence sestupná místo vzestupná. A poté následuje vzestupné řazení všech prvků od indexů i+1 až n-1. Seřazením jsem dosáhli vytvoření nejmenšího lexikografického následníka původní variace. Časová složitost v nejhorším případě: Časová složitost pomocné funkce swap_and_sort je 1 + (n k)/2 + (n 1)/2. Počet porovnání je (n k + i) kde 0 i k 1. 42