Generátor mocnin dvojky

Začalo to nějak takto

V předmluvě Stopařova průvodce se praví: „Vesmír je velký, fakt velký, ani by jste nevěřili, jak hrozně moc úžasně velký je; a tak dále. Také se tam praví, že pokud se zhluboka nadechnete, můžete ve vesmírném vakuu přežít asi 30 vteřin. Ovšem vzhledem k té velikosti je pravděpodobnost, že vás během té doby někdo zachrání rovna jedna ku dvěma na dvě miliardy sedmdesát devět miliónů čtyřista šedesát tisíc třista čtyřicátou sedmou, což je náhodou také telefoní číslo izlingtonského bytu, kam se Arthur vydal na maškarní večírek a potkal dívku, kterou se mu nepodařilo sbalit.

Vzdělanější z vás poznali, že se jedná o úryvek z filmu Stopařův průvodce po galaxii. Ono číslo 22 079 460 347 mě fascinovalo a inspirovalo k vytvoření tohoto programu. Řekl jsem si, že toto číslo by bylo zajímavé si vyčíslit. A tak jsem začal pracovat na jeho vyčíslení. Aby to však nebylo tak jednoduché, vzal jsem to obecněji a vytvořil jsem univerzální generátor mocnin dvojky od 20 do 24 294 967 296 s tím, že vygenerované číslo se uloží do textového souboru. A jako třešničku jsem k tomu dodal ještě soubory BIN.TXT a HEX.TXT, které tak zadané číslo uloží kromě dekadické soustavy také v binární a hexadecimální.

Při tvorbě programu jsem však zjistil, že kromě toho, že nemám dostatečně dobrý hardware na odzkoušení plné funkce programu (momentálně mě tížil nedostatek místa na disku, takže soubor s binárním číslem 2150 000 000 se mi na disk vešel jen tak tak. Po dopsání algoritmu na generování dekadického čísla jsem pak zjistil, že doba generování je exponenciálně závislá na velikosti zadaného exponentu, takže zatímco číslo 210 000 mi vyplivl asi po 3 sekundách, číslo 2100 000 se generovalo asi minutu, generování 21 0000 000 jsem po asi hodině zastavil.

Účel programu, zhmotnit číslo 22 079 460 347, sice nebyl naplněn, nicméně program je hotov a co je hlavní, uveřejňuji zde jeho zdrojové kódy (ty jsou ovšem jen pro silnější povahy!).

Binární číslo

Zapsat binární číslo bylo to nejjednodušší, proto tím také začínám. Máte-li totiž číslo 2n, pak zapsáno binárně je vlastně tvořeno tolika ciframi, kolik je exponent n. Samozřejmě, že binární číslo začíná vždy jedničkou, takže ji tam stačí prostě vepsat. Tím by byl algoritmus hotov: Vypíšeme jedničku a za i n nul. Pro přehlednost (má-li u kupy nul přehlednost vůbec smysl) jsem použil i oddělovač číslic, klasicky binárně po čtveřicích. Podstatně se tak algoritmus zrychlil, protože je rychlejší vypsat tisíckrát ‚ 0000‘ než čtyřtisíckrát ‚0‘.

Aby však byl zápis validní, musel jsem si číslo na čtveřice rozdělit a zjistit co mi zbyde a to dát na začátek (například číslo 26D=100 0000B). Tento zbytek se určí opravdu doslova – v 7. řádku se prostě vypíše po jedné tolik nul, kolik zbývá (počítá se zbytek po celočíselném dělení – 8. řádek) do kompletní čtveřice a teprve potom už se vypíšou kompletní čtveřice (řádek 11).

 

Hexadecimální číslo

U hexa čísla je situace podobná jako u binárního. Ve své podstatě stačí říci, že každé 4 cifry binárně jsou 1 cifra hexadecimálně. Tím pádem má-li binární číslo třeba 1000 kompletních čtveřic, v hexa soustavě to bude rovných 250 cifer. Všechny (až na jednu) kompletní binární čtveřice jsou však nulové a tak se opět do hexadecimálního soubru zapíše n-krát ‚ 00‘, neboť hexadecimální čísla se oddělují po dvojicích cifer.

Ovšem problém nastal u té nekompletní úvodní dvojice cifer (například 223D=80 00 00H) neboť ta narozdíl od binárního čísla není pouze 1 a několik nul. Nabývá hodnot 1H (dvojnásobek je) 2H (další dvojnásobek je) 4H (pak) 8H (poté pozor, nikoliv 16H, ale) 10H, 20H, 40H, 80H a 1 00H, tedy 1H a další kompletní dvojice.

O vygenerování tohoto začátku se starají řádky 9 až 23. Nejdříve si opět určí zbytek, stejně jak u binárního čísla (jen pochopitelně s jiným dělitelem), a poté by stačilo vyčíslit 2tento zbytek a převést toto číslo do hexadecimální soustavy. Číslo by sice by nebylo moc velké (nejvyšší hodnota by byla 80H, 128D), takže by tomuto postupu moc nebránilo. Ale to málo je jednak, fakt, že jazyk C nenabízí moc funkcí k umocňování (funkce pow nefungovala, tak jak měla) a pro převody číslených soustav neznám žádnou, a navíc by to bylo zbytečně časově náročné, když to jde i trošku „oprasit“.

Prostě se zeptá jestli ten zbytek je v hexa soustavě jednociferný (řádek 12), a pokud je, tak tu 1 cifru vyčíslí a vypíše. Pokud není, je tedy dvojciferný, vyčíslí tu první cifru a za ni vepíše nulu. Poslední řádek (25.) pak doplní zbývající, kompletní dvojce.

 

Dekadické číslo

Algoritmus pro generování dekadického čísla je z této trojice nejšílenější a proto si jej nechávám úplně na závěr. Dvojkový a šestnáctkový měly totiž základ, který určitým způsobem stál na čísle 2, jehož mocniny jsme chtěli zpracovávat. Ale desítka, ta je sice pětinásobkem dvojky, ale ta pěkta nám tam pořád hapruje a dělá velké problémy.

První myšlenka algoritmu měla fungovat asi nějak tak, že cyklus n-krát (kde n je počet mocnin), prodvojnásobí číslo počínaje jedničkou ( přičemž nás zajímá pouze poslední cifra, zbytek se ignrouje) ale po každé, když přeteče do vyššího řádu, zinkrementuje se (už jenom kvůli tomuto názvu jsem to chtěl napsat 😉 registr postupných inkrementací, jehož obsah by se pak předal dál a stejným způsobem násobil a násobil, a toto celé by se pak nějak opakovalo, až by se došlo do stavu, kdy by byl RPI prázdný, tedy už by nemělo co přetékat.

Bohužel tento systém selhal a tak mě napadl druhý, a to ten, že jsem zjistil, že čísla se v každém řádu opakují – pro řád jednotek je to (1), 2, 4, 8, (1)6, (3)2, a znovu. Opakují-li se takto čísla v jednotkách, opakují se i v desítkách, stovkách, tisících … Jenže perioda tohoto opakování rostla exponenciálně s řádem (pro jednotky zmiňované 4 čísla, pro desítky 20, pro stovky 100, pro tisíce 500, …) a navíc mě nenapadlo, jak ty data získat.

Jedinná použitelná metoda byla průběžné násobení a přičítání přetečení, tedy metoda, jakou to počítají děti na základní škole. Opravdu, musel jsem si vytvořit (dynmické) pole, ve kterém jsou uloženy jednotlivé cifry. Vzhledem k tomu, že ukládat 1 číslici, tedy číslo 0 až 9 na 4Byty dávalo účinnost asi 0,000002‰, tak mě napadlo, že když tam dám místo jedné cifry 3, nejen že se zjednoduší oddělování tisíců, ale možná to bude i rychlejší.

Doba generování 2100 000 snížíla z cca 57 sekund na pouhých 23. Jen tak z hecu jsem tam zkusil uložit těch cifer šest (čímž se ovšem naprosto brutálně zkomplikoval výpis do souboru a počet cifer se také musí celý přepočítávat) avšak když jsem zjistil, že čas je ještě téměř poloviční (14 vteřin), a paměti je také potřeba polovina, zmlkl jsem a nechal to tak.

Popis funkce algoritmu

Algoritmus tedy začne zalokováním paměti do zásoby (1 tisíc prvků). Poté začne násobit dvěma prvky pole (řádek 15), z nichž první začíní čislem 1 (řádek 9) a když už bude větší než 1 000 000 (řádek 17), tak uloží do proměnné carry jedničku (řádek 19), která se přičte v dalším „řádu“ (v další šestici). Jakmile dojede jednou (projde všechny číslice), ptá se, jestli přetekl i ten poslední řád (řádek 22)(třeba přechod z 512 na 1024), a tak zvýší počet řádů, které má procházet (řádek 29), automaticky tam zapíše tu jedničku, která do něj přetekla (řádek 28), ale to až po té, co si je jist, že ještě sem může zapisovat (že má ještě zalokovaný dostatek paměti; jinak si musí zabrat místo na dalších 2 000 prvků).

Jakmile číslo vygeneruje, musí poměrně složitě to, co má v paměti vypsat do souboru, tak aby to nějak vypadalo. To znamená, že čísla rozdělí na první a zbývající 3 cifry, které vypíše po sobě, avšak první musí vypsat bez bezvýznaných nul ( a to se v 34. řádku rozhoduje jestli tak má učinit u té první nebo až zbývající trojice). Součastně se musí hlídat, aby nevypsal dvakrát to samé nebo se nepokusil vypsat data, která už nepatří tomuto programu. S poslední šesticí cifer jsem měl problém vypsat ji v cyklu na 37 řádku a tak ji vypisuji zvlášť o řádek níž.

Tím, že v proměnné pocet majíce původně sloužit k uchování počtu cifer je uložen počet šestic cifer, je třeba určit přesný počet cifer. Ty se určují tak, že vezme první šestici a postupně ji provnává s mocninami desíti.

 

Stažení programu

Program jsem tvořil v mém IDE C write’n’run 1 (verze 2 je ke stažení zde) na Ubuntu, takže je primárně optimalizován na linuxové operační systémy. Při jeho tvorbě jsem s ním však měl nemalé problémy (proto jsem jej také tvořil měsíc) a při tom jsem jej zkoušel i na Windows, takže dodám i Windows verzi.

Nicméně zatím zde dodávám alespoň zdrojový kód:

Stažení zdrojového kódu generátoru mocnin dvojky.

a automatickou instalačku pro linux (stačí uložit a spustit):

Instalačka pro Linux ZDE

 

On-line verze programu

Ještě před tím, než jsem začal psát tento program v jazyce C, vytvořil jsem jej v PHP. Tuto prvotní verzi algoritmu samozřejmě dodávám také. Akorát on-line verze je omezena na exponent 5 000.

On-line verze zde

Přehled programovacích jazyků

Zde chystám jsem před asi 2 lety chystal úplně hustodémonsky krutopřísně brutální tabulku, ve které srovnám jazyky které umím-Pascal, Delphi; C, Bash, php, (případně i MS Visual Fox Pro nebo JavaScript a Javu, kterou zatím moc neumím…). Bude Měla vypadat nějak takto:

Dámy a pánové, až teď si někdo všiml a upozornil mě na nemalé množství chyb v tabulce. Navíc dnes již vím, že většina těchto jazyku je nesrovnatelná, a tak tuto tabulku berte prosím jako ostrašující případ, jak nahlíží začínající programátor, snažící se si co nejrychleji osvojit základy co nejvíce jazyků, na programování. 😉

Pascal Delphi C Bash PHP
Rok vzniku 1970 1990 1973 1989 1994
Společnost Borland Borland K&R FSF PHP Group
Platforma DOS/ Windows MS Windows Unix Unix multiplatformní
kompilátor TPC, FPC DCC? gcc
objektový? ne ano ano ne ano
case senzitivní? ne ne ne ne ne
podporuje RegExp? ne ne ano ano ano

Moje vývojové prostředí pro textovou konzoli linuxu

Začalo to takhle:

Ti bystřejší pochopili, že se jedná o programování v jazyce C. Ano, takto jsem začínal já programovat v Céčku.

Vzhledem k tomu, že mě v textové konzoli bavilo pracovat, neviděl jsem důvod proč si instalovat nějaké grafické vývojové prostředí. Avšak psát tyto příkazy pořád dokola mě po pár úpravách zdrojového kódu přestalo bavit, ale napadlo mě, že bych si na to mohl napsat skript. Skript prostě provedl výše napsané 4 příkazy a skončil. V případě potřeby jsem jej spustil znovu a znovu a znovu, doku nebyl prográmek hotov.

Když jsem to zabalil do cyklu (abych jej nemusel spouštět pořád dokola), bylo potřeba v určitý okamžik zeptat se uživatele, zda-li chce program ukončit. V tom se ve mně zrodila myšlenka jakéhosi ‚menu‘. Tam by si uživatel mohl zvolit nejen ukončení, ale také jednotlivé příkazy (editace, kompilace, spuštění). Kromě toho si mohl zvolit pracovní soubor, no a pomalu z toho začalo vznikat vývojové prostředí.

Díky jeho funkcím jsem jej nazval skromně C write ‚n‘ run (Céčko – napiš a spusť) a od té doby, jsem jej používal a příležitostně i poupravil o nové funkce.

Postupnými úpravami se však objevilo několik chyb, a navíc, při příchodu na vysokou v září 2011 jsem prošel vstupním kurzem Linuxu, kde jsem se dozvěděl kupu nových informací, které mě přivedly k tomu, abych si C-wnr napsal komplet znovu. Stalo se tak, a vzniklo C-write ‚n‘ run 2.


Stažení C write ‚n‘ run 2.1 (9,3kB)

Upozornění: 13. prosince 2011 v kolem 13. hodiny byla vydána verze 2.1, která odstraňuje problém kompilace matematických funkcí (kompilátor ‚neznal‘ funkce z knihovny math.h) přidáním parametru -lm.

 

 

Krásou C-wnr je však to, že pouhou změnou několika málo příkazů (v podstatě jen jednoho jediného a textové omáčky kolem) tak můžete získat vývojové prostředí pro jazyk Pascal:

Pascal-wnr-2 dodám, až jej budu mít vytvořený.

nebo Java-write ‚n‘ run:

také dodám později.