Teaduslik töö on ilmselt meie Ă”pingute kĂ”ige huvitavam osa. Idee on proovida end valitud suunas juba ĂŒlikoolis. NĂ€iteks lĂ€hevad tarkvaratehnika ja masinĂ”ppe erialade tudengid sageli tegema teadusprojekte (peamiselt JetBrainsis vĂ”i Yandexis, kuid mitte ainult).
Selles postituses rÀÀgin oma projektist arvutiteaduse suunas. Projekti raames uurisin ja rakendasin praktikas lĂ€henemisviise ĂŒhe tuntuma NP-raskete probleemide lahendamiseks: tipupakkumise probleem.
Praegu areneb vĂ€ga kiiresti huvitav lĂ€henemine NP-rasketele probleemidele â parameetrilised algoritmid. PĂŒĂŒan teid tutvustada, rÀÀkida mĂ”nest lihtsast parameetrilisest algoritmist ja kirjeldada ĂŒht vĂ”imsat meetodit, mis on mind palju aidanud. Oma tulemusi esitlesin PACE Challenge'i vĂ”istlusel: avatud testide pĂ”hjal hĂ”ivab minu lahendus kolmanda koha ja lĂ”plikud tulemused avaldatakse 1. juulil.

Minust
Minu nimi on Vasili Alfjurov, hetkel lĂ”petan ma III kursust NIU HSE â Peterburis. Algoritmid huvitavad mind juba koolipĂ€evist, kui Ă”ppisin Moskva 179. koolis ja osalesin edukalt informaatika olĂŒmpiaadidel.
LÔpmatu hulk spetsialiste parametriseeritud algoritmide kohta siseneb baari...
NÀide on pÀrit raamatust
Kujutage ette, et olete baari turvamees vĂ€ikeses linnas. Igal reedel tuleb pool linna teie baari lÔÔgastuma, mis tekitab teile palju muret: peate vĂ€lja viskama rahutud kĂŒlastajad, et vĂ€ltida kaklusi. LĂ”puks hakkab see teile tĂŒĂŒtuks, ja otsustate vĂ”tta ennetavaid meetmeid.
Kuna teie linn on vĂ€ike, teate kindlasti, millised paarid kĂŒlastajatest tĂ”enĂ€oliselt kaklevad, kui nad satuvad baari koos. Teil on nimekiri n inimestest, kes tulevad tĂ€na Ă”htul baari. Otsustate mitte lasta baari mingisuguseid linnaelanikke nii, et kedagi ei tekiks kaklusi. Samal ajal ei soovi teie ĂŒlemus kaotada tulu ja on nördinud, kui te ei lase baari rohkem kui k inimest.
Kahjuks on ees seispev ĂŒlesanne klassikaline NP-raskusi tekitav probleem. Te vĂ”isite seda teada ka kui , vĂ”i tipukaate probleemina. Sellistele ĂŒlesannetele ei ole ĂŒldiselt teada algoritme, mis töötaksid mĂ”istlikus ajas. TĂ€psemalt öeldes ĂŒtleb tĂ”endamata ja ĂŒsna tugev hĂŒpotees ETH (Exponential Time Hypothesis), et seda ĂŒlesannet ei saa lahendada ajaga
, mis tÀhendab, et midagi mÀrgatavalt paremat kui tÀielik lÀbiotsimine ei saa vÀlja mÔelda. NÀiteks oletame, et teie baari on tulemas n = 1000 inimest. Siis sisaldab tÀielik lÀbiotsimine
varianti, mis on umbes
â uskumatult palju. Ănneks on teie juhatus kehtestanud teile piirangu k = 10, nii et kombinatsioonide arv, mida peate lĂ€bi otsima, on palju vĂ€iksem: kĂŒmne elemendi alamhulkade arv on
. See on juba parem, kuid ikkagi ei suudeta seda pÀeva jooksul isegi vÔimsal klusteril lÔpetada.

Kuna sellise pingestatud suhete konfiguratsiooniga baarikĂŒlastajate vahel kakluse tĂ”enĂ€osus vĂ€heneb, tuleb Bobi, Daniela ja Fjodorit sisse lasta. Lahendust, kus jÀÀb vaid kaks vĂ€lja, ei ole.
Kas see tĂ€hendab, et on aeg alla anda ja kĂ”iki sisse lasta? Vaatame muid vĂ”imalusi. NĂ€iteks vĂ”ib mitte lasta sisse ainult neid, kes tĂ”enĂ€oliselt kaklevad vĂ€ga suure hulga inimestega. Kui keegi suudab kakelda vĂ€hemalt ĂŒhe k + 1 teise inimesega, siis teda kindlasti ei tohi lasta sisse â vastasel juhul tuleb kĂ”iki mitte lasta. k + 1 Kohalikud elanikud, kellega ta vĂ”ib kakelda, ja see petab juhtkonna tĂ€iesti Ă€ra.
Oletame, et olete kĂ”ik, keda saite, selle pĂ”himĂ”tte kohaselt vĂ€lja visanud. Siis vĂ”ivad kĂ”ik ĂŒlejÀÀnud kakelda mitte rohkem kui k inimestega. Nende seast vĂ€lja viskades k inimest, saate vĂ€ltida mitte rohkem kui
konflikti. Seega, kui rohkem kui
inimest osaleb vĂ€hemalt ĂŒhes konfliktis, siis te ei suuda neid kindlasti kĂ”iki Ă€ra hoida. Kuna on selge, et tĂ€iesti konfliktituks inimesi te kindlasti sisse lasete, peate uurima kĂ”iki alamhulgi, mille suurus on kĂŒmme, kahesajast inimesest. Nende arv on umbes
, ja sellise hulga operatsioone on juba vÔimalik lÀbi vaadata klastris.
Kui on vĂ”imalik sisse lasta tĂ€iesti konflikti puudutavaid isikuid, siis kuidas on nende suhtes, kes osalevad vaid ĂŒhes konfliktis? Tegelikult vĂ”ib neid ka sisse lasta, sulgedes ukse nende vastase ees. Ja tĂ”epoolest, kui Alice on konfliktis vaid Bobiga, siis kui me laskme neist kahest sisse Alice'i, ei kaota me: Bobil vĂ”ivad olla muud konfliktid, kuid Alicel neid kindlasti ei ole. SeetĂ”ttu pole meie jaoks mĂ”ttekas mitte lasta sisse kedagi kahest. PĂ€rast selliseid operatsioone jÀÀb meie ette mitte rohkem
lahendamata saatusega kĂŒlalisi: kokku on meil
konflikte, igas osaleb kaks osalist ja igaĂŒhel on vĂ€hemalt kaks osalust. See tĂ€hendab, et jÀÀb kaaluda vaid
varianti, mis vĂ”ib kindlasti arvestada pool pĂ€eva sĂŒlearvutil.
Tegelikult saab lihtsate mĂ”ttekĂ€ikude abil saavutada veelgi soodsamaid tingimusi. Tuleb mĂ€rkida, et meil on hĂ€dasti vaja lahendada kĂ”ik vaidlused, st igast konflikti paarist valida vĂ€hemalt ĂŒks inimene, keda me ei lase sisse. Vaatame jĂ€rgmist algoritmi: vĂ”tame mistahes konflikti, kust eemaldame ĂŒhe osalise ja kĂ€ivitame rekursiivselt ĂŒlejÀÀnud osaliste pĂ”hjal, seejĂ€rel eemaldame teise ja kĂ€ivitame ka rekursiivselt. Kuna iga sammu juures eemaldame kellegi, on sellise algoritmi rekursioonipuu binaarne puu sĂŒgavusega k, seega töötab algoritm kokku
, kus n â tipude arv, ja m â servade arv. Meie nĂ€ites on see umbes kĂŒmme miljonit, mis arvutatakse sekundite murdosa jooksul mitte ainult sĂŒlearvutis, vaid isegi mobiiltelefonis.
Ălaltoodud nĂ€ide on nĂ€ide parameetrilisest algoritmist. Parameetrilised algoritmid on algoritmid, mis töötavad ajaga f(k) poly(n), kus p â polĂŒnoom, f â suvaline arvutatav funktsioon, ja k â mingi parameeter, mis vĂ”ib olla palju vĂ€iksem ĂŒlesande suurusest.
Kuni selle algoritmini viivad kĂ”ik mĂ”ttekĂ€igud nĂ€itavad nĂ€idet kerneldamisest â ĂŒks ĂŒldtehnikaid parameetriseeritud algoritmide loomisel. Kerneldamine tĂ€hendab ĂŒlesande suuruse vĂ€hendamist vÀÀrtuseni, mis on piiratud parameetri funktsiooniga. Saadud ĂŒlesannet nimetatakse sageli kerniks. Nii saime lihtsate mĂ”tiskluste kaudu tippude asteid arvestades kvadratilise kerne Vertex Cover'i probleemile, mis on parameetriseeritud vastuse suurusega. On olemas ka teisi parameetreid, mille saab selle ĂŒlesande jaoks valida (nĂ€iteks Vertex Cover Above LP), kuid me arutame just sellist parameetrit.
Pace Challenge
VĂ”istlus (The Parameterized Algorithms and Computational Experiments Challenge) sai alguse 2015. aastal, et luua side parameetriseeritud algoritmide ja praktikates kasutatavate lĂ€henemisviiside vahel, et lahendada arvutusprobleeme. Esimesed kolm vĂ”istlust keskendusid puu laius graafis (), Ć teineri puu leidmisele () ja tsĂŒklite lĂ”ikavate tippude hulga leidmisele (). Sel aastal oli ĂŒks ĂŒlesanne, milles sai oma oskusi proovile panna, ĂŒlaltoodud tippude katte probleem.
VĂ”istlus muutub iga aastaga ĂŒha populaarsemaks. EelĂ€rvete kohaselt osales sel aastal ainult tippkatteprobleemi lahendamise vĂ”istlusel 24 meeskonda. Tuleb mĂ€rkida, et vĂ”istlus kestab mitte paar tundi ega isegi nĂ€dalat, vaid mitu kuud. Meeskondadel on vĂ”imalus uurida kirjandust, vĂ€lja mĂ”elda originaalne idee ja proovida see ellu viia. Tegelikult esindab see vĂ”istlus uurimistööd. KĂ”ige tĂ”husamate lahenduste ideed ning vĂ”itjate auhindamine toimub koos konverentsiga (Rahvusvaheline Parametriseeritud ja TĂ€psete Arvutuste SĂŒmposium) Euroopa suurima ig ŚŚ©Ś Ś algoritmilise kogunemise raames . TĂ€iendavat teavet vĂ”istluse enda kohta leiate , samas kui eelmiste aastate tulemused on saadaval .
Lahenduse skeem
Tippude katmiseks olen proovinud rakendada parameetrilisi algoritme. Need koosnevad tavaliselt kahest osast: lihtsustamise reeglitest (mis ideaalis viivad kerneldamiseni) ja jagamisreeglitest. Lihtsustamise reeglid on sisendi eeltöötlus polĂŒnoomse ajaga. Nende reeglite eesmĂ€rk on viia probleem vĂ”rreldava vĂ€iksema suurusega probleemini. Lihtsustamise reeglid on algoritmi kĂ”ige kulukam osa ja nende rakendamine viib ĂŒldise tööajani
mitte lihtsalt polĂŒnoomse ajani. Meie puhul pĂ”hinevad jagamisreeglid sellel, et iga tipu puhul tuleb vastuseks vĂ”tta kas see vĂ”i tema naaber.
Ăldine skeem on jĂ€rgmine: rakendame lihtsustamise reegleid, siis valime mingi tipu ja teeme kaks rekursiivset kutset: esimeses me vĂ”tame selle vastuseks ja teises me vĂ”tame kĂ”ik tema naabrid. Seda nimetame selle tipu jagamiseks (branching).
Sellesse skeemi lisatakse tĂ€pselt ĂŒks tĂ€iendav element jĂ€rgmises lĂ”igus.
Ideed jagamisreeglite (branching) jaoks
RÀÀgime, kuidas valida tippu, mille kaudu jagunemine toimub.
PÔhikontseptsioon on vÀga ahne algoritmilises mÔttes: valime maksimaalse astmega tipu ja jaguneme just selle kaudu. Miks tundub, et see on parem? Sest rekursiivse kÔne teises harus eemaldasime sel viisil vÀga palju tippe. VÔime loota, et jÀÀb vÀike graaf, mille kallal saame kiiresti töötada.
See lÀhenemine, koos juba arutletud lihtsate kerneldamistehnikatega, ei toimi halvasti, lahendab testid, mille suurus on mitu tuhat tippu. Kuid nÀiteks kuubiliste graafide (st selliste graafide, kus iga tipu aste on kolm) puhul ei toimi see hÀsti.
On veel ĂŒks idee, mis pĂ”hineb ĂŒsna lihtsalt mĂ”ttel: kui graaf on mitteĂŒhendatud, saab selle komponentide ĂŒhenduvuse ĂŒlesande lahendada iseseisvalt, ĂŒhendades vastused lĂ”puks. See on muide see vĂ€ike lubatud muudatus skeemis, mis kiirendab lahendust: varem töötasime sellisel juhul komponentide vastuste arvu ajakulu korrutisega, nĂŒĂŒd aga summa pĂ”hjal. Ja branshingu kiirendamiseks tuleb muuta ĂŒhendatud graaf mitteĂŒhendatud graafiks.
Kuidas seda teha? Kui graafis on liitumispunkt, tuleb teha branshing just selle kaudu. Liitumispunkt on selline tipp, mille eemaldamisel graaf kaotab ĂŒhenduvuse. KĂ”ik liitumispunktid graafis saab leida klassikalise algoritmiga lineaarajas. Selline lĂ€henemine kiirendab branshingut mĂ€rkimisvÀÀrselt.

Iga valitud tipu eemaldamisel laguneb graaf ĂŒhenduvuse komponentideks.
Me teeme nii, kuid soovime rohkem. NĂ€iteks otsida graafis vĂ€ikseid tippude lĂ”ikeid ja teha nende pĂ”hjal lĂ”hustamist. TĂ”husaim tuntud viis, kuidas leida minimaalne globaalne tippude lĂ”ige, on kasutada Gomori-Hu puud, mille ehitamine vĂ”tab kubikulise aja. PACE Challenge'i tĂŒĂŒpiline graafi suurus on mitu tuhat tippu. Sellises olukorras peab rekursioonipuus igas tipus teostama miljardeid operatsioone. Seega on ĂŒlesande lahendamine ettenĂ€htud ajaks lihtsalt vĂ”imatu.
Proovime lahendust optimeerida. Minimaalne tippude lÔige kahe tipu vahel saab leida mis tahes algoritmiga, mis loob maksimaalse voolu. Saame sellele vÔrgule rakendada , praktikas töötab see vÀga kiiresti. Mul on kahtlus, et teoreetiliselt saab tÔendada tööaja hinnangut
, mis on juba tÀiesti vastuvÔetav.
Olen proovinud mitu korda otsida lĂ”ikeid juhuslike tippude paaride vahel ja valida neist kĂ”ige tasakaalustatum. Kahjuks andis see avatud PACE Challenge testides halbu tulemusi. VĂ”rreldes tipptasemel tippude pĂ”hjal töötava algoritmiga, kus rakendatakse sĂŒgava langemise piirangut, jĂ€id pĂ€rast lĂ”ike otsimist suurem graaf. See on tingitud sellest, et lĂ”iked osutusid vĂ€ga tasakaalustamatuks: eemaldades 5-10 tippu, Ă”nnestus eraldada vaid 15-20.
VÀÀrib mÀrkimist, et teoreetiliselt kiiremate algoritmide artiklites kasutatakse palju keerukamaid tehnikaid tippude eraldamiseks. Need tehnikad omavad vÀga keerukat rakendust ning sageli halbu tulemusi aja ja mÀlu osas. Mul ei Ônnestunud neist leida praktikaks piisavalt vastuvÔetavaid lahendusi.
Kuidas rakendada lihtsustamise reegleid
Meil on juba ideed kerneldamise osas. Tuletan meelde:
- Kui on isolatsioonis tip, siis eemaldage see.
- Kui on tipu kraad 1, eemaldage see ja vÔtke tema naaber vastuseks.
- Kui vÀhemalt on tippu kraad k + 1, vÔtke see vastuseks.
Esimese kahega on kĂ”ik selge, kolmandaga on ĂŒks nip. Kui naljaka ĂŒlesande puhul baarist oli meil ĂŒlemine piir, k, siis PACE Challenge'is peame lihtsalt leidma minimaalse suurusega tipukaèŠçă see on iseloomulik muundumine otsinguĂŒlesannetest (Search Problem) lahendusĂŒlesanneteks (Decision Problem), sageli ei tehta kahe ĂŒlesande vahel vahet. Praktikas, kui kirjutame tipu katte lahendaja, vĂ”ib vahe olla. NĂ€iteks nagu kolmandas punktis.
Rakenduse seisukohalt on kaks lĂ€henemisviisi. Esimene meetod nimetatakse Iterative Deepening'iks. See seisneb jĂ€rgmisest: vĂ”ime alustada mingist mĂ”istlikust alumisest piirist vastusele ja seejĂ€rel kĂ€ivitada oma algoritmi, kasutades seda piiri ĂŒlemise piirina, mitte laskumata rekursiooni madalamale kui see piir. Kui leidsime mingi vastuse, on see garanteeritud optimeeritud, vastasel juhul saame seda piiri ĂŒhe vĂ”rra suurendada ja uuesti kĂ€ivitada.
Teine lÀhenemine on hoida mingit praegust optimaalselt vastust ja otsida vÀiksema suurusega vastust, muutma seda parameetrit leides k lisakÀikude eemaldamiseks otsingus.
PĂ€rast mitmeid öiseid katsetusi otsustasin nende kahe meetodi kombinatsiooni kasuks: esmalt kĂ€ivitan oma algoritmi mĂ”ne sĂŒgavuse piiranguga (seades selle nii, et see vĂ”taks tĂŒhise aja vĂ”rreldes pĂ”hilahendusega) ja kasutan parimat leitud lahendust kui ĂŒlemist piiri vastusele â see tĂ€hendab just seda k.
2. astme tipud
Oleme 0. ja 1. astme tipudega saanud hakkama. Selgub, et seda saab teha ka 2. astme tippudega, kuid selle jaoks on graafilt vaja keerukamaid operatsioone.
Selle selgitamiseks tuleb kuidagi tĂ€histada tippe. Nimeta 2. astme tipp tippuks v, ja tema naabreid â tipud x ja y. Edasi lĂ€heb meil kaks juhtumit.
- Kui x ja y â naabrid. Siis vĂ”ib vastuseks vĂ”tta x ja y, ja v eemaldada. TĂ”epoolest, sellest kolmnurgast peab vĂ€hemalt kaks tippu vastuseks vĂ”tma ja me ei kaota, kui vĂ”tame x ja y: neil on ilmselt veel naabreid, aga v nendel ei ole.
- Kui x ja y â mitte naabrid. Siis vĂ€idetakse, et kĂ”ik kolm tippu saab kokku liita ĂŒheks. Idee on see, et sellisel juhul on optimaalne vastus, kus me vĂ”tame kas v, vĂ”i mĂ”lemad tipud x ja y. Esiteks peame vastuseks vĂ”tma kĂ”ik naabrid x ja y, ja teises ei ole see tingimata vajalik. See vastab tĂ€pselt olukordadele, kus me ei arvesta kokku pandud tippu vastusesse ja kus me seda teeme. JÀÀb vaid mĂ€rkida, et mĂ”lemal juhul vĂ€heneb sellise operatsiooni tulemus ĂŒhiku vĂ”rra.

Tuleb mĂ€rkida, et sellise lĂ€henemise rakendamine on ausalt öeldes ĂŒsna keeruline tĂ€pselt lĂ€bi viia. Tippude ĂŒhendamine on keeruline operatsioon, kuna tuleb kopeerida naabrite loendid. Kui seda ei tehta ettevaatlikult, vĂ”ib saada asymptoottisesti mittesoovitava tööaja (nĂ€iteks kui pĂ€rast iga ĂŒhendamist kopeerida palju servi). Olen keskendunud tĂ€ielike teede otsimisele kahe astmega tippude seas ja erinevate erijuhtude lahendamisele, nĂ€iteks tsĂŒklitele, mis koosnevad sellistest tipudest vĂ”i kĂ”igist sellistest tipudest peale ĂŒhe.
Lisaks peab see operatsioon olema pööratav, et saaksime rekursioonist naastes grafi algsesse olekusse taastada. Selle tagamiseks ei kustutanud ma ĂŒhendatud tippude servade loendeid, seega teadsin lihtsalt, kuhu servad suunata. Selline graafide rakendus nĂ”uab samuti ettevaatlikkust, kuid tagab ausalt lineaarsed ajad. Ja mitmekĂŒmne tuhande servaga graafid mahuvad tĂ€iesti protsessori vahemĂ€lu, mis annab suurima kiiruselise eelise.
Lineaarne ydur
LÔpuks, kÔige huvitavam osa ydurist.
Alustuseks meenutame, et bipartiidsetes graafides saab minimaalset tippude katet otsida
. Selleks tuleb rakendada algoritmi et leida seal maksimaalne paaritus, ja seejÀrel rakendada .
Lineaarse yduride idee on jÀrgmine: esmalt jagame graafi, st iga tipu asemel v loome kaks tippu
ja
, ja iga serva asemel u â v loome kaks serva
ja
. Saadud graaf on kahekĂŒndaline. Leiame sellest minimaalse tippkatte. MĂ”ned algse graafi tipud sattuvad sinna kaks korda, mĂ”ned vaid ĂŒks kord ja mĂ”ned ei satu sinna ĂŒldse. Nemhauseri-Trotteri teoreem vĂ€idab, et sellisel juhul vĂ”ib eemaldada tipud, mis ei sattunud sinna kordagi, ja jĂ€tta alles need, mis sattusid kaks korda. Veelgi enam, see ĂŒtleb, et allesjÀÀnud tipud (need, mis sattusid ĂŒks kord) tuleb vĂ€hemalt pooled vĂ”tta vastuseks.
Just Ôppisime graafis jÀtma mitte rohkem kui 2k tipu. TÔepoolest, kui vastuses on vÀhemalt pool kÔigist tipudest, siis ei ole neid seal kokku rohkem kui 2k.
Siin suutsin teha vĂ€ikese edusamme. On selge, et nii koostatud tuum sĂ”ltub sellest, milline minimaalne tippkate kahekĂŒndalises graafis me valisime. Sooviksin vĂ”tta sellise, et allesjÀÀnud tipude arv oleks minimaalne. Varem osati seda teha vaid ajaga
. Ma aga mÔtlesin vÀlja selle algoritmi rakenduse ajaga
, seega on seda tuuma vÔimalik otsida graafides, mille suurus on sadade tuhandete tippude piires igas harutamisetapis.
Tulemus
Praktika nÀitab, et minu lahendus töötab hÀsti testides, kus on mitu sadat tippu ja mitu tuhat serva. Sellistes testides vÔib oodata, et lahendus leitakse poole tunni jooksul. Vastuse leidmise tÔenÀosus suureneb, kui graafikus on piisavalt palju tippusid suure astmega, nÀiteks astmega 10 ja rohkem.
Osalemiseks vĂ”istluses tuli lahendused saata aadressile . Seal esitatud andmete pĂ”hjal , minu lahendus avatud testides hoiab kolmandat kohta kahekĂŒmnest, olles selgelt ees teisest. Kui olla tĂ€iesti aus, siis ei ole tĂ€iesti selge, kuidas lahendusi vĂ”istluse jooksul hinnatakse: nĂ€iteks minu lahendus lĂ€bib vĂ€hem teste kui neljandal kohal olev lahendus, kuid töötab kiiremini nendes, mis tal on.
Suletud testide tulemused avaldatakse esimesel juulil.
Allikas: habr.com
