<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://www.algopedia.ro/wiki/index.php?action=history&amp;feed=atom&amp;title=10_2018_Lectia20</id>
	<title>10 2018 Lectia20 - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://www.algopedia.ro/wiki/index.php?action=history&amp;feed=atom&amp;title=10_2018_Lectia20"/>
	<link rel="alternate" type="text/html" href="https://www.algopedia.ro/wiki/index.php?title=10_2018_Lectia20&amp;action=history"/>
	<updated>2026-04-13T00:43:18Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.44.2</generator>
	<entry>
		<id>https://www.algopedia.ro/wiki/index.php?title=10_2018_Lectia20&amp;diff=16087&amp;oldid=prev</id>
		<title>Bella at 14:42, 26 March 2019</title>
		<link rel="alternate" type="text/html" href="https://www.algopedia.ro/wiki/index.php?title=10_2018_Lectia20&amp;diff=16087&amp;oldid=prev"/>
		<updated>2019-03-26T14:42:34Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;= Tema - rezolvări =&lt;br /&gt;
&lt;br /&gt;
Rezolvări probleme de recursie aici [http://solpedia.francu.com/wiki/index.php/Clasa_a_VII-a_lec%C8%9Bia_3_-_1_oct_2015]&lt;br /&gt;
&lt;br /&gt;
= Lecție - recursivitate, continuare =&lt;br /&gt;
== Exerciții ==&lt;br /&gt;
Rezolvați în clasă următoarele exerciții:&lt;br /&gt;
=== Palindrom ===&lt;br /&gt;
Verificare palindrom:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int putere( int n ) {             // calculam recursiv cea mai mare&lt;br /&gt;
  int p = 1;                      // putere a lui 10 mai mica decit n&lt;br /&gt;
&lt;br /&gt;
  if ( n &amp;lt; 10 )                   // cind n are o singura cifra&lt;br /&gt;
    return 1;                     // cea mai mare putere este 1&lt;br /&gt;
  return 10 * putere( n / 10 );   // adaugam un zero si taiem o cifra&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int palindrom( int n, int p10 ) { // pe baza lui n si a puterii lui 10&lt;br /&gt;
  if ( n &amp;lt; 10 )                   // un numar de o cifra este palindrom&lt;br /&gt;
    return 1;&lt;br /&gt;
  if ( n / p10 != n % 10 )        // testam prima si ultima cifra&lt;br /&gt;
    return 0;&lt;br /&gt;
  return palindrom( n % p10 / 10, p10 / 100 ); // taie prima si ultima cifra&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  int n, pow10;&lt;br /&gt;
&lt;br /&gt;
  scanf( &amp;quot;%d&amp;quot;, &amp;amp;n );&lt;br /&gt;
  pow10 = putere( n );&lt;br /&gt;
  if ( palindrom( n, pow10 ) )&lt;br /&gt;
    printf( &amp;quot;%d este palindrom\n&amp;quot;, n );&lt;br /&gt;
  else&lt;br /&gt;
    printf( &amp;quot;%d nu este palindrom\n&amp;quot;, n );&lt;br /&gt;
  return 0;&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== a&amp;lt;sup&amp;gt;n&amp;lt;/sup&amp;gt; în O(log n) ===&lt;br /&gt;
Calculați &amp;lt;tt&amp;gt;a&amp;lt;sup&amp;gt;n&amp;lt;/sup&amp;gt;&amp;lt;/tt&amp;gt; în mod cît mai eficient (a și n numere naturale). Problema este cunoscută şi sub numele de ridicare la putere în timp logaritmic. Ideea din spatele acestei rezolvări este următoarea:&lt;br /&gt;
&lt;br /&gt;
*Dacă &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt; este par, atunci &amp;lt;tt&amp;gt;a&amp;lt;sup&amp;gt;n&amp;lt;/sup&amp;gt; = a&amp;lt;sup&amp;gt;2*n/2&amp;lt;/sup&amp;gt; = (a&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;)&amp;lt;sup&amp;gt;n/2&amp;lt;/sup&amp;gt;&amp;lt;/tt&amp;gt;&lt;br /&gt;
*Dacă &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt; este impar, atunci &amp;lt;tt&amp;gt;n-1&amp;lt;/tt&amp;gt; este par și avem &amp;lt;tt&amp;gt;a&amp;lt;sup&amp;gt;n&amp;lt;/sup&amp;gt; = a * a&amp;lt;sup&amp;gt;n-1&amp;lt;/sup&amp;gt; =  a * a&amp;lt;sup&amp;gt;2*(n-1)/2&amp;lt;/sup&amp;gt; = a * (a&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;)&amp;lt;sup&amp;gt;(n-1)/2&amp;lt;/sup&amp;gt; = a * (a&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;)&amp;lt;sup&amp;gt;n/2&amp;lt;/sup&amp;gt;&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
În formulele de mai sus am considerat că &amp;lt;tt&amp;gt;/&amp;lt;/tt&amp;gt; este împărțirea întreagă din limbajul C. Se observă că indiferent de paritatea lui &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt;, la fiecare pas al iterației putem transforma &amp;lt;tt&amp;gt;a&amp;lt;/tt&amp;gt; în &amp;lt;tt&amp;gt;a * a&amp;lt;/tt&amp;gt; și apoi putem împărți &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt; la &amp;lt;tt&amp;gt;2&amp;lt;/tt&amp;gt;. Doar în cazurile cînd &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt; este impar vom acumula valoarea curentă a lui &amp;lt;tt&amp;gt;a&amp;lt;/tt&amp;gt; la produsul calculat. Iată soluția bazată pe această idee:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int putere( int a, int n ) {&lt;br /&gt;
  int p;&lt;br /&gt;
&lt;br /&gt;
  if ( n == 0 )&lt;br /&gt;
    return 1;&lt;br /&gt;
  p =  putere( a * a, n / 2 );&lt;br /&gt;
  if ( n % 2 ) // n impar?&lt;br /&gt;
    p *= a;&lt;br /&gt;
&lt;br /&gt;
  return p;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  int a, n;&lt;br /&gt;
&lt;br /&gt;
  scanf( &amp;quot;%d%d&amp;quot;, &amp;amp;a, &amp;amp;n );&lt;br /&gt;
  printf( &amp;quot;%d\n&amp;quot;, putere( a, n ) );&lt;br /&gt;
&lt;br /&gt;
  return 0;&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Permutări ===&lt;br /&gt;
Dîndu-se &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt; să se afișeze toate permutările mulțimii numerelor de la &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; la &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt; în ordine lexicografică.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int v[10];&lt;br /&gt;
char folosit[10];&lt;br /&gt;
&lt;br /&gt;
void perm( int n, int pos ) {&lt;br /&gt;
  int i;&lt;br /&gt;
&lt;br /&gt;
  if ( pos &amp;gt;= n ) {             // daca am umplut n pozitii&lt;br /&gt;
    for ( i = 0; i &amp;lt; n; i++ )   // afiseaza combinarea curenta&lt;br /&gt;
      printf( &amp;quot;%d &amp;quot;, v[i] );&lt;br /&gt;
    printf( &amp;quot;\n&amp;quot; );&lt;br /&gt;
  } else&lt;br /&gt;
    for ( v[pos] = 1; v[pos] &amp;lt;= n; v[pos]++ )&lt;br /&gt;
      if ( !folosit[v[pos]] ) { // daca elementul curent nu e deja folosit&lt;br /&gt;
        folosit[v[pos]] = 1;    // marcheaza-l ca folosit&lt;br /&gt;
        perm( n, pos + 1 );     // genereaza restul permutarii&lt;br /&gt;
        folosit[v[pos]] = 0;    // demarcheaza elementul&lt;br /&gt;
      }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  int n;&lt;br /&gt;
&lt;br /&gt;
  scanf( &amp;quot;%d&amp;quot;, &amp;amp;n );&lt;br /&gt;
  perm( n, 0 );&lt;br /&gt;
  return 0;&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Plată suma ===&lt;br /&gt;
Moduri plată suma cu monede 1, 2, 5: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int mon[3] = { 1, 2, 5 };&lt;br /&gt;
&lt;br /&gt;
int suma( int s, int nrmon ) { // in cite feluri platim s folosind nrmon monede&lt;br /&gt;
  if ( nrmon == 0 )            // daca nu mai avem monede&lt;br /&gt;
    return 0;                  // nu putem plati (zero moduri de plata)&lt;br /&gt;
  if ( s &amp;lt; 0 )                 // o suma negativa nu se poate plati&lt;br /&gt;
    return 0;&lt;br /&gt;
  if ( s == 0 )                // o suma de zero se plateste intr-un singur fel&lt;br /&gt;
    return 1;&lt;br /&gt;
  // putem plati in doua feluri de combinatii&lt;br /&gt;
  // - fie in combinatii care se termina cu ultima moneda din vector&lt;br /&gt;
  // - fie in combinatii care nu folosesc deloc ultima moneda din vector&lt;br /&gt;
  return suma( s - mon[nrmon - 1], nrmon ) + suma( s, nrmon - 1 );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  int s;&lt;br /&gt;
&lt;br /&gt;
  scanf( &amp;quot;%d&amp;quot;, &amp;amp;s );&lt;br /&gt;
  printf( &amp;quot;%d este platibila in %d moduri\n&amp;quot;, s, suma( s, 3 ) );&lt;br /&gt;
  return 0;&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Tipuri de recursie ==&lt;br /&gt;
Există două moduri de implementare a unei soluții recursive:&lt;br /&gt;
=== Recursie Top-Down ===&lt;br /&gt;
Recursie &amp;#039;&amp;#039;&amp;#039;Top-Down&amp;#039;&amp;#039;&amp;#039;: cînd pornim cu o problemă mai mare și apelăm funcția pentru probleme mai mici pe care le folosim ulterior în apelul curent. În subproblema de bază returnăm un rezultat de bază, cum ar fi zero sau unu. În această categorie sînt toate exemplele de mai sus cu excepția combinărilor, permutărilor si sumei de monede.&lt;br /&gt;
&lt;br /&gt;
=== Recursie Bottom-Up ===&lt;br /&gt;
Recursie &amp;#039;&amp;#039;&amp;#039;Bottom-Up&amp;#039;&amp;#039;&amp;#039;: cînd pornim cu o problemă mai mare și cu un acumulator de soluție, care inițial este vid (zero sau unu), iar în apelurile ulterioare reducem mărimea problemei adăugînd (construind) soluția finală în acumulator. Exemplele de combinări și permutări sînt Bottom-Up în care acumulatorul este vectorul și nu este transmis în mod explicit, deși am putea face acest lucru.&lt;br /&gt;
=== Exemple de recursie Bottom-Up cu acumulator ===&lt;br /&gt;
Încercați să rezolvați în clasă următoarele exerciții:&lt;br /&gt;
==== Factorial ====&lt;br /&gt;
Funcția factorial implementată recursiv bottom-up cu acumulator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int fact( int n, int ac ) {&lt;br /&gt;
  if ( n == 1 )                 // cind n = 1 am terminat calculul in ac&lt;br /&gt;
    return ac;&lt;br /&gt;
  return fact( n - 1, n * ac ); // adaugam n la acumulator&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  int n;&lt;br /&gt;
&lt;br /&gt;
  scanf( &amp;quot;%d&amp;quot;, &amp;amp;n );&lt;br /&gt;
  printf( &amp;quot;%d! = %d\n&amp;quot;, n, fact( n, 1 ) );&lt;br /&gt;
&lt;br /&gt;
  return 0;&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Palindrom ====&lt;br /&gt;
Verificarea că un număr este palindrom este, în fapt, mai ușor de scris bottom-up. În acumulator vom construi inversul numărului &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt;. La final vom compara inversul cu originalul. Iată implementarea (comparați cu implementarea top-down):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int palindrom( int n, int nc, int ac ) {&lt;br /&gt;
  if ( n == 0 )            // cind n=0 ac este inversul lui nc&lt;br /&gt;
    return nc == ac;       // daca sint egale, returnam 1, altfel 0&lt;br /&gt;
  return palindrom( n / 10, nc, ac * 10 + n % 10 ); // taiem o cifra tin n&lt;br /&gt;
}                                                   // si o adaugam la ac&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  int n;&lt;br /&gt;
&lt;br /&gt;
  scanf( &amp;quot;%d&amp;quot;, &amp;amp;n );&lt;br /&gt;
  if ( palindrom( n, n, 0 ) )&lt;br /&gt;
    printf( &amp;quot;%d este palindrom\n&amp;quot;, n );&lt;br /&gt;
  else&lt;br /&gt;
    printf( &amp;quot;%d nu este palindrom\n&amp;quot;, n );&lt;br /&gt;
  return 0;&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== a&amp;lt;sup&amp;gt;n&amp;lt;/sup&amp;gt; în O(n) ====&lt;br /&gt;
Iată varianta recursivă bottom-up cu acumulator a funcției putere:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int putere( int a, int n, int ac ) {&lt;br /&gt;
  if ( n == 0 ) // cind puterea e zero acumulatorul are rezultatul&lt;br /&gt;
    return ac;&lt;br /&gt;
  return putere( a, n - 1, a * ac ); // acumuleaza a la rezultat&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  int n, a;&lt;br /&gt;
&lt;br /&gt;
  scanf( &amp;quot;%d%d&amp;quot;, &amp;amp;a, &amp;amp;n );&lt;br /&gt;
  printf( &amp;quot;%d^%d = %d\n&amp;quot;, a, n, putere( a, n, 1 ) );&lt;br /&gt;
  return 0;&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== a&amp;lt;sup&amp;gt;n&amp;lt;/sup&amp;gt; în O(log n) ====&lt;br /&gt;
Iată varianta recursivă bottom-up cu acumulator a funcției putere calculată în timp logaritmic:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// in permanenta a^n * ac detine rezultatul&lt;br /&gt;
// cind n devine zero a^n * ac = ac, deci rezultatul este chiar ac&lt;br /&gt;
int putere( int a, int n, int ac ) {&lt;br /&gt;
  if ( n == 0 ) // cind puterea e zero acumulatorul are rezultatul&lt;br /&gt;
    return ac;&lt;br /&gt;
  if ( n % 2 )                         // cind n impar&lt;br /&gt;
    ac *= a;                           // acumuleaza a la rezultat&lt;br /&gt;
  return putere( a * a, n / 2, ac );   // totuna cu (a*a)^(n/2)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  int n, a;&lt;br /&gt;
&lt;br /&gt;
  scanf( &amp;quot;%d%d&amp;quot;, &amp;amp;a, &amp;amp;n );&lt;br /&gt;
  printf( &amp;quot;%d^%d = %d\n&amp;quot;, a, n, putere( a, n, 1 ) );&lt;br /&gt;
  return 0;&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Transformarea recursiei din Top-Down în Bottom-Up ===&lt;br /&gt;
Orice funcție recursivă de tip Top-Down care pe orice cale de execuție are un singur apel recursiv se poate transforma într-o funcție de tip Bottom-Up prin adăugarea unui parametru acumulator. De ce ne interesează acest lucru? Datorită &amp;#039;&amp;#039;recursiei la coadă&amp;#039;&amp;#039;. Recursia la coadă este definită ca un apel recursiv exact la ieșirea din funcție. Toate exemplele pe care le-am dat la recursia bottom-up sînt recursive la coadă. Exemplele date la top-down nu sînt recursive la coadă, cu excepția lui &amp;lt;tt&amp;gt;cmmdc&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Recursia la coadă este foarte utilă, deoarece nu consumă stivă. În mod normal fiecare apel de funcție consumă cel puțin patru octeți pe stivă (adresa de întoarcere). Ei bine, în cazul apelurilor recursive la coadă stiva consumată este zero (ca să fim exacţi nu este zero, ci &amp;#039;&amp;#039;O(1)&amp;#039;&amp;#039;). Explicația acestui fapt este prea tehnică pentru a o detalia aici. Ea ține de modul în care este organizat calculatorul modern Von Neumann și de modul în care se execută apelurile de funcții.&lt;br /&gt;
&lt;br /&gt;
Ce înseamnă pentru noi acest lucru? Înseamnă că putem chema o funcție, recursiv, de un milion de ori, fără să depășim memoria stivă. Ce înseamnă pentru omenire acest lucru? Înseamnă că putem avea întregi limbaje care nu conțin instrucțiuni de ciclare (&amp;lt;tt&amp;gt;while&amp;lt;/tt&amp;gt; sau &amp;lt;tt&amp;gt;for&amp;lt;/tt&amp;gt;), cum ar fi limbajele funcționale (&amp;#039;&amp;#039;&amp;#039;Scheme&amp;#039;&amp;#039;&amp;#039;, &amp;#039;&amp;#039;&amp;#039;Lisp&amp;#039;&amp;#039;&amp;#039;) sau limbajele logice (&amp;#039;&amp;#039;&amp;#039;Prolog&amp;#039;&amp;#039;&amp;#039;). Acest lucru este posibil deoarece recursivitatea la coadă poate înlocui cu succes buclele. Aceste limbaje se bazează pe definiția recursivă a algoritmului. În unele cazuri programele sînt mai clare într-o gîndire recursivă.&lt;br /&gt;
&lt;br /&gt;
= Temă =&lt;br /&gt;
== Clasa a 7-a ==&lt;br /&gt;
[http://varena.ro/runda/2015-10-08-clasa-7-tema-4 Tema 4 clasa a 7&amp;lt;sup&amp;gt;a&amp;lt;/sup&amp;gt;]&lt;br /&gt;
* [http://varena.ro/problema/fibonacci fibonacci]&lt;br /&gt;
* [http://varena.ro/problema/sumacfnr suma cifrelor unui număr]&lt;br /&gt;
* [http://varena.ro/problema/hanoi turnurile din Hanoi] , animatie :http://www.puzzle.ro/ro/play_toh.htm&lt;br /&gt;
* [http://varena.ro/problema/aranjamente aranjamente]&lt;br /&gt;
&lt;br /&gt;
Tema constă din patru probleme. Primele două trebuie rezolvate folosind recursivitate bottom-up cu acumulator și recursie la coadă. A treia este o problemă celebră de introducere în recursivitate. A patra problemă este foarte grea. La această problemă  este în regulă să luați doar 50p. Încercați să luați 100p.&lt;br /&gt;
&lt;br /&gt;
Rezolvări aici [http://solpedia.francu.com/wiki/index.php/Clasa_a_VII-a_lec%C8%9Bia_4_-_8_oct_2015]&lt;/div&gt;</summary>
		<author><name>Bella</name></author>
	</entry>
</feed>