Mundarija (21)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. Ikki qism
- 2.2. Chuqurlik chegarasi
- 2.3. Memoizatsiya
- 2.4. Rekursiya vs iteratsiya
- 2.5. Rekursiyani iteratsiyaga aylantirish
- 2.6. Quyruqli rekursiya va Python
- 2.7. Amaliy naqshlar
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — Asoslar va chuqurlik
- Misol 2 — Memoizatsiya
- Misol 3 — Rekursiya vs iteratsiya
- Misol 4 — Amaliy: klassik algoritmlar
- 5. To'g'ri va noto'g'ri tushunishlar
- 6. Keng tarqalgan xatolar va yechimlari
- 7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 8. Eng yaxshi amaliyotlar
- 9. Amaliy topshiriq
- Xulosa
7.13-dars: Rekursiya
7-QISM — FUNKSIYALAR · 13-dars
1. Kirish va motivatsiya
Rekursiya — funksiya o'zini chaqiradi:
def faktorial(n):
if n <= 1:
return 1
return n * faktorial(n - 1)
faktorial(5) # 120Ba'zi masalalar rekursiv tabiatan:
# Fayl tizimi daraxti
def hajm(yol):
if yol.is_file():
return yol.stat().st_size
return sum(hajm(x) for x in yol.iterdir())
# JSON ichma-ich tuzilmasi
def barglar(d):
for v in d.values():
if isinstance(v, dict):
yield from barglar(v)
else:
yield vLekin Pythonda rekursiya bilan uch muammo bor:
def fib(n):
return n if n < 2 else fib(n-1) + fib(n-2)
fib(35) # ⚠️ ~5 soniya — eksponensialdef sanagich(n):
return 0 if n == 0 else 1 + sanagich(n - 1)
sanagich(2000) # ❌ RecursionErrordef yigindi(r):
return 0 if not r else r[0] + yigindi(r[1:]) # ⚠️ O(n²) — kesimBu darsda:
- Rekursiyaning ikki qismi: asos va qadam
- Chuqurlik chegarasi va nega u bor
- Memoizatsiya — eksponensialdan chiziqliga
- Rekursiya vs iteratsiya — qachon qaysi biri
- Quyruqli rekursiya va nega Pythonda yo'q
- Amaliy naqshlar: daraxtlar, "bo'l va hukmronlik qil", backtracking
2. Nazariya — chuqur tushuntirish
2.1. Ikki qism
Har rekursiv funksiyada ikki narsa bo'lishi shart:
1. Asos holat (base case) — rekursiyani to'xtatadi:
def faktorial(n):
if n <= 1: # ⭐ ASOS
return 1
return n * faktorial(n - 1) # ⭐ QADAM2. Rekursiv qadam — masalani kichraytiradi.
Asos holat yo'q → cheksiz rekursiya:
def yomon(n):
return n * yomon(n - 1) # ❌ RecursionError
def yomon2(n):
if n <= 1:
return 1
return n * yomon2(n) # ❌ kichraymaydiAsos holat yetib bo'lmaydigan:
def yomon3(n):
if n == 0: # ⚠️ manfiy son bilan yetib bo'lmaydi
return 1
return n * yomon3(n - 1)
yomon3(-1) # ❌ RecursionErrorXavfsiz shakl:
def faktorial(n):
if n < 0:
raise ValueError("manfiy son")
if n <= 1:
return 1
return n * faktorial(n - 1)2.2. Chuqurlik chegarasi
import sys
sys.getrecursionlimit() # 1000 (sukut)Amaldagi chegara kamroq — freym stekida joy kerak:
def sanagich(n):
return 0 if n == 0 else 1 + sanagich(n - 1)
sanagich(996) # ✅ (taxminan)
sanagich(1000) # ❌ RecursionErrorChegarani oshirish:
sys.setrecursionlimit(10_000) # ⚠️ ehtiyot bo'lingBu xavfli — haqiqiy chegara C stek hajmi:
RecursionError → Python xatosi, ushlash mumkin
Segmentation fault → jarayon o'ladi, ushlab bo'lmaydiNega chegara bor:
Har funksiya chaqiruvi freym obyekti yaratadi:
import sys
def f(): return sys._getframe()
frame = f()
sys.getsizeof(frame) # ~100-500 baytChegarasiz cheksiz rekursiya butun xotirani yeydi yoki C stekni to'ldiradi (segfault).
Xavfsiz oshirish (threading bilan):
import sys, threading
sys.setrecursionlimit(100_000)
threading.stack_size(64 * 1024 * 1024) # 64 MB stek
def ish():
chuqur_rekursiya()
t = threading.Thread(target=ish)
t.start()
t.join()Yaxshiroq — iterativ yechim (2.5-bo'lim).
2.3. Memoizatsiya
Muammo — takroriy hisoblash:
def fib(n):
return n if n < 2 else fib(n - 1) + fib(n - 2)fib(5) uchun chaqiruvlar daraxti:
fib(5)
/ \
fib(4) fib(3)
/ \ / \
fib(3) fib(2) fib(2) fib(1)
/ \ / \ / \
fib(2) fib(1) ... ...fib(3) ikki marta, fib(2) uch marta hisoblanadi. Murakkablik — O(2ⁿ).
Yechim — natijalarni eslab qolish:
from functools import cache # Python 3.9+
@cache
def fib(n):
return n if n < 2 else fib(n - 1) + fib(n - 2)
fib(100) # bir zumdaMurakkablik: O(2ⁿ) → O(n).
lru_cache — chegara bilan:
from functools import lru_cache
@lru_cache(maxsize=128)
def qimmat(x): ...
qimmat.cache_info() # CacheInfo(hits=..., misses=...)
qimmat.cache_clear()Talablar:
- Argumentlar hashlanadigan bo'lishi kerak
- Funksiya toza bo'lishi kerak (bir xil kirish → bir xil chiqish)
@cache
def f(r: list): ... # ❌ TypeError: unhashable type
@cache
def f(t: tuple): ... # ✅Qo'lda memoizatsiya:
def fib(n, _kesh={0: 0, 1: 1}): # ⚠️ o'zgaruvchan sukut (7.3-dars)
if n not in _kesh:
_kesh[n] = fib(n - 1) + fib(n - 2)
return _kesh[n]Ishlaydi, lekin @cache aniqroq va cache_clear bor.
2.4. Rekursiya vs iteratsiya
Har rekursiv funksiyani sikl bilan yozish mumkin (Church–Turing).
# Rekursiv
def faktorial(n):
return 1 if n <= 1 else n * faktorial(n - 1)
# Iterativ
def faktorial(n):
natija = 1
for i in range(2, n + 1):
natija *= i
return natijaSolishtirish:
| Rekursiya | Iteratsiya | |
|---|---|---|
| Xotira | O(chuqurlik) — stek | O(1) |
| Tezlik | Sekinroq (freym) | Tezroq |
| Chuqurlik | ~1000 | Cheksiz |
| O'qilishi | Daraxt/graf uchun | Chiziqli uchun |
| Debug | Qiyinroq | Osonroq |
Qachon rekursiya:
- Ma'lumot rekursiv tuzilma (daraxt, graf, JSON)
- "Bo'l va hukmronlik qil" (quicksort, mergesort, binar qidiruv)
- Backtracking (labirint, N-vazir, sudoku)
- Chuqurlik kichik va cheklangan
Qachon iteratsiya:
- Chiziqli o'tish (
for x in r) - Chuqurlik katta yoki noma'lum
- Tezlik muhim
- Oddiy akkumulyatsiya
Tezlik farqi:
faktorial_rekursiv(20) # ~1.8 µs
faktorial_iterativ(20) # ~1.1 µs
math.factorial(20) # ~0.1 µs ⭐ C da2.5. Rekursiyani iteratsiyaga aylantirish
1. Chiziqli rekursiya → sikl:
# Rekursiv
def yigindi(r, i=0):
return 0 if i >= len(r) else r[i] + yigindi(r, i + 1)
# Iterativ
def yigindi(r):
natija = 0
for x in r:
natija += x
return natija2. Daraxt aylanishi → aniq stek:
# Rekursiv
def barglar(d):
for v in d.values():
if isinstance(v, dict):
yield from barglar(v)
else:
yield v
# Iterativ (stek bilan)
def barglar(d):
stek = [d]
while stek:
joriy = stek.pop()
if isinstance(joriy, dict):
stek.extend(joriy.values())
else:
yield joriy Tartib o'zgaradi — stek.pop() oxiridan oladi. Bir xil tartib uchun reversed().
3. Quyruqli rekursiya → sikl:
# Quyruqli (natija to'g'ridan-to'g'ri qaytariladi)
def yigindi(r, i=0, jami=0):
if i >= len(r):
return jami
return yigindi(r, i + 1, jami + r[i]) # ⭐ quyruqli chaqiruv
# Sikl
def yigindi(r):
jami = 0
for x in r:
jami += x
return jamiPython quyruqli rekursiyani optimallashtirmaydi (2.6-bo'lim).
2.6. Quyruqli rekursiya va Python
Quyruqli chaqiruv (tail call) — rekursiv chaqiruv funksiyaning oxirgi amali:
def quyruqli(n, akk=1):
if n <= 1:
return akk
return quyruqli(n - 1, akk * n) # ⭐ quyruqli
def quyruqsiz(n):
if n <= 1:
return 1
return n * quyruqsiz(n - 1) # ⚠️ ko'paytirish qoladiQuyruqli rekursiya nazariy jihatdan siklga aylantirilishi mumkin — freym qayta ishlatiladi, stek o'smaydi.
Python buni QILMAYDI:
quyruqli(10_000) # ❌ RecursionErrorGuido nima uchun rad etgan (2009, "Tail Recursion Elimination"):
- Traceback yo'qoladi — debug qiyinlashadi
- Python — funksional til emas — sikl tabiiy
- Yashirin optimallashtirish — "Aniq yashirinlikdan yaxshi"
- Kutubxona qo'llab-quvvatlashi — barcha amalga oshirish qo'llab-quvvatlashi kerak
"I don't think it's a good idea to try to encourage a Python programming style that relies on tail recursion... Python has excellent looping constructs."
Qaysi tillar qo'llab-quvvatlaydi:
| Til | Quyruqli optimallashtirish |
|---|---|
| Scheme, Haskell, Erlang | Kafolatlangan |
| Scala, Kotlin | (@tailrec, tailrec) |
| JavaScript (ES6) | Spetsifikatsiyada bor, amalda deyarli yo'q |
| Python, Java, C# | |
| C, C++, Rust | Kompilyator ba'zan qiladi |
Amaliy natija: Pythonda quyruqli rekursiya yozishning ma'nosi yo'q — sikl yozing.
2.7. Amaliy naqshlar
1. Daraxt aylanishi:
def aylanish(tugun, daraja=0):
yield tugun, daraja
for bola in tugun.bolalar:
yield from aylanish(bola, daraja + 1)2. "Bo'l va hukmronlik qil":
def binar_qidiruv(r, x, chap=0, ong=None):
ong = len(r) - 1 if ong is None else ong
if chap > ong:
return -1
orta = (chap + ong) // 2
if r[orta] == x:
return orta
if r[orta] < x:
return binar_qidiruv(r, x, orta + 1, ong)
return binar_qidiruv(r, x, chap, orta - 1)3. Backtracking:
def permutatsiyalar(r):
if len(r) <= 1:
yield r
return
for i in range(len(r)):
for p in permutatsiyalar(r[:i] + r[i+1:]):
yield [r[i]] + p4. O'zaro rekursiya:
def juftmi(n):
return True if n == 0 else toqmi(n - 1)
def toqmi(n):
return False if n == 0 else juftmi(n - 1)5. Yordamchi funksiya bilan:
def yigindi(r):
"""Ochiq API — oddiy imzo."""
def ichki(i, jami):
return jami if i >= len(r) else ichki(i + 1, jami + r[i])
return ichki(0, 0)Umumiy tuzoqlar:
# ❌ Kesim — O(n²)
def yigindi(r):
return 0 if not r else r[0] + yigindi(r[1:]) # har chaqiruvda nusxa
# ✅ Indeks bilan
def yigindi(r, i=0):
return 0 if i >= len(r) else r[i] + yigindi(r, i + 1)
# ❌ O'zgaruvchan sukut
def yig(r, natija=[]): ... # 7.3-dars tuzog'i
# ✅
def yig(r, natija=None):
natija = [] if natija is None else natija3. Tez ma'lumotnoma
Ikki qism
def f(n):
if ASOS_SHART: ⭐ ASOS — to'xtatadi
return ASOS_QIYMAT
return ... f(KICHRAYTIRILGAN) ⭐ QADAM
⚠️ Asos yo'q yoki kichraymasa → RecursionErrorChuqurlik
sys.getrecursionlimit() 1000 (sukut)
sys.setrecursionlimit(n) ⚠️ segfault xavfi
Xavfsiz oshirish:
threading.stack_size(64*1024*1024)
+ alohida oqimda ishga tushirish
⭐ Yaxshiroq: iterativ yechim (stek bilan)Memoizatsiya
from functools import cache, lru_cache
@cache cheksiz (3.9+)
@lru_cache(maxsize=128) chegara bilan
fib: O(2ⁿ) → O(n)
⚠️ Argumentlar HASHLANADIGAN bo'lsin
⚠️ Funksiya TOZA bo'lsinRekursiya vs iteratsiya
Rekursiya: daraxt, graf, bo'l-va-hukmronlik, backtracking
Iteratsiya: chiziqli, katta chuqurlik, tezlik muhim
Xotira: O(chuqurlik) vs O(1)
Tezlik: sekinroq vs tezroqQuyruqli rekursiya
return f(...) quyruqli chaqiruv
Python OPTIMALLASHTIRMAYDI (Guido ataylab rad etgan)
→ Pythonda quyruqli rekursiya yozishning ma'nosi yo'qTuzoqlar
f(r[1:]) ❌ O(n²) — kesim nusxa yaratadi
f(r, i+1) ✅ indeks
def f(r, n=[]) ❌ o'zgaruvchan sukut
@cache + list argument ❌ unhashable4. Batafsil misollar
Misol 1 — Asoslar va chuqurlik
"""Rekursiyaning ikki qismi va chegaralari."""
import sys
import time
print("=== 1. Asos va qadam ===")
def faktorial(n):
"""✅ To'g'ri rekursiya."""
if n < 0:
raise ValueError(f"manfiy son: {n}")
if n <= 1: # ⭐ ASOS
return 1
return n * faktorial(n - 1) # ⭐ QADAM
print(f" def faktorial(n):")
print(f" if n <= 1: return 1 ← ASOS")
print(f" return n * faktorial(n-1) ← QADAM\n")
for n in [0, 1, 5, 10, 20]:
print(f" faktorial({n:>2}) = {faktorial(n):,}")
try:
faktorial(-1)
except ValueError as e:
print(f"\n faktorial(-1) → ❌ {e}")
print("\n=== 2. ⚠️ Noto'g'ri rekursiya ===")
def asossiz(n):
"""❌ Asos holat yo'q."""
return n * asossiz(n - 1)
def kichraymaydi(n):
"""❌ Argument kichraymaydi."""
if n <= 1:
return 1
return n * kichraymaydi(n)
def yetib_bolmaydi(n):
"""❌ Asos holatga yetib bo'lmaydi."""
if n == 0:
return 1
return n * yetib_bolmaydi(n - 1)
XATOLAR = [
("Asos yo'q", asossiz, 5),
("Kichraymaydi", kichraymaydi, 5),
("Asosga yetib bo'lmaydi", yetib_bolmaydi, -1),
]
print(f" {'Xato turi':<26} {'Natija'}")
print(" " + "─" * 56)
for nom, f, arg in XATOLAR:
try:
f(arg)
natija = "✅ ishladi"
except RecursionError:
natija = "❌ RecursionError"
print(f" {nom:<26} {natija}")
print(f"\n ⭐ Uch shart:")
print(f" 1. ASOS holat bor")
print(f" 2. Har qadamda argument KICHRAYADI")
print(f" 3. Asos holatga YETIB BORADI")
print("\n=== 3. Chuqurlik chegarasi ===")
def sanagich(n):
return 0 if n == 0 else 1 + sanagich(n - 1)
print(f" sys.getrecursionlimit() = {sys.getrecursionlimit()}\n")
def maksimal_chuqurlik():
"""Amaldagi chegarani topadi."""
chap, ong = 1, sys.getrecursionlimit() * 2
while chap < ong:
orta = (chap + ong + 1) // 2
try:
sanagich(orta)
chap = orta
except RecursionError:
ong = orta - 1
return chap
maks = maksimal_chuqurlik()
print(f" Amaldagi maksimal chuqurlik: {maks}")
print(f" ⚠️ Chegaradan kam — chunki freymlar joriy stekda ham bor\n")
for n in [100, 500, maks, maks + 1, 2000]:
try:
natija = sanagich(n)
holat = f"✅ {natija}"
except RecursionError:
holat = "❌ RecursionError"
print(f" sanagich({n:>5}) → {holat}")
print("\n=== 4. Chegarani oshirish ===")
asl_chegara = sys.getrecursionlimit()
print(f" ⚠️ sys.setrecursionlimit(10_000):")
sys.setrecursionlimit(10_000)
try:
natija = sanagich(5000)
print(f" sanagich(5000) → ✅ {natija}")
except RecursionError:
print(f" sanagich(5000) → ❌ RecursionError")
sys.setrecursionlimit(asl_chegara)
print(f"""
⚠️ XAVF: haqiqiy chegara — C STEK hajmi.
RecursionError → Python xatosi, ushlash mumkin
Segmentation fault → jarayon o'ladi, ushlab bo'lmaydi
sys.setrecursionlimit(1_000_000) → deyarli aniq segfault
""")
print(f" ✅ Xavfsiz oshirish (alohida oqim + katta stek):")
print(f"""
import sys, threading
sys.setrecursionlimit(100_000)
threading.stack_size(64 * 1024 * 1024) # 64 MB
t = threading.Thread(target=chuqur_ish)
t.start(); t.join()
""")
import threading
natija_konteyner = []
def chuqur_ish():
try:
natija_konteyner.append(sanagich(50_000))
except RecursionError:
natija_konteyner.append("RecursionError")
sys.setrecursionlimit(100_000)
asl_stek = threading.stack_size()
try:
threading.stack_size(64 * 1024 * 1024)
t = threading.Thread(target=chuqur_ish)
t.start()
t.join()
print(f" Sinov: 64 MB stek bilan sanagich(50_000) → {natija_konteyner[0]}")
except (ValueError, RuntimeError) as e:
print(f" Sinov: {e}")
finally:
threading.stack_size(asl_stek)
sys.setrecursionlimit(asl_chegara)
print("\n=== 5. Freym stekini ko'rish ===")
def daraja_1():
return daraja_2()
def daraja_2():
return daraja_3()
def daraja_3():
import traceback
stek = traceback.extract_stack()
return [q.name for q in stek[-5:]]
print(f" Chaqiruv zanjiri:")
for i, nom in enumerate(daraja_1(), 1):
print(f" {i}. {nom}")
print(f"\n Freym hajmi:")
def freym_ol():
return sys._getframe()
f = freym_ol()
print(f" sys.getsizeof(frame) = {sys.getsizeof(f)} bayt")
print(f" 1000 chuqurlik ≈ {sys.getsizeof(f) * 1000 / 1024:.0f} KB")
print("\n=== 6. Rekursiya chuqurligini kuzatish ===")
def kuzatilgan_faktorial(n, daraja=0):
otstup = " " * daraja
print(f" {otstup}→ faktorial({n})")
if n <= 1:
print(f" {otstup}← 1 (ASOS)")
return 1
natija = n * kuzatilgan_faktorial(n - 1, daraja + 1)
print(f" {otstup}← {natija}")
return natija
print(f" faktorial(4):\n")
kuzatilgan_faktorial(4)
print(f"\n ⭐ Rekursiya IKKI yo'nalishda ishlaydi:")
print(f" → pastga (asosga qadar)")
print(f" ← yuqoriga (natijalarni birlashtirib)")Natijaning muhim qismi:
=== 2. ⚠️ Noto'g'ri rekursiya ===
Xato turi Natija
────────────────────────────────────────────────────────
Asos yo'q ❌ RecursionError
Kichraymaydi ❌ RecursionError
Asosga yetib bo'lmaydi ❌ RecursionError
=== 3. Chuqurlik chegarasi ===
sys.getrecursionlimit() = 1000
Amaldagi maksimal chuqurlik: 997
sanagich( 100) → ✅ 100
sanagich( 997) → ✅ 997
sanagich( 2000) → ❌ RecursionError
=== 6. Rekursiya chuqurligini kuzatish ===
faktorial(4):
→ faktorial(4)
→ faktorial(3)
→ faktorial(2)
→ faktorial(1)
← 1 (ASOS)
← 2
← 6
← 24Nima ko'rsatdi: 2.1, 2.2-bo'limlar.
Misol 2 — Memoizatsiya
"""Eksponensialdan chiziqliga."""
import time
import sys
from functools import cache, lru_cache
print("=== 1. Muammo: takroriy hisoblash ===")
chaqiruvlar = [0]
def fib_sekin(n):
chaqiruvlar[0] += 1
return n if n < 2 else fib_sekin(n - 1) + fib_sekin(n - 2)
print(f" def fib(n): return n if n<2 else fib(n-1) + fib(n-2)\n")
print(f" {'n':>4} {'Natija':>12} {'Chaqiruvlar':>14} {'Vaqt':>10}")
print(" " + "─" * 46)
for n in [10, 20, 25, 30]:
chaqiruvlar[0] = 0
boshlandi = time.perf_counter()
natija = fib_sekin(n)
vaqt = time.perf_counter() - boshlandi
print(f" {n:>4} {natija:>12,} {chaqiruvlar[0]:>14,} "
f"{vaqt * 1000:>8.1f} ms")
print(f"\n ⚠️ Chaqiruvlar soni EKSPONENSIAL o'sadi — O(2ⁿ)")
print("\n=== 2. Chaqiruvlar daraxti ===")
def fib_daraxt(n, daraja=0, kuzatuv=None):
if kuzatuv is None:
kuzatuv = []
kuzatuv.append((daraja, n))
if n < 2:
return n, kuzatuv
a, _ = fib_daraxt(n - 1, daraja + 1, kuzatuv)
b, _ = fib_daraxt(n - 2, daraja + 1, kuzatuv)
return a + b, kuzatuv
natija, kuzatuv = fib_daraxt(5)
print(f" fib(5) chaqiruvlar daraxti:\n")
for daraja, n in kuzatuv[:16]:
print(f" {' ' * daraja}fib({n})")
print(f" ...")
from collections import Counter
hisob = Counter(n for _, n in kuzatuv)
print(f"\n Har qiymat necha marta hisoblandi:")
for n in sorted(hisob, reverse=True):
print(f" fib({n}) × {hisob[n]} {'█' * hisob[n]}")
print(f"\n Jami chaqiruv: {len(kuzatuv)}")
print("\n=== 3. ⭐ @cache bilan ===")
@cache
def fib_kesh(n):
return n if n < 2 else fib_kesh(n - 1) + fib_kesh(n - 2)
print(f" @cache")
print(f" def fib(n): return n if n<2 else fib(n-1) + fib(n-2)\n")
print(f" {'n':>5} {'Natija':>26} {'Vaqt':>12}")
print(" " + "─" * 48)
for n in [30, 50, 100, 200, 500]:
boshlandi = time.perf_counter()
natija = fib_kesh(n)
vaqt = time.perf_counter() - boshlandi
korinish = f"{natija:,}" if natija < 10 ** 15 else f"{natija:.3e}"
print(f" {n:>5} {korinish:>26} {vaqt * 1_000_000:>9.1f} µs")
print(f"\n Kesh: {fib_kesh.cache_info()}")
print(f"\n Solishtirish (n=30):")
chaqiruvlar[0] = 0
boshlandi = time.perf_counter()
fib_sekin(30)
sekin_vaqt = time.perf_counter() - boshlandi
sekin_chaqiruv = chaqiruvlar[0]
fib_kesh.cache_clear()
boshlandi = time.perf_counter()
fib_kesh(30)
kesh_vaqt = time.perf_counter() - boshlandi
print(f" Keshsiz: {sekin_chaqiruv:>10,} chaqiruv, "
f"{sekin_vaqt * 1000:>8.1f} ms")
print(f" @cache: {fib_kesh.cache_info().misses:>10,} chaqiruv, "
f"{kesh_vaqt * 1000:>8.3f} ms")
print(f" Tezlik: {sekin_vaqt / kesh_vaqt:>10,.0f}x")
print(f"\n ⭐ Murakkablik: O(2ⁿ) → O(n)")
print("\n=== 4. @cache vs @lru_cache ===")
@cache
def a(n): return n
@lru_cache(maxsize=None)
def b(n): return n
@lru_cache(maxsize=3)
def c(n): return n
for i in range(6):
c(i)
print(f" {'Dekorator':<24} {'maxsize':<10} {'Xatti-harakat'}")
print(" " + "─" * 62)
print(f" {'@cache':<24} {'None':<10} cheksiz, eng tez (3.9+)")
print(f" {'@lru_cache(maxsize=None)':<24} {'None':<10} cheksiz")
print(f" {'@lru_cache(maxsize=128)':<24} {'128':<10} LRU chegara")
print(f"\n @lru_cache(maxsize=3), 6 ta chaqiruv:")
print(f" {c.cache_info()}")
print(f" ⭐ Eng kam ishlatilganlar chiqarildi")
print(f"\n Metodlar:")
print(f" cache_info() → statistika")
print(f" cache_clear() → tozalash")
print(f" __wrapped__ → asl funksiya")
fib_kesh.cache_clear()
print(f"\n cache_clear() dan keyin: {fib_kesh.cache_info()}")
print("\n=== 5. ⚠️ Kesh talablari ===")
@cache
def hashlanadigan(n: int, t: tuple = ()):
return n + len(t)
TALABLAR = [
("int", (5,), {}),
("tuple", (5, (1, 2)), {}),
("frozenset", (5, frozenset([1])), {}),
("str", ("abc",), {}),
("list", (5, [1, 2]), {}),
("dict", (5, {"a": 1}), {}),
("set", (5, {1, 2}), {}),
]
print(f" {'Argument turi':<16} {'Natija'}")
print(" " + "─" * 56)
for nom, args, kwargs in TALABLAR:
try:
hashlanadigan(*args, **kwargs)
natija = "✅ keshlandi"
except TypeError as e:
natija = f"❌ {str(e)[:36]}"
print(f" {nom:<16} {natija}")
print(f"\n ✅ Yechim — o'zgarmas turga aylantirish:")
print(f"""
@cache
def _ichki(t: tuple): ...
def ommaviy(r: list):
return _ichki(tuple(r))
""")
@cache
def _yigindi(t: tuple):
return sum(t)
def yigindi(r: list):
return _yigindi(tuple(r))
print(f" yigindi([1,2,3]) = {yigindi([1, 2, 3])}")
print(f" yigindi([1,2,3]) = {yigindi([1, 2, 3])} (keshdan)")
print(f" {_yigindi.cache_info()}")
print(f"\n ⚠️ Funksiya TOZA bo'lishi kerak:")
@cache
def notoza(n):
return n + time.time() # ⚠️ har safar boshqa
a1 = notoza(1)
time.sleep(0.01)
a2 = notoza(1)
print(f" notoza(1) ikki marta: {a1 == a2} ⚠️ kesh eski qiymatni beradi")
print("\n=== 6. Qo'lda memoizatsiya ===")
def fib_qolda(n, _kesh={0: 0, 1: 1}):
"""⚠️ O'zgaruvchan sukut — ataylab."""
if n not in _kesh:
_kesh[n] = fib_qolda(n - 1, _kesh) + fib_qolda(n - 2, _kesh)
return _kesh[n]
def fib_yopilma():
"""✅ Yopilma bilan."""
kesh = {0: 0, 1: 1}
def ichki(n):
if n not in kesh:
kesh[n] = ichki(n - 1) + ichki(n - 2)
return kesh[n]
ichki.kesh = kesh
ichki.tozala = lambda: (kesh.clear(), kesh.update({0: 0, 1: 1}))
return ichki
fib_y = fib_yopilma()
USULLAR = [
("@cache", fib_kesh),
("O'zgaruvchan sukut", fib_qolda),
("Yopilma", fib_y),
]
print(f" {'Usul':<24} {'fib(50)':>18} {'Kesh hajmi':>12}")
print(" " + "─" * 58)
for nom, f in USULLAR:
natija = f(50)
if nom == "@cache":
hajm = f.cache_info().currsize
elif nom == "Yopilma":
hajm = len(f.kesh)
else:
hajm = len(f.__defaults__[0])
print(f" {nom:<24} {natija:>18,} {hajm:>12}")
print(f"\n ⭐ @cache afzalliklari:")
print(f" • cache_info(), cache_clear()")
print(f" • maxsize chegarasi (lru_cache)")
print(f" • Niyat aniq")
print(f" • C da yozilgan — tezroq")
import timeit
SOZLASH = "from __main__ import fib_kesh, fib_qolda, fib_y"
print(f"\n Tezlik (1M chaqiruv, keshdan):")
for nom, kod in [("@cache", "fib_kesh(30)"),
("Sukut", "fib_qolda(30)"),
("Yopilma", "fib_y(30)")]:
vaqt = timeit.timeit(kod, setup=SOZLASH, number=1_000_000)
print(f" {nom:<12} {vaqt:.3f} s")Natijaning muhim qismi:
=== 1. Muammo: takroriy hisoblash ===
n Natija Chaqiruvlar Vaqt
──────────────────────────────────────────────
10 55 177 0.1 ms
20 6,765 21,891 5.2 ms
25 75,025 242,785 54.8 ms
30 832,040 2,692,537 736.4 ms
=== 3. ⭐ @cache bilan ===
n Natija Vaqt
────────────────────────────────────────────────
30 832,040 96.4 µs
100 3.542e+20 91.7 µs
500 1.394e+104 803.3 µs
Solishtirish (n=30):
Keshsiz: 2,692,537 chaqiruv, 645.4 ms
@cache: 31 chaqiruv, 0.042 ms
Tezlik: 15,404x
=== 5. ⚠️ Kesh talablari ===
Argument turi Natija
────────────────────────────────────────────────────────
int ✅ keshlandi
tuple ✅ keshlandi
list ❌ unhashable type: 'list'
dict ❌ unhashable type: 'dict'Nima ko'rsatdi: 2.3-bo'lim.
Misol 3 — Rekursiya vs iteratsiya
"""Qachon qaysi biri."""
import sys
import time
import timeit
from collections import deque
print("=== 1. Chiziqli: faktorial ===")
def faktorial_rekursiv(n):
return 1 if n <= 1 else n * faktorial_rekursiv(n - 1)
def faktorial_iterativ(n):
natija = 1
for i in range(2, n + 1):
natija *= i
return natija
import math
SOZLASH = "from __main__ import faktorial_rekursiv, faktorial_iterativ\nimport math"
print(f" faktorial(20):\n")
print(f" {'Usul':<24} {'Vaqt':>12} {'Nisbat':>9}")
print(" " + "─" * 48)
natijalar = []
for nom, kod in [
("Rekursiv", "faktorial_rekursiv(20)"),
("Iterativ", "faktorial_iterativ(20)"),
("math.factorial", "math.factorial(20)"),
]:
vaqt = timeit.timeit(kod, setup=SOZLASH, number=200_000)
natijalar.append((nom, vaqt))
eng_tez = min(v for _, v in natijalar)
for nom, vaqt in natijalar:
print(f" {nom:<24} {vaqt:>10.3f} s {vaqt / eng_tez:>8.1f}x")
print(f"\n Chuqurlik chegarasi:")
for n in [900, 1500]:
try:
faktorial_rekursiv(n)
r = "✅"
except RecursionError:
r = "❌ RecursionError"
faktorial_iterativ(n)
print(f" n={n:<6} rekursiv: {r:<22} iterativ: ✅")
print(f"\n ⭐ Chiziqli masalada — ITERATSIYA")
print("\n=== 2. Daraxt: rekursiya tabiiy ===")
DARAXT = {
"loyiha": {
"src": {
"main.py": 1200,
"utils": {"helper.py": 800, "config.py": 400},
},
"testlar": {"test_main.py": 600},
"README.md": 3100,
}
}
def hajm_rekursiv(tugun):
"""✅ Tabiiy va qisqa."""
if isinstance(tugun, int):
return tugun
return sum(hajm_rekursiv(v) for v in tugun.values())
def hajm_iterativ(tugun):
"""⚠️ Stek bilan — uzunroq."""
jami = 0
stek = [tugun]
while stek:
joriy = stek.pop()
if isinstance(joriy, int):
jami += joriy
else:
stek.extend(joriy.values())
return jami
print(f" Fayl daraxti hajmi:\n")
print(f" Rekursiv: {hajm_rekursiv(DARAXT):,} bayt")
print(f" Iterativ: {hajm_iterativ(DARAXT):,} bayt")
print(f"\n Kod uzunligi:")
import inspect
for f in [hajm_rekursiv, hajm_iterativ]:
qatorlar = len([q for q in inspect.getsource(f).splitlines()
if q.strip() and not q.strip().startswith(('"""', "#"))])
print(f" {f.__name__:<20} {qatorlar} qator")
print(f"\n ⭐ Daraxt uchun — REKURSIYA (qisqaroq va aniqroq)")
print("\n=== 3. Daraxtni chizish ===")
def chiz(tugun, prefiks="", nom="/"):
"""Rekursiv — daraxt tuzilishi tabiiy."""
if isinstance(tugun, int):
print(f" {prefiks}{nom} ({tugun:,} B)")
return
print(f" {prefiks}{nom}")
elementlar = list(tugun.items())
for i, (k, v) in enumerate(elementlar):
oxirgi = i == len(elementlar) - 1
belgi = "└── " if oxirgi else "├── "
yangi_prefiks = prefiks + (" " if oxirgi else "│ ")
if isinstance(v, dict):
print(f" {prefiks}{belgi}{k}/")
chiz_ichki(v, yangi_prefiks)
else:
print(f" {prefiks}{belgi}{k} ({v:,} B)")
def chiz_ichki(tugun, prefiks):
elementlar = list(tugun.items())
for i, (k, v) in enumerate(elementlar):
oxirgi = i == len(elementlar) - 1
belgi = "└── " if oxirgi else "├── "
if isinstance(v, dict):
print(f" {prefiks}{belgi}{k}/")
chiz_ichki(v, prefiks + (" " if oxirgi else "│ "))
else:
print(f" {prefiks}{belgi}{k} ({v:,} B)")
print(f" Fayl daraxti:\n")
chiz_ichki(DARAXT, "")
print("\n=== 4. Aylanish tartibi ===")
ICHMA_ICH = {"a": {"b": 1, "c": {"d": 2, "e": 3}}, "f": 4}
def barglar_rekursiv(d):
for k, v in d.items():
if isinstance(v, dict):
yield from barglar_rekursiv(v)
else:
yield k, v
def barglar_stek(d):
stek = [d]
while stek:
joriy = stek.pop()
if isinstance(joriy, dict):
stek.extend(joriy.items())
elif isinstance(joriy, tuple):
k, v = joriy
if isinstance(v, dict):
stek.extend(v.items())
else:
yield k, v
def barglar_navbat(d):
navbat = deque([d])
while navbat:
joriy = navbat.popleft()
if isinstance(joriy, dict):
navbat.extend(joriy.items())
elif isinstance(joriy, tuple):
k, v = joriy
if isinstance(v, dict):
navbat.extend(v.items())
else:
yield k, v
print(f" Ma'lumot: {ICHMA_ICH}\n")
print(f" {'Usul':<24} {'Natija'}")
print(" " + "─" * 56)
for nom, f in [("Rekursiv (DFS)", barglar_rekursiv),
("Stek (DFS, teskari)", barglar_stek),
("Navbat (BFS)", barglar_navbat)]:
print(f" {nom:<24} {list(f(ICHMA_ICH))}")
print(f"""
⭐ Rekursiv — chuqurlik bo'yicha (DFS), tabiiy tartib
Stek — DFS, lekin TESKARI tartib
Navbat — kenglik bo'yicha (BFS)
""")
print("\n=== 5. Chuqur tuzilma ===")
def chuqur_yarat(n):
d = {"qiymat": "eng chuqur"}
for _ in range(n):
d = {"daraja": d}
return d
def chuqurlik_rekursiv(d, n=0):
if not isinstance(d, dict):
return n
return max((chuqurlik_rekursiv(v, n + 1) for v in d.values()), default=n)
def chuqurlik_iterativ(d):
maks = 0
stek = [(d, 0)]
while stek:
joriy, daraja = stek.pop()
maks = max(maks, daraja)
if isinstance(joriy, dict):
stek.extend((v, daraja + 1) for v in joriy.values())
return maks
print(f" {'Chuqurlik':>10} {'Rekursiv':<22} {'Iterativ'}")
print(" " + "─" * 50)
for n in [100, 500, 2000, 10_000]:
d = chuqur_yarat(n)
try:
r = f"✅ {chuqurlik_rekursiv(d)}"
except RecursionError:
r = "❌ RecursionError"
i = f"✅ {chuqurlik_iterativ(d)}"
print(f" {n:>10} {r:<22} {i}")
print(f"\n ⚠️ Nega 500 da yiqildi, limit esa 1000?")
print(f" max(... for v in d.values()) — generator ifodasi. U ham")
print(f" o'z FREYMIGA ega, shuning uchun har daraja 2 freym oladi:")
print(f" 500 daraja ≈ 1000 freym → RecursionError.")
print(f"\n ⭐ Chuqurlik noma'lum bo'lsa — ITERATIV")
print("\n=== 6. Xotira ===")
def stek_chuqurligi():
"""Joriy stek chuqurligini qaytaradi."""
n = 0
f = sys._getframe()
while f:
n += 1
f = f.f_back
return n
def olchash(n):
if n == 0:
return stek_chuqurligi()
return olchash(n - 1)
print(f" Rekursiya chuqurligi va stek:\n")
print(f" {'Chuqurlik':>10} {'Stek freymlari':>16}")
print(" " + "─" * 30)
for n in [0, 10, 100, 500]:
print(f" {n:>10} {olchash(n):>16}")
freym = sys._getframe()
print(f"\n Bir freym: ~{sys.getsizeof(freym)} bayt")
print(f" 1000 chuqurlik ≈ {sys.getsizeof(freym) * 1000 / 1024:.0f} KB")
print(f"\n Iterativ (stek bilan):")
print(f" Ro'yxat elementi: ~8 bayt (ko'rsatkich)")
print(f" 1000 element ≈ 8 KB")
print(f" ⭐ ~{sys.getsizeof(freym) / 8:.0f}x kam xotira")
print("\n=== 7. Qaror jadvali ===")
print("""
┌────────────────────────────┬──────────────────────────────────┐
│ Vazifa │ Tanlov │
├────────────────────────────┼──────────────────────────────────┤
│ Chiziqli o'tish │ Iteratsiya ⭐ │
│ Akkumulyatsiya (sum, max) │ Tayyor funksiya ⭐ │
│ Daraxt / graf │ Rekursiya ⭐ │
│ JSON / ichma-ich tuzilma │ Rekursiya ⭐ │
│ Bo'l va hukmronlik qil │ Rekursiya ⭐ │
│ Backtracking │ Rekursiya ⭐ │
│ Chuqurlik > 1000 │ Iteratsiya (stek) ⭐ │
│ Chuqurlik noma'lum │ Iteratsiya ⭐ │
│ Tezlik kritik │ Iteratsiya │
│ Takroriy hisoblash │ Rekursiya + @cache ⭐ │
└────────────────────────────┴──────────────────────────────────┘
""")Natijaning muhim qismi:
=== 1. Chiziqli: faktorial ===
faktorial(20):
Usul Vaqt Nisbat
────────────────────────────────────────────────
Rekursiv 0.516 s 9.6x
Iterativ 0.363 s 6.8x
math.factorial 0.054 s 1.0x
Chuqurlik chegarasi:
n=900 rekursiv: ✅ iterativ: ✅
n=1500 rekursiv: ❌ RecursionError iterativ: ✅
=== 5. Chuqur tuzilma ===
Chuqurlik Rekursiv Iterativ
──────────────────────────────────────────────────
100 ✅ 101 ✅ 101
100 ✅ 101 ✅ 101
2000 ❌ RecursionError ✅ 2001
10000 ❌ RecursionError ✅ 10001
=== 6. Xotira ===
Bir freym: ~264 bayt
1000 chuqurlik ≈ 258 KB
1000 element ≈ 8 KB
⭐ ~33x kam xotiraNima ko'rsatdi: 2.4, 2.5-bo'limlar.
Misol 4 — Amaliy: klassik algoritmlar
"""Rekursiya tabiiy bo'lgan masalalar."""
import time
from functools import cache
print("=== 1. Bo'l va hukmronlik qil ===")
def binar_qidiruv(r, x, chap=0, ong=None):
"""O(log n) — har qadamda yarmini tashlaydi."""
ong = len(r) - 1 if ong is None else ong
if chap > ong:
return -1
orta = (chap + ong) // 2
if r[orta] == x:
return orta
if r[orta] < x:
return binar_qidiruv(r, x, orta + 1, ong)
return binar_qidiruv(r, x, chap, orta - 1)
def merge_sort(r):
"""O(n log n) — bo'lish va birlashtirish."""
if len(r) <= 1:
return r
orta = len(r) // 2
chap = merge_sort(r[:orta])
ong = merge_sort(r[orta:])
return birlashtir(chap, ong)
def birlashtir(a, b):
natija = []
i = j = 0
while i < len(a) and j < len(b):
if a[i] <= b[j]:
natija.append(a[i]); i += 1
else:
natija.append(b[j]); j += 1
natija.extend(a[i:])
natija.extend(b[j:])
return natija
def quick_sort(r):
"""O(n log n) o'rtacha."""
if len(r) <= 1:
return r
tayanch = r[len(r) // 2]
kichik = [x for x in r if x < tayanch]
teng = [x for x in r if x == tayanch]
katta = [x for x in r if x > tayanch]
return quick_sort(kichik) + teng + quick_sort(katta)
import random
random.seed(1)
SARALANGAN = list(range(0, 100, 3))
TASODIFIY = [random.randrange(100) for _ in range(15)]
print(f" Binar qidiruv (saralangan: {SARALANGAN[:8]}...):\n")
for x in [0, 33, 99, 50]:
joy = binar_qidiruv(SARALANGAN, x)
holat = f"indeks {joy}" if joy >= 0 else "topilmadi"
print(f" qidir({x:>3}) → {holat}")
print(f"\n Saralash:")
print(f" Asl: {TASODIFIY}")
print(f" merge_sort: {merge_sort(TASODIFIY)}")
print(f" quick_sort: {quick_sort(TASODIFIY)}")
print(f" sorted(): {sorted(TASODIFIY)}")
import timeit
KATTA = [random.randrange(10_000) for _ in range(2000)]
SOZLASH = f"from __main__ import merge_sort, quick_sort\nr = {KATTA!r}"
print(f"\n Tezlik ({len(KATTA):,} element):")
for nom, kod in [("merge_sort", "merge_sort(r)"),
("quick_sort", "quick_sort(r)"),
("sorted (Timsort)", "sorted(r)")]:
vaqt = timeit.timeit(kod, setup=SOZLASH, number=20)
print(f" {nom:<20} {vaqt * 1000 / 20:>8.2f} ms")
print(f"\n ⭐ sorted() — C da yozilgan Timsort, har doim tezroq")
print("\n\n=== 2. Backtracking: N-vazir ===")
def n_vazir(n):
"""N ta vazirni shohmot taxtasiga joylashtirish."""
yechimlar = []
def xavfsizmi(joylashuv, qator, ustun):
for q, u in enumerate(joylashuv):
if u == ustun or abs(q - qator) == abs(u - ustun):
return False
return True
def joylashtir(joylashuv):
qator = len(joylashuv)
if qator == n:
yechimlar.append(list(joylashuv))
return
for ustun in range(n):
if xavfsizmi(joylashuv, qator, ustun):
joylashuv.append(ustun)
joylashtir(joylashuv) # ⭐ rekursiya
joylashuv.pop() # ⭐ backtrack
joylashtir([])
return yechimlar
for n in [4, 5, 6, 8]:
boshlandi = time.perf_counter()
yechimlar = n_vazir(n)
vaqt = time.perf_counter() - boshlandi
print(f" {n}×{n} taxta: {len(yechimlar):>3} yechim "
f"({vaqt * 1000:>7.1f} ms)")
print(f"\n 4×4 birinchi yechim:")
yechim = n_vazir(4)[0]
for qator, ustun in enumerate(yechim):
taxta = "".join("♛ " if u == ustun else "· " for u in range(4))
print(f" {taxta}")
print("\n\n=== 3. Permutatsiyalar va kombinatsiyalar ===")
def permutatsiyalar(r):
"""Barcha tartiblanishlar."""
if len(r) <= 1:
yield list(r)
return
for i in range(len(r)):
for p in permutatsiyalar(r[:i] + r[i + 1:]):
yield [r[i]] + p
def kombinatsiyalar(r, k):
"""k elementli barcha to'plamlar."""
if k == 0:
yield []
return
if len(r) < k:
return
for kombinatsiya in kombinatsiyalar(r[1:], k - 1):
yield [r[0]] + kombinatsiya
yield from kombinatsiyalar(r[1:], k)
def barcha_qism_toplamlar(r):
"""2ⁿ ta qism to'plam."""
if not r:
yield []
return
for qism in barcha_qism_toplamlar(r[1:]):
yield qism
yield [r[0]] + qism
R = ["a", "b", "c"]
print(f" Elementlar: {R}\n")
print(f" Permutatsiyalar ({len(list(permutatsiyalar(R)))}):")
print(f" {[''.join(p) for p in permutatsiyalar(R)]}")
print(f"\n 2 elementli kombinatsiyalar:")
print(f" {[''.join(k) for k in kombinatsiyalar(R, 2)]}")
print(f"\n Barcha qism to'plamlar ({2 ** len(R)}):")
print(f" {[''.join(q) or '∅' for q in barcha_qism_toplamlar(R)]}")
print(f"\n ⭐ itertools bilan solishtiring:")
from itertools import permutations, combinations, chain
print(f" permutations(r) → {[''.join(p) for p in permutations(R)]}")
print(f" combinations(r, 2) → {[''.join(k) for k in combinations(R, 2)]}")
qism_toplamlar = chain.from_iterable(
combinations(R, k) for k in range(len(R) + 1)
)
print(f" chain(combinations...) → "
f"{[''.join(q) or '∅' for q in qism_toplamlar]}")
print(f" ⭐ itertools — C da yozilgan, tezroq")
print("\n\n=== 4. Dinamik dasturlash ===")
@cache
def tanga_almashtirish(summa, tangalar):
"""Minimal tangalar soni."""
if summa == 0:
return 0
if summa < 0:
return float("inf")
return min(
(1 + tanga_almashtirish(summa - t, tangalar) for t in tangalar),
default=float("inf"),
)
@cache
def zinapoya(n):
"""n zinaga chiqish usullari (1 yoki 2 qadam)."""
if n <= 2:
return max(n, 1)
return zinapoya(n - 1) + zinapoya(n - 2)
@cache
def levenshtein(a: str, b: str) -> int:
"""Ikki satr orasidagi tahrirlash masofasi."""
if not a:
return len(b)
if not b:
return len(a)
if a[0] == b[0]:
return levenshtein(a[1:], b[1:])
return 1 + min(
levenshtein(a[1:], b), # o'chirish
levenshtein(a, b[1:]), # qo'shish
levenshtein(a[1:], b[1:]), # almashtirish
)
TANGALAR = (1, 5, 10, 25)
print(f" Tanga almashtirish (tangalar: {TANGALAR}):")
for summa in [30, 63, 99]:
n = tanga_almashtirish(summa, TANGALAR)
print(f" {summa:>3} → {n} ta tanga")
print(f"\n Zinapoya (1 yoki 2 qadam):")
for n in [3, 5, 10, 50]:
print(f" {n:>3} zina → {zinapoya(n):,} usul")
print(f"\n Levenshtein masofasi:")
JUFTLIKLAR = [
("kitten", "sitting"),
("salom", "kalom"),
("python", "python"),
("abc", "xyz"),
]
for a, b in JUFTLIKLAR:
print(f" {a!r:<10} ↔ {b!r:<10} = {levenshtein(a, b)}")
print(f"\n Kesh statistikasi:")
for f in [tanga_almashtirish, zinapoya, levenshtein]:
print(f" {f.__name__:<20} {f.cache_info()}")
print(f"""
⭐ Dinamik dasturlash = rekursiya + memoizatsiya
• Masala kichik qism masalalarga bo'linadi
• Qism masalalar TAKRORLANADI
• @cache eksponensialni polinomialga aylantiradi
""")
print("\n=== 5. Xanoy minoralari ===")
def xanoy(n, manba="A", maqsad="C", yordamchi="B", qadamlar=None):
"""Klassik rekursiv masala."""
if qadamlar is None:
qadamlar = []
if n == 1:
qadamlar.append((manba, maqsad))
return qadamlar
xanoy(n - 1, manba, yordamchi, maqsad, qadamlar)
qadamlar.append((manba, maqsad))
xanoy(n - 1, yordamchi, maqsad, manba, qadamlar)
return qadamlar
print(f" {'Disklar':>8} {'Qadamlar':>10} {'Formula (2ⁿ-1)':>16}")
print(" " + "─" * 38)
for n in range(1, 8):
qadamlar = xanoy(n)
print(f" {n:>8} {len(qadamlar):>10} {2 ** n - 1:>16}")
print(f"\n 3 disk uchun qadamlar:")
for i, (a, b) in enumerate(xanoy(3), 1):
print(f" {i}. {a} → {b}")
print(f"""
⭐ Xanoy — rekursiyaning klassik namunasi:
n diskni ko'chirish = (n-1) ni ko'chirish
+ eng kattasini ko'chirish
+ (n-1) ni qaytarish
""")
print("\n=== 6. Rekursiya tuzoqlari ===")
print(f" ❌ Kesim bilan — O(n²):\n")
def yigindi_kesim(r):
return 0 if not r else r[0] + yigindi_kesim(r[1:])
def yigindi_indeks(r, i=0):
return 0 if i >= len(r) else r[i] + yigindi_indeks(r, i + 1)
R = list(range(500))
SOZLASH = f"from __main__ import yigindi_kesim, yigindi_indeks\nr = {R!r}"
for nom, kod in [("r[1:] bilan (O(n²))", "yigindi_kesim(r)"),
("indeks bilan (O(n))", "yigindi_indeks(r)"),
("sum(r)", "sum(r)")]:
vaqt = timeit.timeit(kod, setup=SOZLASH, number=1000)
print(f" {nom:<24} {vaqt * 1000 / 1000:>8.3f} ms")
print(f"""
⚠️ r[1:] har chaqiruvda YANGI ro'yxat yaratadi:
n + (n-1) + (n-2) + ... = O(n²) xotira va vaqt
✅ Indeks bilan — nusxa yo'q
⭐ Eng yaxshisi — sum(r)
""")
print(f" ❌ O'zgaruvchan sukut:\n")
def yig_yomon(r, natija=[]):
"""⚠️ 7.3-dars tuzog'i."""
for x in r:
natija.append(x)
return natija
def yig_yaxshi(r, natija=None):
natija = [] if natija is None else natija
for x in r:
natija.append(x)
return natija
print(f" yig_yomon([1,2]) → {yig_yomon([1, 2])}")
print(f" yig_yomon([3,4]) → {yig_yomon([3, 4])} ⚠️ oldingi qoldi")
print(f" yig_yaxshi([1,2]) → {yig_yaxshi([1, 2])}")
print(f" yig_yaxshi([3,4]) → {yig_yaxshi([3, 4])} ✅")Natijaning muhim qismi:
=== 2. Backtracking: N-vazir ===
4×4 taxta: 2 yechim ( 0.1 ms)
5×5 taxta: 10 yechim ( 0.2 ms)
6×6 taxta: 4 yechim ( 0.7 ms)
8×8 taxta: 92 yechim ( 16.2 ms)
4×4 birinchi yechim:
· ♛ · ·
· · · ♛
♛ · · ·
· · ♛ ·
=== 4. Dinamik dasturlash ===
Levenshtein masofasi:
'kitten' ↔ 'sitting' = 3
'salom' ↔ 'kalom' = 1
'python' ↔ 'python' = 0
'abc' ↔ 'xyz' = 3
=== 6. Rekursiya tuzoqlari ===
r[1:] bilan (O(n²)) 1.357 ms
indeks bilan (O(n)) 0.157 ms
sum(r) 0.004 msNima ko'rsatdi: 2.3, 2.7-bo'limlar.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
| "Rekursiya har doim chiroyliroq" | Chiziqli masalada sikl aniqroq |
| "Chuqurlik chegarasi 1000" | Amalda ~990 — joriy stek ham hisoblanadi |
"setrecursionlimit xavfsiz" |
Katta qiymat → segfault |
| "Python quyruqli rekursiyani optimallashtiradi" | Ataylab qilmaydi |
"@cache har doim ishlatilishi mumkin" |
Argumentlar hashlanadigan, funksiya toza bo'lsin |
| "Rekursiya sekin — ishlatmang" | Daraxt/graf uchun tabiiy va to'g'ri |
"r[1:] bilan rekursiya normal" |
O(n²) — indeks ishlating |
| "Rekursiv fib normal" | O(2ⁿ) — @cache shart |
| "Iterativ har doim tezroq" | Bir xil algoritm uchun — ha, lekin kod uzunroq |
6. Keng tarqalgan xatolar va yechimlari
1. Asos holat yo'q
def f(n): return n * f(n-1) # ❌ RecursionError
def f(n):
if n <= 1: return 1 # ✅
return n * f(n-1)2. Argument kichraymaydi
return f(n) # ❌
return f(n - 1) # ✅3. Asosga yetib bo'lmaydi
if n == 0: return 1 # ⚠️ manfiy son bilan
if n <= 0: return 1 # ✅4. Kesim bilan
f(r[1:]) # ❌ O(n²)
f(r, i + 1) # ✅5. Memoizatsiya yo'q
def fib(n): return fib(n-1)+fib(n-2) # ❌ O(2ⁿ)
@cache # ✅ O(n)
def fib(n): ...6. @cache + o'zgaruvchan argument
@cache
def f(r: list): ... # ❌ unhashable
@cache
def _f(t: tuple): ... # ✅
def f(r): return _f(tuple(r))7. O'zgaruvchan sukut
def f(r, natija=[]): ... # ❌ 7.3-dars tuzog'i
def f(r, natija=None): ... # ✅8. Chuqur tuzilma uchun rekursiya
def yur(d): ... # ⚠️ 1000 daraja chegara
def yur_iterativ(d): # ✅ stek bilan
stek = [d]7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 7.1-dars (o'tilgan): funksiya asoslari
- 7.3-dars (o'tilgan): o'zgaruvchan sukut tuzog'i
- 6.14-dars (o'tilgan): ichma-ich tuzilmalar, rekursiv aylanish
- 6.17-dars (o'tilgan): generatorlar,
yield from - 10-qism:
functools.cache, dinamik dasturlash - 15-qism:
sys.setrecursionlimit,threading.stack_size - Algoritmlar: saralash, qidiruv, graf, DP
8. Eng yaxshi amaliyotlar
Asos holat birinchi qatorlarda. Va u yetib boradigan bo'lsin.
Takroriy hisoblash bo'lsa —
@cache. O(2ⁿ) → O(n).Kesim o'rniga indeks.
f(r, i+1),f(r[1:])emas.Chuqurlik noma'lum bo'lsa — iterativ. Stek yoki navbat bilan.
Chiziqli masalada sikl. Rekursiya daraxt/graf uchun.
setrecursionlimitdan qoching. Segfault xavfi.Quyruqli rekursiya yozmang. Python uni optimallashtirmaydi.
itertoolsni tekshiring.permutations,combinations— tayyor va tezroq.
9. Amaliy topshiriq
Vazifa 1: Natijani bashorat qiling
1. def f(n): return 1 if n <= 1 else n * f(n-1)
print(f(5))
2. def g(n): return n * g(n-1)
g(5)
3. import sys; print(sys.getrecursionlimit())
4. def h(n): return 0 if n == 0 else 1 + h(n-1)
h(2000)
5. from functools import cache
@cache
def fib(n): return n if n < 2 else fib(n-1) + fib(n-2)
print(fib(50))
6. @cache
def k(r: list): return sum(r)
k([1, 2])
7. def m(r): return 0 if not r else r[0] + m(r[1:])
print(m([1,2,3]))
8. def p(n, akk=1): return akk if n <= 1 else p(n-1, akk*n)
print(p(5))
9. def q(n): return q(n)
q(1)
10. def s(r, i=0): return 0 if i >= len(r) else r[i] + s(r, i+1)
print(s([1,2,3]))
11. def juft(n): return True if n == 0 else toq(n-1)
def toq(n): return False if n == 0 else juft(n-1)
print(juft(10))
12. fib.cache_clear(); print(fib.cache_info().currsize)Javoblar
120-
RecursionError— asos yo'q 1000-
RecursionError 12586269025-
TypeError: unhashable type: 'list' 6— ishlaydi, lekin O(n²)120— quyruqli, lekin optimallashtirilmaydi-
RecursionError— kichraymaydi 6True0
Vazifa 2: Xatolarni tuzating
1. def f(n): return n * f(n-1)
2. def f(n):
if n == 0: return 1
return n * f(n-1) # f(-1) bilan
3. def fib(n): return n if n<2 else fib(n-1)+fib(n-2)
4. def yig(r): return 0 if not r else r[0] + yig(r[1:])
5. @cache
def f(r: list): return sum(r)
6. def f(r, natija=[]): natija.append(r); return natija
7. sys.setrecursionlimit(1_000_000)
8. def chuqurlik(d): # 10000 darajali tuzilma
return 1 + max(chuqurlik(v) for v in d.values())Javoblar
1. if n <= 1: return 1 qo'shing
2. if n <= 0: return 1
3. @cache qo'shing
4. def yig(r, i=0): return 0 if i >= len(r) else r[i] + yig(r, i+1)
5. @cache def _f(t: tuple); def f(r): return _f(tuple(r))
6. def f(r, natija=None): natija = [] if natija is None else natija
7. threading.stack_size() bilan alohida oqim, yoki iterativ
8. Iterativ (stek bilan) yozingVazifa 3: Rekursiv vositalar
Yozing (har biri uchun iterativ versiya ham):
chuqurlik(d)— ichma-ich tuzilma chuqurligitekisla(r)— ichma-ich ro'yxatni tekislashbarglar(d)— barcha barg qiymatlaryol_topish(d, kalit)— kalitgacha yo'lhisobla(d)— barcha son qiymatlar yig'indisi- Aylanma havoladan himoya (
id()to'plami) - Chuqurlik chegarasi (
max_daraja)
Vazifa 4: Memoizatsiya tadqiqoti
fibni 4 xil yozing: sodda,@cache, qo'lda, iterativ- Har biri uchun: vaqt, chaqiruvlar soni, xotira
nga bog'liqlik grafigi@lru_cache(maxsize)— turli hajmlarda- Kesh samaradorligi (hit rate)
- Qachon memoizatsiya foydali emas
Vazifa 5: Klassik algoritmlar
Rekursiv yozing va itertools/standart kutubxona bilan solishtiring:
permutatsiyalar(r),kombinatsiyalar(r, k)qism_toplamlar(r)merge_sort,quick_sortbinar_qidiruvxanoy(n)- Har biri uchun tezlik va murakkablik
Vazifa 6: Dinamik dasturlash
@cache bilan yeching:
- Tanga almashtirish (minimal soni va usullar soni)
- Ryukzak masalasi (knapsack)
- Eng uzun umumiy ketma-ketlik (LCS)
- Levenshtein masofasi (+ tahrirlash qadamlari)
- Zinapoya (n qadam bilan)
- Har biri uchun keshsiz versiya bilan solishtiring
Vazifa 7: O'ylash
Nega Python quyruqli rekursiyani optimallashtirmaydi, garchi bu texnik jihatdan mumkin bo'lsa ham?
Javob
Guido to'rt sabab keltirgan (2009, "Tail Recursion Elimination").
1. Traceback yo'qoladi.
Quyruqli optimallashtirish freym'ni qayta ishlatadi. Ya'ni:
def a(): return b()
def b(): return c()
def c(): raise ValueError("xato")
a()Optimallashtirish bilan traceback:
File "x.py", line 3, in c
raise ValueError("xato")Optimallashtirishsiz:
File "x.py", line 5, in <module>
a()
File "x.py", line 1, in a
return b()
File "x.py", line 2, in b
return c()
File "x.py", line 3, in c
raise ValueError("xato")Ikkinchisi ancha foydali. Guido:
"Also, tail recursion elimination makes stack traces less useful."
Python — debug qulayligiga katta e'tibor beradigan til.
2. Python funksional til emas.
Quyruqli rekursiya — Scheme, Haskell, Erlang uchun asosiy vosita, chunki u yerda sikl yo'q:
(define (loop n)
(if (= n 0) 'done (loop (- n 1))))Pythonda esa sikl bor va u tabiiyroq:
for i in range(n): ...
while shart: ...Guido:
"I don't think it's a good idea to try to encourage a Python programming style that relies on tail recursion... Python has excellent looping constructs."
3. Yashirin optimallashtirish.
Zen: "Explicit is better than implicit."
Quyruqli optimallashtirish — jimgina xatti-harakat o'zgarishi:
def f(n):
return f(n - 1) # optimallashtirish BOR → cheksiz sikl
# optimallashtirish YO'Q → RecursionErrorRecursionError — foydali signal: "algoritmingizda xato bor". Uni yo'qotish xatolarni yashiradi.
4. Barcha amalga oshirish qo'llab-quvvatlashi kerak.
Agar CPython buni qilsa, PyPy, Jython, IronPython, MicroPython ham qilishi kerak. Aks holda kod ba'zi amalga oshirishlarda ishlaydi, ba'zilarida yo'q.
Bu — til spetsifikatsiyasiga jiddiy qo'shimcha.
Texnik nozikliklar:
Pythonda quyruqli chaqiruvni aniqlash oson emas:
def f(n):
return f(n - 1) # quyruqli?Ha, lekin:
def f(n):
try:
return f(n - 1) # ❌ quyruqli EMAS — finally bo'lishi mumkin
finally:
tozalash()def f(n):
with ochiq_fayl():
return f(n - 1) # ❌ __exit__ chaqirilishi kerakclass A:
def f(self, n):
return self.f(n-1) # ⚠️ self.f dinamik — boshqa funksiya bo'lishi mumkinOxirgi holat ayniqsa muhim: Pythonda self.f ish vaqtida aniqlanadi. Kompilyator "bu o'sha funksiya" deb ayta olmaydi.
Muqobil yechimlar:
1. Dekorator (hiyla):
def tail_call(f):
"""⚠️ Ishlaydi, lekin sekin va chalkash."""
def ichki(*args, **kwargs):
while True:
natija = f(*args, **kwargs)
if not isinstance(natija, TailCall):
return natija
args, kwargs = natija.args, natija.kwargs
return ichkiBunday kutubxonalar bor (tco, tail-recursive), lekin ular:
- Sekinroq (qo'shimcha o'rash)
- Kod chalkashroq
- Traceback baribir yo'qoladi
2. Trampolin naqshi:
def trampolin(f, *args):
while callable(f):
f = f(*args) if args else f()
args = ()
return f3. Oddiy sikl:
def yigindi(r):
jami = 0
for x in r:
jami += x
return jamiBu — eng oddiy, tez va o'qiladigan.
Boshqa tillar:
| Til | Holat |
|---|---|
| Scheme | Standart TALAB qiladi |
| Haskell, Erlang | Kafolatlangan |
| Scala | @tailrec (kompilyator tekshiradi) |
| Kotlin | tailrec kalit so'zi |
| JavaScript | ES6 da bor, faqat Safari amalga oshirgan |
| Java, C# | |
| Python |
Scala/Kotlin yondashuvi qiziq: ular aniq e'lon talab qiladi:
tailrec fun f(n: Int, akk: Int = 1): Int =
if (n <= 1) akk else f(n - 1, akk * n)Kompilyator tekshiradi: agar funksiya quyruqli bo'lmasa — xato beradi. Bu — "aniq yashirinlikdan yaxshi" tamoyiliga mos.
Pythonda shunga o'xshash narsa taklif qilingan, lekin qabul qilinmagan.
Xulosa: bu — falsafiy qaror. Python debug qulayligi va oddiylikni tanladi. Amaliy natija: Pythonda quyruqli rekursiya yozmang — sikl yozing. Bu tezroq, o'qilishi yaxshiroq va chuqurlik chegarasi yo'q.
Nimani mustahkamlaydi: 2.2, 2.4, 2.6-bo'limlar.
Xulosa
Bu darsda rekursiyani o'rgandik.
Eng muhim uch fikr:
Ikki qism majburiy: asos va kichrayuvchi qadam. Asos holat bo'lmasa yoki unga yetib bo'lmasa —
RecursionError. Chuqurlik chegarasi ~1000 (amalda ~990) va uni oshirish segfault xavfini keltiradi.Takroriy hisoblash bo'lsa —
@cache.fib(30)keshsiz 2.7 million chaqiruv va yarim soniya,@cachebilan 31 chaqiruv va mikrosoniyalar. O(2ⁿ) → O(n).Rekursiya — daraxt va graf uchun, sikl — chiziqli uchun. JSON aylanishi, "bo'l va hukmronlik qil", backtracking — rekursiya tabiiy. Chiziqli akkumulyatsiya, katta yoki noma'lum chuqurlik — iteratsiya. Va
r[1:]o'rniga indeks ishlating: kesim rekursiyani O(n²) qiladi.
Keyingi darsda — 7-qismning yakuni: docstring va tur ko'rsatkichlari. Funksiyani hujjatlash, mypy bilan tekshirish va zamonaviy Python uslubi.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!