by roundabout, Friday, 16 October 2026, 11:11:16 (1792149076), pushed by roundabout, Friday, 16 October 2026, 11:17:49 (1792149469)
Author identity: Vlad <vlad.muntoiu@gmail.com>
845a727289902f4cc0a271e767ba2bc1874b5900
divizibilitate.xhtml
@@ -0,0 +1,771 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html lang="ro" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8"/>
<title>Algoritmi de divizibilitate</title>
<link rel="stylesheet" href="style.css"/>
</head>
<body>
<h1>Algoritmi de divizibilitate</h1>
<section>
<h2>Problemă cu sume parțiale</h2>
<p>
Se dă o matrice de dimensiuni N și M, de numere naturale, și un
număr natural K; pentru fiecare celulă să se calculeze latura
(impară) minimă a unui pătrat centrat pe celulă astfel încât
suma pe acel pătrat să fie cel puțin K. Pătratul trebuie să fie
inclus în matrice; dacă nu există niciunul trebuie să se afișeze
-1.
</p>
<h3>Exemplu</h3>
<h4>Intrare:</h4>
<pre>6 4 10
1 2 3 4 5 6
36 25 16 9 4 1
7 7 7 7 7 7
10 8 10 3 3 3</pre>
<h4>Ieșire:</h4>
<pre>-1 -1 -1 -1 -1 -1
1 1 1 3 3 -1
-1 3 3 3 3 -1
1 -1 1 -1 -1 -1
</pre>
</section>
<section>
<h2>Aflarea tuturor divizorilor</h2>
<p>
Funcția următoare calculează lista (neordonată) a divizorilor
unui număr. Se folosește de faptul că divizorii sunt în perechi,
cu excepția eventualului radical natural al numărului.
</p>
<pre><code>vector<<b>int</b>> divisors(<b>int const</b> x) {
vector<<b>int</b>> div;
<b>int</b> d;
<b>for</b> (d = 1; d*d < x; ++d) {
<b>if</b> (!(x % d)) {
div.push_back(d);
div.push_back(x / d);
}
}
<b>if</b> (d*d == x) {
div.push_back(d);
}
<b>return</b> div;
}</code></pre>
</section>
<section>
<h2>Aflarea tuturor factorilor primi</h2>
<pre><code>vector<<b>int</b>> prime_factors(<b>int</b> x) {
vector<<b>int</b>> pf;
<b>int</b> d = 2;
<b>while</b> (d*d <= x && x != 1) {
<b>if</b> (!(x % d)) {
pf.push_back(d);
<b>do</b> {
x /= d;
} <b>while</b> (!(x % d));
}
++d;
}
<b>if</b> (x != 1) {
pf.push_back(x);
}
<b>return</b> pf;
}</code></pre>
</section>
<section>
<h2>Cel mai mare divizor comun – Algoritmul lui Euclid</h2>
<pre><code><b>int</b> gcd(<b>int</b> a, <b>int</b> b) {
<b>while</b> (b != 0) {
<b>int const</b> r = a % b;
a = b;
b = r;
}
<b>return</b> a;
}</code></pre>
<p>
<a href="https://en.wikipedia.org/wiki/File:Euclidean_algorithm_1071_462.gif">
Exemplificare vizuală.
</a>
</p>
</section>
<section>
<h2>Ciurul lui Eratostene</h2>
<p>
Este un algoritm antic prin care se poate afla, pentru orice număr
natural mai mic decât o limită, dacă e număr prim.
</p>
<p>
Se începe cu un vector de N elemente, inițial de valoare 0 (0
înseamnă număr prim și 1 înseamnă număr compus). Pozițiile 0 și
1 vor avea valoarea 1. Ideea algoritmului este: pentru fiecare element
din vector începând cu 2, elementul este prim dacă s-a lucrat
la elementele anterioare și încă este nemarcat, deoarece, dacă
era compus, ar fi fost marcat anterior. Dacă este prim, se marchează
toți multiplii elementului, în afară de el însuși.
</p>
<p>
Multiplii unui indice <code>i</code> sunt <code>2*i</code>,
<code>3*i</code> etc. Deoarece multiplii până la <code>i*i</code>
conțin un factor mai mic decât <code>i</code>, nu se mai procesează,
fiind deja marcați prin acest factor mai mic.
</p>
<pre><code>vector<<b>char</b>> get_is_prime(<b>int const</b> size) {
vector<<b>char</b>> sieve(size);
sieve[0] = 1, sieve[1] = 1;
<b>for</b> (<b>int</b> i = 2; i*i < size; ++i) {
<b>if</b> (!sieve[i]) {
<b>for</b> (<b>int</b> j = i*i; j < size; j += i) {
sieve[j] = 1;
}
}
}
<b>return</b> sieve;
}</code></pre>
<div class="table-container">
<table class="sieve-example">
<tr>
<td class="sel1">0</td>
<td class="sel1">1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
<td>6</td>
<td>7</td>
<td>8</td>
<td>9</td>
<td>10</td>
<td>11</td>
<td>12</td>
<td>13</td>
<td>14</td>
<td>15</td>
</tr>
<tr>
<td>16</td>
<td>17</td>
<td>18</td>
<td>19</td>
<td>20</td>
<td>21</td>
<td>22</td>
<td>23</td>
<td>24</td>
<td>25</td>
<td>26</td>
<td>27</td>
<td>28</td>
<td>29</td>
<td>30</td>
<td>31</td>
</tr>
<tr>
<td>32</td>
<td>33</td>
<td>34</td>
<td>35</td>
<td>36</td>
<td>37</td>
<td>38</td>
<td>39</td>
<td>40</td>
<td>41</td>
<td>42</td>
<td>43</td>
<td>44</td>
<td>45</td>
<td>46</td>
<td>47</td>
</tr>
<tr>
<td>48</td>
<td>49</td>
<td>50</td>
<td>51</td>
<td>52</td>
<td>53</td>
<td>54</td>
<td>55</td>
<td>56</td>
<td>57</td>
<td>58</td>
<td>59</td>
<td>60</td>
<td>61</td>
<td>62</td>
<td>63</td>
</tr>
</table>
</div>
<div class="table-container">
<table class="sieve-example">
<tr>
<td class="sel1">0</td>
<td class="sel1">1</td>
<td class="sel4">2</td>
<td>3</td>
<td class="sel2">4</td>
<td>5</td>
<td class="sel2">6</td>
<td>7</td>
<td class="sel2">8</td>
<td>9</td>
<td class="sel2">10</td>
<td>11</td>
<td class="sel2">12</td>
<td>13</td>
<td class="sel2">14</td>
<td>15</td>
</tr>
<tr>
<td class="sel2">16</td>
<td>17</td>
<td class="sel2">18</td>
<td>19</td>
<td class="sel2">20</td>
<td>21</td>
<td class="sel2">22</td>
<td>23</td>
<td class="sel2">24</td>
<td>25</td>
<td class="sel2">26</td>
<td>27</td>
<td class="sel2">28</td>
<td>29</td>
<td class="sel2">30</td>
<td>31</td>
</tr>
<tr>
<td class="sel2">32</td>
<td>33</td>
<td class="sel2">34</td>
<td>35</td>
<td class="sel2">36</td>
<td>37</td>
<td class="sel2">38</td>
<td>39</td>
<td class="sel2">40</td>
<td>41</td>
<td class="sel2">42</td>
<td>43</td>
<td class="sel2">44</td>
<td>45</td>
<td class="sel2">46</td>
<td>47</td>
</tr>
<tr>
<td class="sel2">48</td>
<td>49</td>
<td class="sel2">50</td>
<td>51</td>
<td class="sel2">52</td>
<td>53</td>
<td class="sel2">54</td>
<td>55</td>
<td class="sel2">56</td>
<td>57</td>
<td class="sel2">58</td>
<td>59</td>
<td class="sel2">60</td>
<td>61</td>
<td class="sel2">62</td>
<td>63</td>
</tr>
</table>
</div>
<div class="table-container">
<table class="sieve-example">
<tr>
<td class="sel1">0</td>
<td class="sel1">1</td>
<td class="sel3">2</td>
<td class="sel4">3</td>
<td class="sel1">4</td>
<td>5</td>
<td class="sel1">6</td>
<td>7</td>
<td class="sel1">8</td>
<td class="sel2">9</td>
<td class="sel1">10</td>
<td>11</td>
<td class="sel2">12</td>
<td>13</td>
<td class="sel1">14</td>
<td class="sel2">15</td>
</tr>
<tr>
<td class="sel1">16</td>
<td>17</td>
<td class="sel2">18</td>
<td>19</td>
<td class="sel1">20</td>
<td class="sel2">21</td>
<td class="sel1">22</td>
<td>23</td>
<td class="sel2">24</td>
<td>25</td>
<td class="sel1">26</td>
<td class="sel2">27</td>
<td class="sel1">28</td>
<td>29</td>
<td class="sel2">30</td>
<td>31</td>
</tr>
<tr>
<td class="sel1">32</td>
<td class="sel2">33</td>
<td class="sel1">34</td>
<td>35</td>
<td class="sel2">36</td>
<td>37</td>
<td class="sel1">38</td>
<td class="sel2">39</td>
<td class="sel1">40</td>
<td>41</td>
<td class="sel2">42</td>
<td>43</td>
<td class="sel1">44</td>
<td class="sel2">45</td>
<td class="sel1">46</td>
<td>47</td>
</tr>
<tr>
<td class="sel2">48</td>
<td>49</td>
<td class="sel1">50</td>
<td class="sel2">51</td>
<td class="sel1">52</td>
<td>53</td>
<td class="sel2">54</td>
<td>55</td>
<td class="sel1">56</td>
<td class="sel2">57</td>
<td class="sel1">58</td>
<td>59</td>
<td class="sel2">60</td>
<td>61</td>
<td class="sel1">62</td>
<td class="sel2">63</td>
</tr>
</table>
</div>
<div class="table-container">
<table class="sieve-example">
<tr>
<td class="sel1">0</td>
<td class="sel1">1</td>
<td class="sel3">2</td>
<td class="sel3">3</td>
<td class="sel1">4</td>
<td class="sel4">5</td>
<td class="sel1">6</td>
<td>7</td>
<td class="sel1">8</td>
<td class="sel1">9</td>
<td class="sel1">10</td>
<td>11</td>
<td class="sel1">12</td>
<td>13</td>
<td class="sel1">14</td>
<td class="sel1">15</td>
</tr>
<tr>
<td class="sel1">16</td>
<td>17</td>
<td class="sel1">18</td>
<td>19</td>
<td class="sel1">20</td>
<td class="sel1">21</td>
<td class="sel1">22</td>
<td>23</td>
<td class="sel1">24</td>
<td class="sel2">25</td>
<td class="sel1">26</td>
<td class="sel1">27</td>
<td class="sel1">28</td>
<td>29</td>
<td class="sel2">30</td>
<td>31</td>
</tr>
<tr>
<td class="sel1">32</td>
<td class="sel1">33</td>
<td class="sel1">34</td>
<td class="sel2">35</td>
<td class="sel1">36</td>
<td>37</td>
<td class="sel1">38</td>
<td class="sel1">39</td>
<td class="sel2">40</td>
<td>41</td>
<td class="sel1">42</td>
<td>43</td>
<td class="sel1">44</td>
<td class="sel2">45</td>
<td class="sel1">46</td>
<td>47</td>
</tr>
<tr>
<td class="sel1">48</td>
<td>49</td>
<td class="sel2">50</td>
<td class="sel1">51</td>
<td class="sel1">52</td>
<td>53</td>
<td class="sel1">54</td>
<td class="sel2">55</td>
<td class="sel1">56</td>
<td class="sel1">57</td>
<td class="sel1">58</td>
<td>59</td>
<td class="sel2">60</td>
<td>61</td>
<td class="sel1">62</td>
<td class="sel1">63</td>
</tr>
</table>
</div>
<div class="table-container">
<table class="sieve-example">
<tr>
<td class="sel1">0</td>
<td class="sel1">1</td>
<td class="sel3">2</td>
<td class="sel3">3</td>
<td class="sel1">4</td>
<td class="sel3">5</td>
<td class="sel1">6</td>
<td class="sel4">7</td>
<td class="sel1">8</td>
<td class="sel1">9</td>
<td class="sel1">10</td>
<td>11</td>
<td class="sel1">12</td>
<td>13</td>
<td class="sel1">14</td>
<td class="sel1">15</td>
</tr>
<tr>
<td class="sel1">16</td>
<td>17</td>
<td class="sel1">18</td>
<td>19</td>
<td class="sel1">20</td>
<td class="sel1">21</td>
<td class="sel1">22</td>
<td>23</td>
<td class="sel1">24</td>
<td class="sel1">25</td>
<td class="sel1">26</td>
<td class="sel1">27</td>
<td class="sel1">28</td>
<td>29</td>
<td class="sel1">30</td>
<td>31</td>
</tr>
<tr>
<td class="sel1">32</td>
<td class="sel1">33</td>
<td class="sel1">34</td>
<td class="sel1">35</td>
<td class="sel1">36</td>
<td>37</td>
<td class="sel1">38</td>
<td class="sel1">39</td>
<td class="sel1">40</td>
<td>41</td>
<td class="sel1">42</td>
<td>43</td>
<td class="sel1">44</td>
<td class="sel1">45</td>
<td class="sel1">46</td>
<td>47</td>
</tr>
<tr>
<td class="sel1">48</td>
<td class="sel2">49</td>
<td class="sel1">50</td>
<td class="sel1">51</td>
<td class="sel1">52</td>
<td>53</td>
<td class="sel1">54</td>
<td class="sel1">55</td>
<td class="sel2">56</td>
<td class="sel1">57</td>
<td class="sel1">58</td>
<td>59</td>
<td class="sel1">60</td>
<td>61</td>
<td class="sel1">62</td>
<td class="sel2">63</td>
</tr>
</table>
</div>
<div class="table-container">
<table class="sieve-example">
<tr>
<td class="sel1">0</td>
<td class="sel1">1</td>
<td class="sel3">2</td>
<td class="sel3">3</td>
<td class="sel1">4</td>
<td class="sel3">5</td>
<td class="sel1">6</td>
<td class="sel3">7</td>
<td class="sel1">8</td>
<td class="sel1">9</td>
<td class="sel1">10</td>
<td class="sel3">11</td>
<td class="sel1">12</td>
<td class="sel3">13</td>
<td class="sel1">14</td>
<td class="sel1">15</td>
</tr>
<tr>
<td class="sel1">16</td>
<td class="sel3">17</td>
<td class="sel1">18</td>
<td class="sel3">19</td>
<td class="sel1">20</td>
<td class="sel1">21</td>
<td class="sel1">22</td>
<td class="sel3">23</td>
<td class="sel1">24</td>
<td class="sel1">25</td>
<td class="sel1">26</td>
<td class="sel1">27</td>
<td class="sel1">28</td>
<td class="sel3">29</td>
<td class="sel1">30</td>
<td class="sel3">31</td>
</tr>
<tr>
<td class="sel1">32</td>
<td class="sel1">33</td>
<td class="sel1">34</td>
<td class="sel1">35</td>
<td class="sel1">36</td>
<td class="sel3">37</td>
<td class="sel1">38</td>
<td class="sel1">39</td>
<td class="sel1">40</td>
<td class="sel3">41</td>
<td class="sel1">42</td>
<td class="sel3">43</td>
<td class="sel1">44</td>
<td class="sel1">45</td>
<td class="sel1">46</td>
<td class="sel3">47</td>
</tr>
<tr>
<td class="sel1">48</td>
<td class="sel1">49</td>
<td class="sel1">50</td>
<td class="sel1">51</td>
<td class="sel1">52</td>
<td class="sel3">53</td>
<td class="sel1">54</td>
<td class="sel1">55</td>
<td class="sel1">56</td>
<td class="sel1">57</td>
<td class="sel1">58</td>
<td class="sel3">59</td>
<td class="sel1">60</td>
<td class="sel3">61</td>
<td class="sel1">62</td>
<td class="sel1">63</td>
</tr>
</table>
</div>
</section>
<section>
<h2>Algoritmi asemănători cu Ciurul lui Eratostene</h2>
<p>
Atenție de la ce multiplu începem să parcurgem vectorul.
Dacă trebuie să numărăm toți factorii primi, parcurgem
începând de la <code>i</code>, nu de la <code>i*i</code>.
De asemenea, unii algoritmi cer să realizăm parcurgerea
multiplilor tuturor numerelor, nu doar a celor prime,
în cazul în care vrem să numărăm, de exemplu, toți
divizorii.
</p>
<p>
<b>Observații:</b>
</p>
<ul>
<li>
Pentru orice număr sub 2310 sunt maxim 4 factori primi.
</li>
<li>
Pentru orice număr sub 30030 sunt maxim 5 factori primi.
</li>
<li>
Pentru orice număr sub 510510 sunt maxim 6 factori primi.
</li>
<li>
Pentru orice număr sub 9699690 sunt maxim 7 factori primi.
</li>
<li>
15 factori primi sunt suficienți pentru orice număr natural
de până la 2<sup>64</sup>. Nu putem însă face vreun ciur
de această dimensiune, deoarece nu există calculator cu
atâta memorie.
</li>
<li>
Numerele prime au un singur factor prim, adică numărul însuși.
</li>
</ul>
<h3>Numărul de factori primi</h3>
<p>
De exemplu, 42 are factorii primi 2, 3 și 7.
</p>
<pre><code>vector<<b>char</b>> get_pf_count(<b>int const</b> size) {
vector<<b>char</b>> sieve(size);
sieve[0] = -1, sieve[1] = 0;
<b>for</b> (<b>int</b> i = 2; i < size; ++i) {
<b>if</b> (!sieve[i]) {
<b>for</b> (<b>int</b> j = i; j < size; j += i) {
++sieve[j];
}
}
}
<b>return</b> sieve;
}</code></pre>
<p>
Deoarece pentru orice număr care încape în dimensiunea
memoriei sunt mult sub 256 de factori primi, tipul
<code><b>char</b></code> ar trebui să fie suficient
pentru acest calcul.
</p>
<h3>Suma divizorilor</h3>
<pre><code>vector<<b>int</b>> get_divisor_sum(<b>int const</b> size) {
vector<<b>int</b>> sieve(size);
sieve[0] = -1;
<b>for</b> (<b>int</b> i = 1; i < size; ++i) {
<b>for</b> (<b>int</b> j = i; j < size; j += i) {
sieve[j] += i;
}
}
<b>return</b> sieve;
}</code></pre>
<h3>Cel mai mare/cel mai mic factor prim</h3>
<pre><code>vector<<b>int</b>> get_max_pf(<b>int const</b> size) {
vector<<b>int</b>> sieve(size);
sieve[0] = -1, sieve[1] = -1;
<b>for</b> (<b>int</b> i = 2; i < size; ++i) {
<b>if</b> (!sieve[i]) {
<b>for</b> (<b>int</b> j = i; j < size; j += i) {
sieve[j] = i;
}
}
}
<b>return</b> sieve;
}</code></pre>
<pre><code>vector<<b>int</b>> get_min_pf(<b>int const</b> size) {
vector<<b>int</b>> sieve(size);
sieve[0] = -1, sieve[1] = -1;
<b>for</b> (<b>int</b> i = 2; i < size; ++i) {
<b>if</b> (!sieve[i]) {
sieve[i] = i;
<b>for</b> (<b>int</b> j = i*i; j < size; j += i) {
sieve[j] = i;
}
}
}
<b>return</b> sieve;
}</code></pre>
</section>
<section>
<h2>Algoritmi care folosesc Ciurul lui Eratostene</h2>
<h3>Lista numerelor prime</h3>
<p>
Pur și simplu generăm Ciurul lui Eratostene și apoi memorăm
care indici din el au valoarea 0.
</p>
<pre><code>vector<<b>int</b>> all_prime_numbers(vector<<b>int</b>> <b>const</b> &is_prime) {
vector<<b>int</b>> prime_numbers;
<b>for</b> (<b>int</b> i = 0; i < is_prime.size(); ++i) {
<b>if</b> (is_prime[i]) {
prime_numbers.push_back(i);
}
}
<b>return</b> prime_numbers;
}</code></pre>
<h3>Descompunerea în factori primi</h3>
<p>
Ne amintim că, pentru a descompune în factori primi un număr,
căutam cu un toți factorii primi ai acestuia, folosind un
<code><b>for</b></code> pentru a-i căuta. Problema este că,
dintre numerele naturale, puține sunt numere prime, și nu
are sens să le parcurgem pe toate când ne interesează doar
numerele prime.
</p>
<pre><code>vector<<b>int</b>> prime_factors(vector<<b>int</b>> <b>const</b> &prime_numbers) {
vector<<b>int</b>> pf;
<b>int</b> i = 0;
<b>while</b> (i < prime_numbers.size()
&& prime_numbers[i]*prime_numbers[i] <= x
&& x != 1) {
<b>int const</b> d = prime_numbers[i];
<b>if</b> (!(x % d)) {
pf.push_back(d);
<b>do</b> {
x /= d;
} <b>while</b> (!(x % d));
}
++i;
}
<b>if</b> (x != 1) {
pf.push_back(x);
}
<b>return</b> pf;
}</code></pre>
<p>
Acest algoritm poate descompune numere până la pătratul
limitei ciurului.
</p>
<h3>Descompunerea rapidă în factori primi a numerelor mai mici</h3>
<p>
Să considerăm că ciurul are dimensiunea de 16 777 216,
sau 2<sup>24</sup>, o dimensiune rezonabilă (ocupă 16 megaocteți,
în contextul în care acum în majoritatea problemelor de concurs
se oferă 32–256 MB).
</p>
<p>
Pentru această dimensiune putem descompune în factori primi orice
număr de până la pătratul ei, adică 2<sup>48</sup>, adică
281 474 976 710 656, adică orice număr
natural cu până la 14 cifre zecimale.
</p>
<p>
Dacă nu avem nevoie de descompunerea numerelor atât de mari, putem
lucra cu un algoritm mai eficient. Acesta este un algoritm în care
numărul de operații e proporțional cu suma exponenților factorilor primi.
Dacă algoritmul anterior are ca număr maxim de pași numărul de numere
prime disponibile, acesta are ca număr maxim de pași suma exponenților.
Pentru numere de până la 2<sup>24</sup>, suma exponenților e maxim 23
(se obține chiar folosind doar factori de 2, care cresc cel mai lent).
</p>
<pre><code>vector<<b>int</b>> prime_factors(vector<<b>int</b>> <b>const</b> &min_pf) {
vector<<b>int</b>> pf;
<b>int</b> last_factor = -1;
<b>while</b> (x != 1) {
<b>if</b> (last_factor != min_pf[x]) {
pf.push_back(min_pf[x]);
last_factor = min_pf[x];
}
x /= min_pf[x];
}
<b>return</b> pf;
}</code></pre>
<p>
Acest algoritm se folosește de tehnica precedentă pentru a genera
factorul prim minim. Dacă numărul nu este 1, luăm acest factor prim
și sărim la numărul fără factorul prim.
</p>
</section>
</body>
</html>
style.css
@@ -37,6 +37,22 @@ p {
h1, h2, h3, h4, h5, h6, p, ul, ol, dl, pre {
margin: 0;
}
table:not(.matrix) {
border-collapse: collapse;
:is(td, th) {
border: 1px solid #00000080;
padding: 0.25lh;
}
}
.table-container {
display: block;
overflow-x: auto;
white-space: nowrap;
}
.sieve-example {
table-layout: fixed;
min-width: 100%;
}
.matrix {
border-collapse: collapse;
:is(td, th) {