Varg ütleb:
Tere pärastlõunast, ma tõesti ei saa aru, kuidas see funktsioon arvutusi teeb.
{
kui (n==1) tagastab 1; //kui uus väärtus on 1, siis lisame selle 1-ga, mitte eelmisega. sest eelmine on null. ja liitmine 1+0 on lõpmatu.
else return summa(n-1)+n; //aga kui n>1, siis lisa see eelmise väärtusega, mis võrdub kõigi elementide summaga kuni n
}
Minu arusaamist mööda on n-s 5, tingimus ei sobi siis on täidetud see kood summa(n-1)+n ehk siis 5-le liidetakse midagi, mis saadi sulgudes lahutamise teel, aga mis on (5 - 1)+5 ja kui jah, siis mis selle aritmeetilise tehte peatab:?: :?: :?: Mis on eelmine väärtus, kust see tuleb, millega see võrdub:?: :?: :?:
Jah, peaaegu kõik on nii, nagu ma aru sain (viimases lõigus näitasite rekursiooni))), kuid jääb küsimus: kuidas saadakse summa, mis seejärel ekraanil kuvatakse?
Töötan enda jaoks Dev C++-ga see näide kuvab summa ==15, kui lugeda nii nagu näites kirjas, siis tuleb summa erinevaks.
Kirjutasin eespool, võtame (5-1)+5=4+5=9
:
1+2+3+4+5 = 15. Näide väljastab õigesti.(5) //Andsime funktsioonile 5, kontrollisime selle võrdsust ühega. ei ole võrdne, kutsus funktsiooni uuesti, edastades sellesse 5-1
(5-1+(5)) //...
(4-1+(5-1+(5)))
(3-1+(4-1+(5-1+(5))))
(2-1+(3-1+(4-1+(5-1+(5)))))2-1 == 1, funktsiooni kutsumine on lõpetatud.
(2-1+(3-1+(4-1+(5-1+(5))))) == 15
see on tulemus.
Siin on funktsiooni tulemuseks kahe esimese arvu erinevus ja n on ülejäänud parempoolne osa
__________________________________
Peate lihtsalt funktsioonist õigesti aru saama ja aktsepteerima seda arvutatud väärtusena, mitte mõistma seda muutujana. See on sarnane muutujale, kuid lähemal arvutuslikule konstandile, kuigi see pole konstant, on seda lihtsalt niimoodi mugavam tajuda.
Jah, jah, jah, mul ei olnud aega kirjutada, et sain aru, kõik on õige, midagi ei saanud kohe läbi. Aitäh hea sait))
Ja see ei ole 8. real ikkagi täiesti loogiline, kui muudate tagastatud numbri 1-st 2-ks, muutub summa 16-ks. Kuidas on see tingimus seotud 9. reaga?
Ka sellega on kõik selge, lihtsalt return 2 lisab oma ilmajäämise niiöelda summale juurde.
:
mitte lisatäht, vaid see kaks ja kui kirjutada -3, siis ühekordse liitmisel lahutab see kolm jne.
Kogu loogika seisneb selles, et iga rekursiivne funktsioon vajab tagastuspunkti.
Seos üheksanda reaga seisneb selles, et rekursiivsete kõnede ajal suunatakse summa funktsioonile arv, seda arvu vähendatakse iga kord ühe (n-1) võrra, seda tulemust n-1 kontrollitakse võrdsuse suhtes; üks ja kui võrdsus on tõene, liidetakse kogu saadud summa selle deklaratsiooni numbriga. vastasel juhul liidetakse kogu summa selle uue n-1-ga
Rekursioon on üsna tavaline nähtus, mis ei esine mitte ainult teadusvaldkondades, vaid ka nendes Igapäevane elu. Näiteks Droste efekt, Sierpinski kolmnurk jne. Lihtsaim viis rekursiooni nägemiseks on suunata veebikaamera arvutimonitori ekraanile, loomulikult pärast selle sisselülitamist. Seega salvestab kaamera arvutiekraani pildi ja kuvab selle sellel ekraanil, see on midagi suletud ahela taolist. Selle tulemusena näeme midagi tunneliga sarnast.
Programmeerimises on rekursioon tihedalt seotud funktsioonidega, just tänu funktsioonidele on programmeerimises olemas selline asi nagu rekursioon või rekursiivne funktsioon. Lihtsate sõnadega, rekursioon on funktsiooni (meetodi) osa määratlus iseenda kaudu, see tähendab, et see on funktsioon, mis kutsub ennast otse (oma kehas) või kaudselt (teise funktsiooni kaudu). Tüüpiline rekursiivsed probleemid on ülesanded: n! leidmine, Fibonacci arvud. Oleme sellised probleemid juba lahendanud, kuid kasutades silmuseid, see tähendab iteratiivselt. Üldiselt võib öelda, et kõike iteratiivselt lahendatavat saab lahendada rekursiivselt ehk rekursiivse funktsiooni abil. Kogu lahendus taandub põhi- või, nagu seda nimetatakse ka, põhijuhtumi lahendamisele. On olemas selline asi nagu rekursioonisamm või rekursiivne kõne. Juhul, kui keeruka probleemi lahendamiseks kutsutakse välja rekursiivne funktsioon (mitte põhijuhtum), siis teatud arv rekursiivsed kõned või sammud ülesande taandamiseks lihtsamaks. Ja nii edasi, kuni jõuame põhilahendus. Töötame välja programmi, mis deklareerib rekursiivse funktsiooni, mis arvutab n!
"stdafx.h" #include
// kood Code::Blocks
// Dev-C++ kood
// factorial.cpp: määrab konsoolirakenduse sisenemispunkti. #kaasa
IN read 7, 9, 21 Andmetüüp deklareeritakse märgita long int , kuna faktoriaali väärtus kasvab väga kiiresti, näiteks juba 10! = 3 628 800 Kui andmetüübi suurus ei ole piisav, saame tulemuseks täiesti vale väärtuse. Kood deklareerib rohkem operaatoreid, kui on vaja n! leidmiseks. Seda tehakse selleks, et programm näitaks pärast käivitamist, mis toimub rekursiivsete kõnede igal etapil. Pange tähele esiletõstetud koodiridu, read 23, 24, 28 on n! rekursiivne lahendus. 23., 24. read on rekursiivse funktsiooni põhilahendus, st niipea, kui muutuja väärtus f võrdub 1 või 0 (kuna me teame, et 1! = 1 ja 0! = 1), rekursiivsed kõned peatuvad ja väärtusi hakatakse tagastama iga rekursiivse kõne kohta. Kui esimese rekursiivse kõne väärtus tagastab, tagastab programm arvutatud faktoriaali väärtuse. IN rida 28 funktsioon faktoriaal() kutsub ennast, kuid selle argument on ühe võrra väiksem. Argumenti vähendatakse iga kord, et jõuda konkreetse lahenduseni. Programmi tulemus (vt joonis 1).
Sisestage n!: 5 1. sammu tulemus = 0 2. sammu tulemus = 0 3. sammu tulemus = 0 4. sammu tulemus = 0 5! = 120
Joonis 1 – Rekursioon C++ keeles
Programmi tulemuse põhjal on iga samm selgelt nähtav ja tulemus igal sammul on null, välja arvatud viimane rekursiivne kõne. Oli vaja arvutada viis faktoriaali. Programm tegi neli rekursiivset kõnet ja viiendal kõnel leiti baasjuhtum. Ja kui programmil oli baasjuhtumile lahendus, lahendas see eelmised sammud ja väljastas üldtulemuse. Joonisel 1 on näha vaid neli sammu, sest viiendas etapis leiti osalahend, mis lõppkokkuvõttes tagastas lõpplahenduse ehk 120. Joonisel 2 on rekursiivne arvutusskeem 5!. Diagramm näitab selgelt, et esimene tulemus tagastatakse konkreetse lahenduseni jõudmisel, kuid mitte kohe pärast iga rekursiivset kõnet.
Joonis 2 – Rekursioon C++ keeles
Nii et leida 5! vaja teada 4! ja korrutage see 5-ga; 4! = 4 * 3! ja nii edasi. Vastavalt joonisel 2 näidatud skeemile taandatakse arvutus erijuhtumi leidmiseni, see tähendab 1!, misjärel tagastatakse väärtused kordamööda igale rekursiivsele kõnele. Viimane rekursiivne kõne tagastab väärtuse 5!.
Töötame faktoriaali leidmise programmi ümber, et saada faktoriaalide tabel. Selleks deklareerime for-tsükli, milles kutsume välja rekursiivse funktsiooni.
// kood Code::Blocks // Dev-C++ kood // factorial.cpp: määrab konsoolirakenduse sisenemispunkti. #kaasa IN read 16-19 deklareeritakse tsükkel, milles kutsutakse välja rekursiivne funktsioon. Kõik programmis mittevajalik on kommenteeritud. Pärast programmi käivitamist peate sisestama väärtuse, milleni faktoriaalid tuleb arvutada. Programmi tulemus on näidatud joonisel 3. Sisestage n!: 14 1!=1 2!=2 3!=6 4!=24 5!=120 6!=720 7!=5040 8!=40320 9!=362880 10!=3628800 11!=39916800 1 !=479001600 13!=6227020800 14!=87178291200 Joonis 3 – Rekursioon C++ keeles Nüüd näete, kui kiiresti faktoriaal muide suureneb, tulemus on juba 14! ei ole õige, see on andmetüübi suuruse puudumise tagajärg. Õige väärtus on 14! = 87178291200. Vaatame veel üht tüüpilist probleemi – Fibonacci arvude leidmist rekursiooni abil. Allpool on sellise probleemi rekursiivse lahenduse kood. Sisestame samale reale Fibonacci seeria numbri järjekorranumbri ja programm leiab Fibonacci seeriast kõik numbrid, mille seerianumbrid on sisestatust väiksemad või sellega võrdsed. // fibonacci.cpp: määrab konsoolirakenduse sisenemispunkti. #include "stdafx.h" #include // kood Code::Blocks // Dev-C++ kood // fibonacci.cpp: määrab konsoolirakenduse sisenemispunkti. #kaasa IN rida 6 raamatukogu ühendatud Sisestage Fibonacci seeria number: 30 1 = 0 2 = 1 3 = 1 4 = 2 5 = 3 6 = 5 7 = 8 8 = 13 9 = 21 10 = 34 11 = 55 12 = 89 13 = 144 14 = 23 15 = 377 16 = 610 17 = 987 18 = 1597 19 = 2584 20 = 4181 21 = 6765 22 = 10946 23 = 17711 24 = 28657 25 = 2 6 = 2 6 = 2 6 = 2 6 = 2 8 = 196418 29 = 317811 30 = 514229 Joonis 4 – Rekursioon C++ keeles Lahendus taandub keerulise probleemi jagamisele kaheks lihtsamaks. Näiteks Fibonacci seeria kolmanda numbri leidmiseks peate esmalt leidma esimese ja teise ning seejärel need lisama. Esimene arv on erijuhtum ja on võrdne 0-ga (null), teine arv on samuti erijuhtum ja on võrdne 1-ga. Seetõttu on Fibonacci seeria kolmas arv võrdne esimese ja teise = summaga. 1. Rekursiivne funktsioon, mille me programmeerisime Fibonacci seerianumbrite otsimiseks, põhjendas ligikaudu samal viisil. Töötame välja veel ühe rekursiivse programmi, mis lahendab klassikalise probleemi - “Hanoi torn”. Antud on kolm varrast, millest ühel on virn n-nda arvu kettaid ja kettad ei ole ühesuurused (erineva läbimõõduga kettad) ja on paigutatud nii, et liikudes ülevalt alla piki varda suureneb ketaste läbimõõt järk-järgult. See tähendab, et väiksemad kettad peaksid asuma ainult suurematel ketastel. Peate selle ketaste virna algsest varrast teisaldama kahest ülejäänud varrast (enamasti on see kolmas varras). Kasutage ühte varrastest abina. Korraga saate liigutada ainult ühte ketast ja suuremat ketast ei tohiks kunagi asetada väiksema ketta peale. Oletame, et peate viima kolm ketast esimeselt vardalt kolmandale, mis tähendab, et teine varras on abiseade. Selle probleemi visuaalne lahendus on rakendatud Flashis. Animatsiooni käivitamiseks klõpsake nuppu Start, selle peatamiseks nuppu stop. Programm peab olema kirjutatud n-nda arvu ketaste jaoks. Kuna me lahendame selle ülesande rekursiivselt, peame esmalt leidma lahenduse erijuhud. Selles probleemis on ainult üks erijuhtum - see on siis, kui on vaja liigutada ainult ühte ketast ja sel juhul pole isegi abivarda vaja, kuid me lihtsalt ei pööra sellele tähelepanu. Nüüd on vaja korraldada rekursiivne lahendus, kui ketaste arv on rohkem kui üks. Tutvustame mõnda tähistust, et mitte liiga palju kirjutada: <Б>
— varras, millel kettad algselt asuvad (alusvarras); Edasi kasutame ülesande lahendamise algoritmi kirjeldamisel neid tähistusi. Kolme ketta teisaldamiseks <Б>
peal <Ф>
peame esmalt kaks ketast teisaldama <Б>
peal <П>
ja seejärel liigutage kolmas ketas (suurim) asukohta <Ф>
, sest <Ф>
tasuta Liikuma n kettad koos <Б>
peal <Ф>
me peame kõigepealt liikuma n-1 kettad koos <Б>
peal <П>
ja seejärel teisaldage n-s ketas (suurim) asukohta <Ф>
, sest <Ф>
tasuta Pärast seda peate liikuma n-1 kettad koos <П>
peal <Ф>
, varda kasutamise ajal <Б>
abimehena. Need kolm toimingut on kogu rekursiivne algoritm. Sama algoritm pseudokoodis: // hanoi_tower.cpp: määrab konsoolirakenduse sisenemispunkti. // Programm, mis lahendab rekursiivselt Hanoi torni probleemi #include "stdafx.h" #include // kood Code::Blocks // Dev-C++ kood // hanoi_tower.cpp: määrab konsoolirakenduse sisenemispunkti. // Programm, mis lahendab rekursiivselt Hanoi torni probleemi #include Joonisel 5 on kujutatud Tower of Hanoi rekursiivse programmi näide. Esiteks sisestasime ketaste arvu, mis võrdub kolmega, seejärel tutvustasime alusvarda (esimene) ja määrasime lõpliku varda (kolmanda). Automaatselt sai teine ritv abiks. Programm andis tulemuse, mis kattub täielikult selle probleemi animeeritud lahendusega. Ketaste arvu sisestamine: 3 Sisestage põhivarda arv: 1 Sisestage lõpliku varda arv: 3 1) 1 -> 3 2) 1 -> 2 3) 3 -> 2 4) 1 -> 3 5) 2 -> 1 6) 2 -> 3 7) 1 -> 3 Joonis 5 – Rekursioon C++ keeles Jooniselt on näha, et kõigepealt liigub ketas ühelt vardalt kolmele, siis ühelt vardalt teisele, kolmelt vardalt vardalekaks jne. See tähendab, et programm kuvab lihtsalt ketta liigutuste jada ja minimaalse sammude arvu, mille jooksul kõik kettad liigutatakse. Kõiki neid probleeme saab lahendada iteratiivselt. Tekib küsimus: "Kumba on parem lahendada, iteratiivselt või rekursiivselt?" Vastan: “Rekursiooni miinuseks on see, et see kulutab oluliselt rohkem arvutiressursse kui iteratsioon. Selle tulemuseks on suur koormus nii RAM-ile kui ka protsessorile. Kui on ilmne, et konkreetset probleemi saab lahendada iteratiivselt, siis tuleks seda kasutada teistmoodi, kasutades rekursiooni! Olenevalt lahendatavast probleemist muutub programmide kirjutamise keerukus ühe või teise lahendusmeetodi kasutamisel. Kuid sagedamini on koodi loetavuse seisukohalt rekursiivsel meetodil lahendatud probleem palju selgem ja lühem. Tere Habrahabr! Selles artiklis räägime rekursiooniprobleemidest ja nende lahendamisest. Programmeerimises on rekursioon tihedalt seotud funktsioonidega, just tänu funktsioonidele on programmeerimises olemas selline asi nagu rekursioon ehk rekursiivne funktsioon. Lihtsamalt öeldes on rekursioon funktsiooni (meetodi) osa määratlus iseenda kaudu, see tähendab, et see on funktsioon, mis kutsub ennast otse (oma kehas) või kaudselt (teise funktsiooni kaudu). Rekursioonist on palju räägitud. Siin on mõned head ressursid: võrgust Iga rekursiivsel kujul rakendatud algoritmi saab iteratiivsel kujul ümber kirjutada ja vastupidi. Jääb küsimus, kas see on vajalik ja kui tõhus see on. Selle õigustamiseks võib tuua järgmised argumendid. Alustuseks võime meenutada rekursiooni ja iteratsiooni määratlusi. Rekursioon on andmetöötluse korraldamise viis, mille käigus programm kutsub ennast otse või teiste programmide abil. Iteratsioon on andmetöötluse korraldamise viis, kus teatud toiminguid korratakse mitu korda, ilma et see tooks kaasa rekursiivseid programmikutseid. Pärast seda võime järeldada, et need on vastastikku vahetatavad, kuid mitte alati samade kuludega ressursside ja kiiruse osas. Selle põhjenduseks võime tuua järgmise näite: on funktsioon, milles teatud algoritmi organiseerimiseks on tsükkel, mis sooritab toimingute jada sõltuvalt loenduri hetkeväärtusest (see ei pruugi sõltuda see). Kuna tsükkel on olemas, tähendab see, et keha kordab toimingute jada - tsükli iteratsioone. Saate liigutada toimingud eraldi alamprogrammi ja edastada sellele loenduri väärtuse, kui see on olemas. Alamprogrammi täitmise lõppedes kontrollime tsükli täitmise tingimusi ja kui see on tõene, jätkame alamprogrammi uue kutsega, kui see on vale, lõpetame täitmise. Sest Panime kogu tsükli sisu alamprogrammi, mis tähendab, et alamprogrammi on paigutatud ka tsükli täitmise tingimus ning selle saab kätte funktsiooni tagastusväärtuse, viitega edastatavate parameetrite või alamprogrammile pointeriga , aga ka globaalseid muutujaid. Lisaks on lihtne näidata, et tsüklist antud alamprogrammi kõne saab hõlpsasti teisendada alamprogrammi enda kõneks või mittekõneks (väärtuse tagastamine või lihtsalt töö lõpetamine), juhindudes teatud tingimustest (neist, mis olid varem tsükliseisundis). Kui nüüd vaadata meie abstraktset programmi, siis näeb see umbkaudu välja nagu väärtuste edastamine alamprogrammile ja nende kasutamine, mida alamprogramm selle lõppedes muudab, s.t. asendasime iteratiivse tsükli antud algoritmi lahendamiseks alamprogrammi rekursiivse kutsega. Iteratiivsele lähenemisele rekursiooni toomise ülesanne on sümmeetriline. Kokkuvõtteks võime väljendada järgmisi mõtteid: iga lähenemisviisi jaoks on oma ülesannete klass, mille määravad konkreetse ülesande konkreetsed nõuded. Selle kohta saate lisateavet Seega koosneb rekursiivne funktsioon Avalik klass Lahendus ( public static int recursion(int n) ( // väljumise tingimus // Põhijuhtum // millal lõpetada rekursiooni kordamine? if (n == 1) ( return 1; ) // Rekursiooni samm / rekursiivne tingimus tagastamine recursion(n - 1) * n ) public static void main(String args) ( System.out.println(recursion(5)); // rekursiivse funktsiooni kutsumine ) Siin on põhitingimus tingimus, kui n=1. Kuna me teame, et 1!=1 ja arvutada 1! me ei vaja midagi. Arvutage 2! saame kasutada 1!, st. 2!=1!*2. Arvutage 3! vajame 2!*3... N arvutamiseks! me vajame (n-1)!*n. See on rekursiooni samm. Teisisõnu, arvu n faktoriaalväärtuse saamiseks piisab eelmise arvu faktoriaalväärtuse korrutamisest n-ga. Sildid:
<П>
- abi- või vahevarras;
<Ф>
— lõppvarras – varras, mille külge tuleb kettad liigutada.
n-1 kolima <П>
n kolima <Ф>
n-1 liikuma <П>
peal <Ф>
, kasutamise ajal <Б>
abimehenaLühidalt rekursioonist
Rekursioon on üsna tavaline nähtus, mis ei esine mitte ainult teaduse valdkondades, vaid ka igapäevaelus. Näiteks Droste efekt, Sierpinski kolmnurk jne. Üks viis rekursiooni nägemiseks on suunata veebikaamera arvutimonitori ekraanile, loomulikult pärast selle sisselülitamist. Seega salvestab kaamera arvutiekraani pildi ja kuvab selle sellel ekraanil, see on midagi suletud ahela taolist. Selle tulemusena näeme midagi tunneliga sarnast.
Eeldatakse, et lugeja on rekursiooniga teoreetiliselt tuttav ja teab, mis see on. Selles artiklis pöörame rohkem tähelepanu rekursiooniprobleemidele. Ülesanded
Rekursiooni õppimisel on kõige tõhusam viis rekursiooni mõistmiseks probleemide lahendamine. Kuidas lahendada rekursiooniprobleeme?
Kõigepealt peate mõistma, et rekursioon on omamoodi liialdus. Üldiselt võib öelda, et kõike iteratiivselt lahendatavat saab lahendada rekursiivselt ehk rekursiivse funktsiooni abil.
Nii nagu loendusel (tsüklil), peab ka rekursioonil olema peatustingimus – põhijuhtum (muidu, nagu tsükkelgi, töötab rekursioon igavesti – lõpmatu). See tingimus on juhtum, milleni rekursioon läheb (rekursiooni samm). Igal etapil kutsutakse välja rekursiivne funktsioon, kuni järgmine väljakutse käivitab põhitingimuse ja rekursioon peatub (õigemini naaseb viimase funktsioonikutseni). Kogu lahendus taandub põhijuhtumi lahendamisele. Juhul, kui keerulise probleemi lahendamiseks (mitte põhijuhtumi) kutsutakse välja rekursiivne funktsioon, tehakse rida rekursiivseid väljakutseid või samme, et probleem lihtsamaks muuta. Ja nii edasi, kuni saame põhilahenduse.
Vaatame seda faktoriaali leidmise näitel:
Lisa märksõnu