DIOFANTOVA ROVNICE | ||
Problém řešení Diofantovy lineární rovnice: Je dána množina přirozených čísel A[1], ..., A[N] a přirozené číslo C, nalezněte takovou podmnožinu A[1], ..., A[N], aby součet jejich prvků byl roven C. Tento problém je NP-úplný. Speciální případ pro sudé C, C = (A[1] + A[2] + ... + A[N]) div 2, je tzv. problém spravedlivého rozdělení (partition problem). Uvedu velice jednoduchý exponenciální algoritmus pro řešení těchto problémů.
... ani na nejrychlejším počítači na světě?V literatuře se můžeme dočíst (následující citáty jsou vybrány z více různých zdrojů): Jediný známý způsob řešení je prozkoumávat postupně všechny podmnožiny, ale těch je u N-prvkové množiny 2N. Například pro N = 300 je počet všech podmnožin větší než odhadovaný celkový počet elektronů, protonů a netronů v celém vesmíru. Pokud by tuto úlohu řešil počítač, který by za sekundu prozkoumal miliardu podmnožin, nedokončil by výpočet dříve, než by vyhaslo naše slunce. Už spravedlivé rozdělení věcí z trochu větší kanceláře, N = 100, nemá smysl řešit ani na NEJRYCHLEJŠÍM POČÍTAČI NA SVĚTĚ.
Algortimus DIOPHANTUvedu jednoduchý exponenciální algoritmus pro řešení Diofantovy lineární rovnice: Je dána množina přirozených čísel A[1], ..., A[N] a přirozené číslo C, nalezněte takovou podmnožinu A[1], ..., A[N], aby součet jejich prvků byl roven C. Algoritmus Utřiď prvky pole ve vzestupném pořadí. Vypočti Ls[1] = A[1], Ls[2] = Ls[1] + A[2], ..., Ls[J] = Ls[J - 1] + A[J], ... pro J = 3, ..., N. Je-li A[1] <= C & C <= Ls[N], pak
V tomto článku představuji nerekurzívní implementaci algoritmu DIOPHANT v jazyce Rexx.
S = S + 1; Stack.S = (L - 1) (C - A.L) D Pokud řešení existuje, vyvolá se procedura EXISTUJE. Uvedu její jednoduchý příklad, který jen vypíše řešení na obrazovce a ukončí výpočet:
Nejhorší případ - Časová složitost O(2N)Následující příklad dokazuje, že algoritmus DIOPHANT má v nejhorším případě exponenciální složitost. Sloupec označený P(N) obsahuje počty push operací.
Můžeme ušetřit zhruba polovinu push operací, když budeme hledat jen jedinou podmnožinu, která obsahuje prvek A[N] a součet jejichž prvků má být roven ((A[1] + ... + A[N]) div 2) - A[N]. To je ovšem problém řešení Diofantovy linearní rovnice pro N - 1 prvků. V tomto článku budu problém spravedlivého rozdělení řešit jako "obyčejný" problém řešení Diofantovy lineární rovnice a to i v případě, když (A[1] + ... + A[N]) div 2 bude liché číslo.
Průměrný? případ - Příklady polynomiální složitostiCo je průměrný případ pro problém řešení Diofantovy lineární rovnice? Toť otázka!
V prvním pokusu jsem pro dané N = 10, 20, 30, 40, 50, 100, 200, 300, 400, 500 vygeneroval pole A pomocí následujícího fragmentu programu. Poznamenejme, že v jazyce Rexx vrací volání vestavěné funkce RANDOM(Min, Max) pseudonáhodná čísla z intervalu <Min, Max>, kde Max je nejvýše rovno 100000.
FORMAT je vestavěná funkce jazyka, jejíž volání v tomto případě vrací zaokrouhlenou celočíselnou hodnotu Sum * RANDI(). Volání funkce RANDI přitom vrací pseudonáhodné číslo v intervalu 0 až 1. Použil jsem funkci z článku Park S.K., Miler K.W.: Random Number Generators: Good ones are hard to find. CACM 1988, Vol. 31, No. 10, p. 1192-1201. V globální proměnné Seed je uložena běžná, naposledy určená, hodnota celočíselné posloupnosti. Generátor pseudonáhodných čísel musí být inicializován tak, že proměnné Seed se přiřadí hodnota z intervalu 1 až 2147483646. Pseudonáhodná čísla z intervalu 0 až 1 pak mohou být generována opakovaným voláním funkce RANDI. Poznámka: // je operátor operace zbytek po dělení.
Pro dané N jsem 100krát vygeneroval pole A, vypočetl C a řešil problém spravedlivého rozdělení; 100krát jsem vygeneroval pole A a C a řešil problém řešení Diofantovy lineární rovnice. Výsledky shrnuje následující tabulka. Doba výpočtu se týká PC s procesorem Cyrix 6x86MX a 32MB RAM.
V druhém testu jsem generoval prvky pole A pomocí následujícího fragmentu programu (jeho první instrukce byla numeric digits 16):
Řešil jsem problém spravedlivého rozdělení věcí s maximální cenou více než 21 milionů Kč. Proměnné Seed jsem přiřadil hodnotu 214748364 a pak jsem spustil 20krát DIOPHANT pro každou z hodnot N = 100, 200, 300, 400, 500. Později jsem Seed přiřadil hodnotu 19685089 a pokračoval jsem pro N = 30, 40, 50, 20, 10. Výsledky shrnuje následující tabulka:
Historie algortimu DIOPHANTRekurzívní verzi DIOPHANT v jazyce Pascal, jsem poprvé publikoval v článku Problém dvou loupežníků s podtitulem Praktická poznámka k jednomu prakticky neřešitelnému problému v časopise Bajt, říjen 1993 (36), s. 134-136. V roce 1993 jsem se zabýval pouze řešením problému spravedlivého rozdělení. Nejzajímavějším z popsaných testů praktické použitelnosti programu DIOPHANT v Bajtu bylo spravedlivé rozdělení zboží ze skladu ČKD Blansko a.s.: 10000 položek v cenách, po vynásobení stem, od 2 Kč do téměř 400 miliónů Kč. Tisíckrát jsem z nich náhodně vybral 500 a program DIOPHANT se je pokusil spravedlivě rozdělit. Jen jednou jsem musel výpočet po devíti hodinách násilně přerušit. V ostatních případech bylo řešení vždy nalezeno; nejdéle za 96 minut, v průměru za 16.5 sekundy, v 967 případech ne za déle než 5 sekund (PC AT 386/40 MHz, 8MB RAM, Turbo Pascal 6.0).
|
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |