সরাসরি পর্বে যান
পর্ব ৬৫ · ৮৩-এর মধ্যে · মডিউল ১১

Tree — ধর্ম, rooted tree আর m-ary tree-এর হিসাব

Trees — Characterisation Theorem, Rooted and m-ary Trees, Height Bounds, the Sorting Lower Bound, Cayley's Formula and Prüfer Codes

Taught in Bangla, this lesson proves that six definitions of a tree are equivalent, counts leaves from degrees, and works with rooted trees, levels and height. It derives n = mi + 1 and the leaf formula for full m-ary trees, bounds height, and proves the ⌈log2 n!⌉ comparison lower bound for sorting with decision trees. Cayley's n^(n−2) count comes from Prüfer codes, followed by binary-tree counts, tree centres, BSTs and heaps. In-page Python checks all six conditions and round-trips Prüfer codes.

এই পর্বে যা শিখবেন

  • Tree আর forest-এর সংজ্ঞা, আর tree-এর ছয়টি সমতুল্য রূপের সম্পূর্ণ প্রমাণ
  • Rooted tree-এর ভাষা: root, parent, সন্তান, পূর্বপুরুষ, level, height
  • Full m-ary tree-এ vertex, internal vertex আর leaf-এর সম্পর্ক, আর height-এর সীমা
  • Decision tree দিয়ে তুলনা-ভিত্তিক sorting-এর log2 n! নিম্নসীমার প্রমাণ
  • Prüfer code-এ tree লেখা ও পড়া, আর তা দিয়ে Cayley-র nn−2 সূত্রের প্রমাণ; tree-এর কেন্দ্র

১ · Tree আর forest — cycle-হীন connected graph

একটি পরিবারের বংশতালিকা ভাবুন: দাদা থেকে বাবা-চাচা, তাঁদের থেকে সন্তান। কাউকে খুঁজতে হলে ওপর থেকে নিচে একটিই রাস্তা। বাজারঘরের পণ্য-তালিকাও এমন: “সব পণ্য” থেকে “মুদি”, সেখান থেকে “মসলা”, তারপর “হলুদ”। আবার বাজারঘর যদি সাতটি হাবকে সবচেয়ে কম রাস্তায় জুড়তে চায়, যাতে প্রতিটি হাব থেকে প্রতিটিতে যাওয়া যায় কিন্তু কোনো রাস্তা অকারণে বাড়তি না থাকে, তবে যে নকশা পাবে, সেটিও একই গঠনের। এই গঠনের নাম tree.

এই পর্বে graph মানে সসীম undirected simple graph G = (V, E); n = |V| হলো vertex-সংখ্যা আর m = |E| edge-সংখ্যা। Path, cycle, connected graph আর component-এর সংজ্ঞা পর্ব ৫৯ “Connectivity — path, component, cut vertex আর Menger-এর ধারণা”-এ আছে; সেখানে প্রমাণিত দুটি তথ্য আমরা বারবার ব্যবহার করব।

সংজ্ঞা ১.১ (tree আর forest)

যে graph-এ কোনো cycle নেই, তা acyclic বা cycle-হীন। Connected আর acyclic graph-কে বলে tree. Acyclic graph-কে (connected হোক বা না হোক) বলে forest; তার প্রতিটি component একটি tree. Tree-এর degree 1 এমন vertex-কে বলে leaf (পাতা)। একটি মাত্র vertex-ও একটি tree (n = 1, m = 0).

Tree, forest আর tree নয় এমন graph

বাঁয়ে একটি tree (৫টি vertex, ৪টি edge), মাঝে দুটি tree মিলে একটি forest, ডানে লাল cycle থাকায় tree নয়।

A tree, a forest of two trees, and a graph with a red cycle that is not a tree treeforesttree নয়

সবুজ vertex-গুলো leaf. লক্ষ করুন, বাঁয়ের tree-তে তিনটি leaf, আর মাঝের forest-এর দুটি tree-এর প্রতিটিতে অন্তত দুটি leaf. এটি কাকতাল নয়।

উপপাদ্য ১.২ (অন্তত দুটি leaf)

n ≥ 2 vertex-এর প্রতিটি tree-তে অন্তত দুটি leaf আছে।

প্রমাণ (সবচেয়ে লম্বা path দিয়ে):

  1. Tree T connected আর n ≥ 2, তাই অন্তত একটি edge আছে। T-এর সব path-এর মধ্যে সবচেয়ে বেশি edge-ওয়ালা একটি নিন: P = v0 v1 … vk, k ≥ 1. — সসীম graph-এ path সসীম সংখ্যক
  2. ধরুন v0-এর v1 ছাড়া আরেকটি প্রতিবেশী w আছে। w যদি P-এর কোনো vj (j ≥ 2) হয়, তবে v0 v1 … vj v0 একটি cycle, যা tree-তে অসম্ভব। — acyclic
  3. w যদি P-এ না থাকে, তবে w v0 v1 … vk একটি path, যা P-এর চেয়ে লম্বা; এটিও অসম্ভব। — P সবচেয়ে লম্বা
  4. তাই deg(v0) = 1; একই যুক্তিতে deg(vk) = 1, আর k ≥ 1 বলে v0 ≠ vk. দুটি আলাদা leaf পাওয়া গেল। ∎

একটি নিখুঁত হিসাবও আছে, যা পরীক্ষায় খুব কাজের। পরের অংশে প্রমাণ হবে যে n vertex-এর tree-তে m = n − 1. পর্ব ৫৭-এর handshake lemma বলে degree-র যোগফল 2m = 2n − 2. দুই দিক থেকে 2n বাদ দিলে ∑(deg(v) − 2) = −2. Leaf-এর জন্য deg − 2 = −1, degree 2-এর জন্য 0. তাই leaf-এর সংখ্যা l হলে:

l = 2 + ∑deg(v) ≥ 3 (deg(v) − 2)

অর্থাৎ degree 2-এর vertex leaf-সংখ্যায় কিছু যোগ করে না, আর degree 3-এর প্রতিটি vertex একটি, degree 4-এর প্রতিটি দুটি বাড়তি leaf আনে। Path-এ ঠিক দুটি leaf; তারকা K1,n−1-এ কেন্দ্রের degree n − 1, তাই l = 2 + (n − 3) = n − 1.

২ · ছয়টি সমতুল্য সংজ্ঞা — characterization উপপাদ্যের পূর্ণ প্রমাণ

“Tree” চেনার অনেক উপায় আছে, আর কাজের সময় যেটি সুবিধার, সেটিই ব্যবহার করা হয়। কোনো algorithm যদি n − 1টি edge বেছে নেয় আর cycle এড়ায়, তবে ফলটি tree কি না, তা নিশ্চিত করতে এই উপপাদ্যই লাগে (পর্ব ৬৭ “Spanning tree — DFS/BFS, minimum spanning tree আর Kirchhoff-এর গণনা”-এ Kruskal-এর algorithm ঠিক এভাবে চলে)। প্রথমে দুটি সহায়ক তথ্য।

লেমা ২.১ (পর্ব ৫৯ থেকে)

(ক) একটি edge e একটি bridge (সরালে component বাড়ে) যদি এবং কেবল যদি e কোনো cycle-এ নেই। (খ) একটি edge সরালে component-সংখ্যা হয় অপরিবর্তিত থাকে, নয়তো ঠিক ১ বাড়ে, কারণ শুধু e-এর component-টি বড়জোর দুই টুকরো হতে পারে: e-এর দুই প্রান্তের একটির সঙ্গে বাকি প্রতিটি vertex যুক্ত থাকে।

লেমা ২.২ (edge, vertex আর component)

n vertex, m edge আর c component-এর যেকোনো graph-এ c ≥ n − m, আর সমতা c = n − m হয় যদি এবং কেবল যদি graph-টি acyclic.

প্রমাণ (m-এর ওপর induction):

  1. ভিত্তি m = 0: প্রতিটি vertex নিজেই একটি component, c = n = n − 0, আর edge ছাড়া cycle নেই। দুই দাবিই সত্য। — base case
  2. m ≥ 1 হলে যেকোনো edge e নিন, G′ = G − e. G′-এ m − 1টি edge, তাই hypothesis অনুযায়ী c(G′) ≥ n − m + 1, আর সমতা ঠিক তখন যখন G′ acyclic. — inductive hypothesis
  3. লেমা ২.১ অনুযায়ী c(G) = c(G′) − 1 যদি e bridge, নইলে c(G) = c(G′). দুই ক্ষেত্রেই c(G) ≥ c(G′) − 1 ≥ n − m. — প্রথম দাবি
  4. সমতা c(G) = n − m হয় ঠিক তখন, যখন দুটি অসমতাই সমতা: G′ acyclic আর e bridge, অর্থাৎ e কোনো cycle-এ নেই। — লেমা ২.১(ক)
  5. G-এর প্রতিটি cycle হয় e ধারণ করে, নয়তো G′-এর cycle. তাই “G′ acyclic আর e কোনো cycle-এ নেই” ঠিক “G acyclic”-এর সমান। ∎
উপপাদ্য ২.৩ (tree-এর characterization)

n vertex-এর একটি graph T-এর জন্য নিচের ছয়টি কথা সমতুল্য:

(a) T একটি tree (connected আর acyclic); (b) T-এর প্রতি জোড়া vertex-এর মধ্যে ঠিক একটি path আছে; (c) T connected, কিন্তু যেকোনো edge সরালে disconnected হয় (minimally connected); (d) T acyclic, কিন্তু অ-সংলগ্ন দুটি vertex-এর মধ্যে যেকোনো নতুন edge যোগ করলে cycle হয় (maximally acyclic); (e) T connected আর তাতে n − 1টি edge; (f) T acyclic আর তাতে n − 1টি edge.

প্রমাণের পরিকল্পনা: (a) ⇒ (b) ⇒ (c) ⇒ (d) ⇒ (a) একটি চক্র, তারপর লেমা ২.২ দিয়ে (a) ⇔ (e) আর (a) ⇔ (f). চক্রটি পুরো হলে যেকোনো একটি থেকে বাকি সব আসে।

  1. (a) ⇒ (b). Connected বলে প্রতি জোড়া u, v-এর মধ্যে অন্তত একটি path আছে। ধরুন দুটি আলাদা u–v path P আর Q. u থেকে দুটি পাশাপাশি চলে; x হোক প্রথম vertex, যার পরে তারা আলাদা হয়। P-তে x-এর পরের যে প্রথম vertex y আবার Q-এর x-এর পরের অংশে পড়ে, তা নিন (v নিজে পড়ে, তাই y আছে)। তাহলে P-এর x থেকে y অংশ আর Q-এর x থেকে y অংশ শুধু x আর y-তে মেলে, আর দুটির প্রথম edge আলাদা বলে একসঙ্গে অন্তত ৩টি vertex-এর একটি cycle তৈরি করে। Acyclic-এর বিরোধ। — দুই path থেকে cycle
  2. (b) ⇒ (c). Path আছে বলে T connected. যেকোনো edge uv সরান। T − uv-তে যদি u থেকে v-তে কোনো path থাকত, তবে সেটি আর একক edge uv হতো T-তে দুটি আলাদা u–v path, যা (b)-এর বিরোধী। তাই T − uv disconnected. — অনন্য path
  3. (c) ⇒ (d). T-তে cycle থাকলে তার একটি edge লেমা ২.১ অনুযায়ী bridge নয়; সেটি সরালেও T connected থাকত, যা (c)-এর বিরোধী। তাই T acyclic. এখন অ-সংলগ্ন u, v-এর মধ্যে নতুন edge যোগ করুন: T connected, তাই একটি u–v path P আছে, আর P-এর সঙ্গে নতুন edge uv একটি cycle. — bridge-এর লেমা
  4. (d) ⇒ (a). শুধু connected দেখানো বাকি। ধরুন u আর v আলাদা component-এ। তাহলে তারা অ-সংলগ্ন; (d) অনুযায়ী uv যোগ করলে একটি cycle হয়, আর সেই cycle uv ধারণ করে (আগে cycle ছিল না)। Cycle থেকে uv বাদ দিলে T-তে একটি u–v path থাকে, যা “আলাদা component”-এর বিরোধী। — contradiction
  5. (a) ⇔ (e), (a) ⇔ (f). (a) হলে c = 1 আর acyclic, তাই লেমা ২.২-এর সমতায় 1 = n − m, অর্থাৎ m = n − 1; এ থেকেই (e) আর (f). (e) হলে c = 1 = n − m, সমতা আছে, তাই লেমা ২.২ অনুযায়ী acyclic, অর্থাৎ tree. (f) হলে acyclic, তাই c = n − m = n − (n − 1) = 1, অর্থাৎ connected, তাই tree. ∎

লক্ষ করুন (e) আর (f)-এ দুটি শর্তই লাগে। শুধু “n − 1টি edge” যথেষ্ট নয়: একটি ত্রিভুজ আর একটি একা vertex মিলে ৪টি vertex আর ৩টি edge, অথচ graph-টি connected-ও নয়, acyclic-ও নয়। Forest-এর জন্য লেমা ২.২ সরাসরি বলে: c টি tree-এর forest-এ ঠিক n − c টি edge.

নিচের ব্লক ছোট এলোমেলো graph-এ ছয়টি শর্তই আলাদা আলাদা পদ্ধতিতে পরীক্ষা করে: (b)-এর জন্য প্রতি জোড়ার সব simple path গোনে, (c)-র জন্য প্রতিটি edge সরিয়ে দেখে, (d)-র জন্য প্রতিটি নতুন edge যোগ করে দেখে। উপপাদ্য সত্য হলে প্রতিটি graph-এ ছয়টি উত্তর হয় সব True, নয়তো সব False. প্রথমবার চালালে Python (Pyodide) নামতে কয়েক সেকেন্ড লাগে।

import random
from itertools import combinations
def nbrs(E, u): return [b if a == u else a for a, b in E if u in (a, b)]
def connected(n, E):
    seen, stack = {0}, [0]
    while stack:
        for w in nbrs(E, stack.pop()):
            if w not in seen: seen.add(w); stack.append(w)
    return len(seen) == n
def acyclic(n, E):                  # union-find: an edge inside one set closes a cycle
    p = list(range(n))
    def find(x): return x if p[x] == x else find(p[x])
    for a, b in E:
        if find(a) == find(b): return False
        p[find(a)] = find(b)
    return True
def npaths(E, u, v, seen=()):       # number of simple u-v paths
    if u == v: return 1
    return sum(npaths(E, w, v, seen + (u,)) for w in nbrs(E, u) if w not in seen)
def six(n, E):
    con, acy, m = connected(n, E), acyclic(n, E), len(E)
    non = [e for e in combinations(range(n), 2) if e not in E]
    return (con and acy,
            all(npaths(E, u, v) == 1 for u, v in combinations(range(n), 2)),
            con and all(not connected(n, [f for f in E if f != e]) for e in E),
            acy and all(not acyclic(n, E + [e]) for e in non),
            con and m == n - 1, acy and m == n - 1)
random.seed(65); trees = 0
for trial in range(600):
    n = random.randint(1, 6); allE = list(combinations(range(n), 2))
    m = min(len(allE), max(0, n - 1 + random.choice([-1, 0, 0, 1])))
    s = six(n, sorted(random.sample(allE, m)))
    assert len(set(s)) == 1; trees += s[0]   # all six true or all six false
print(trial + 1, "random graphs: all six conditions agree;", trees, "were trees")
print("triangle + isolated vertex:", six(4, [(0, 1), (0, 2), (1, 2)]))

আসল আউটপুট (ব্রাউজারের Python):

600 random graphs: all six conditions agree; 302 were trees
triangle + isolated vertex: (False, False, False, False, False, False)

৬০০টি graph-এর edge-সংখ্যা ইচ্ছা করে n − 1-এর কাছাকাছি রাখা হয়েছে, যাতে tree আর “প্রায় tree” দুটিই আসে; ৩০২টি tree হয়েছে। assert একবারও ভাঙেনি: কোনো graph-এ একটি শর্ত সত্য আর আরেকটি মিথ্যা হয়নি। শেষ লাইনের graph-এ ঠিক n − 1 = 3টি edge, তবু (e) আর (f) দুটিই False, কারণ সঙ্গের শর্ত (connected বা acyclic) মানে না। কোড প্রমাণ নয়; সে শুধু দেখায় যে ছোট ক্ষেত্রে পাল্টা-উদাহরণ লুকিয়ে নেই। npaths সব path গোনে, তাই বড় graph-এ এটি দ্রুত অচল হয়।

৩ · Rooted tree — parent, সন্তান, পূর্বপুরুষ, level আর height

বংশতালিকা বা পণ্য-তালিকায় একটি vertex বিশেষ: যেখান থেকে সব শুরু। একটি tree-এর যেকোনো একটি vertex r বেছে নিলে, উপপাদ্য ২.৩(b) অনুযায়ী r থেকে প্রতিটি vertex-এ ঠিক একটি path, আর সেই path ধরে প্রতিটি edge-কে “r থেকে দূরের দিকে” মুখ করানো যায়। এভাবেই tree-এর পরিবার-সম্পর্কের ভাষা আসে।

সংজ্ঞা ৩.১ (rooted tree)

একটি rooted tree হলো একটি tree আর তার একটি বেছে নেওয়া vertex r, যার নাম root. v ≠ r হলে r থেকে v-এর অনন্য path-এ v-এর ঠিক আগের vertex u হলো v-এর parent, আর v হলো u-এর সন্তান (child). একই parent-এর সন্তানরা sibling. r থেকে v-এর path-এ v ছাড়া বাকি সব vertex v-এর পূর্বপুরুষ (ancestor); যাদের পূর্বপুরুষ v, তারা v-এর উত্তরসূরি (descendant). সন্তানহীন vertex leaf; অন্তত একটি সন্তান আছে এমন vertex internal vertex. v আর তার সব উত্তরসূরি মিলে v-root-এর subtree.

v-এর level (বা depth) হলো r থেকে v-এর path-এর দৈর্ঘ্য; root-এর level 0. Tree-এর height h হলো সবচেয়ে বড় level.

একটি সূক্ষ্ম বিষয়: rooted tree-এ leaf মানে সন্তানহীন, তাই root-এর একটি মাত্র সন্তান থাকলে root-এর degree 1 হলেও তাকে leaf বলা হয় না। আরেকটি সতর্কতা: কিছু বই level গোনা 1 থেকে শুরু করে, আর কেউ কেউ height-কে edge নয়, vertex দিয়ে মাপে। পরীক্ষায় সংজ্ঞাটি প্রথমেই লিখে নিন। এই কোর্সে (আর পর্ব ১২-এ) height edge-এ মাপা, একক vertex-এর height 0.

বাজারঘরের পণ্য-তালিকা: একটি rooted tree

r = সব পণ্য; A = মুদি, B = বই, C = ইলেকট্রনিক্স; A1 = চাল, A2 = মসলা, A3 = তেল; C1 = ফোন, C2 = ল্যাপটপ, C3 = চার্জার; x, y, z = হলুদ, মরিচ, জিরা। হলুদ রং root, নীল internal vertex, সবুজ leaf.

Rooted ternary tree of shop categories with levels 0 to 3: root r; A, B, C; A1 to A3 and C1 to C3; x, y, z under A2 স্তর ০স্তর ১স্তর ২স্তর ৩ r ABC A1A2A3 C1C2C3 xyz

ছবিতে পড়ুন: y-এর parent A2, পূর্বপুরুষ A2, A আর r; A-এর উত্তরসূরি A1, A2, A3, x, y, z; y-এর level 3, আর tree-এর height 3. B root-এর সন্তান হয়েও leaf (বইয়ের কোনো উপ-বিভাগ নেই)। “u, v-এর পূর্বপুরুষ অথবা u = v” সম্পর্কটি একটি partial order, আর rooted tree-এর ছবি আসলে তার Hasse diagram (পর্ব ৫৬), root-কে ওপরে রেখে।

সংজ্ঞা ৩.২ (m-ary tree)

প্রতিটি internal vertex-এর বড়জোর mটি সন্তান থাকলে rooted tree-টি m-ary tree; প্রতিটি internal vertex-এর ঠিক mটি সন্তান থাকলে full m-ary tree. m = 2 হলে binary tree আর full binary tree, m = 3 হলে ternary. Binary tree-তে সাধারণত সন্তানদের “বাঁ” আর “ডান” আলাদা ধরা হয় (ordered); কোন সন্তান আগে, সেই ক্রমের বিস্তারিত পর্ব ৬৬ “Tree traversal আর expression tree — prefix, infix, postfix”-এ।

ছবির tree-টি full ternary: r, A, C আর A2-এর প্রত্যেকের ঠিক তিনটি সন্তান। পরের অংশে এর গণনা।

৪ · Full m-ary tree-এর গণনা — n = mi + 1 আর leaf-এর সূত্র

ক্রিকেটের একটি নকআউট টুর্নামেন্ট ভাবুন: প্রতি ম্যাচে দুই দল খেলে, হারলে বাদ। প্রতিটি ম্যাচ একটি internal vertex, তার দুই সন্তান দুই খেলোয়াড় দল (বা আগের দুটি ম্যাচের বিজয়ী), আর leaf হলো শুরুর দলগুলো। ২৭টি দল থাকলে কয়টি ম্যাচ? প্রতিটি ম্যাচে ঠিক একটি দল বাদ পড়ে, আর শেষে একটিই থাকে, তাই ২৬টি ম্যাচ। এই সরল যুক্তির সাধারণ রূপই নিচের উপপাদ্য।

উপপাদ্য ৪.১ (full m-ary tree-এর গণনা)

i-টি internal vertex-এর একটি full m-ary tree-এ মোট vertex n = mi + 1, আর leaf l = (m − 1)i + 1.

  1. Root ছাড়া প্রতিটি vertex ঠিক একটি vertex-এর সন্তান: তার অনন্য parent. — সংজ্ঞা ৩.১
  2. তাই n − 1 = সব vertex-এর সন্তান-সংখ্যার যোগফল (প্রতিটি অ-root vertex ঠিক একবার গোনা হয়)। — দুইভাবে গোনা
  3. Leaf-এর সন্তান 0, আর full m-ary বলে প্রতিটি internal vertex-এর ঠিক m. তাই যোগফল = mi, অর্থাৎ n = mi + 1. — full-এর সংজ্ঞা
  4. প্রতিটি vertex হয় leaf, নয় internal: l = n − i = mi + 1 − i = (m − 1)i + 1. ∎

তিনটি রাশির (n, i, l) যেকোনো একটি জানলে বাকি দুটি বের হয়:

জানা আছেinl
iimi + 1(m − 1)i + 1
n(n − 1)/mn((m − 1)n + 1)/m
l(l − 1)/(m − 1)(ml − 1)/(m − 1)l

নকআউটে m = 2, l = 27, তাই i = 26/1 = 26 ম্যাচ। আর একটি দ্রুত অসম্ভবতা-পরীক্ষা: full ternary tree-তে l − 1 = 2i জোড়, তাই leaf-সংখ্যা সবসময় বিজোড়; ১০০টি leaf-এর full ternary tree নেই। Full binary tree-এর জন্য বিশেষ রূপ: l = i + 1, n = 2i + 1, যা পর্ব ১২-এর structural induction দিয়েও প্রমাণ করা যায়।

নিচের stepper ছবির পণ্য-তালিকাটি এক এক করে গড়ে তোলে: প্রতিবার একটি leaf-কে “ভেঙে” তিনটি সন্তান দেওয়া হয়। প্রতিটি ধাপে সূত্র মেলে কি না, দেখুন।

1শুরু: শুধু root r2r ভাঙা: A, B, C3A ভাঙা: A1, A2, A34C ভাঙা: C1, C2, C35A2 ভাঙা: x, y, z6যাচাই: সন্তান গোনা
m = 3
i
n (গোনা)
3i + 1
l (গোনা)
2i + 1
height
  1. শুরু। শুধু root r: i = 0, n = 1, আর r-এর সন্তান নেই বলে r নিজেই leaf, l = 1. সূত্র: 3 · 0 + 1 = 1 আর 2 · 0 + 1 = 1, মেলে।
  2. Root ভাঙা। r-কে তিন সন্তান দিন: মুদি (A), বই (B), ইলেকট্রনিক্স (C). r এখন internal, তাই i = 1. Vertex 4, leaf 3 (A, B, C). সূত্র 4 আর 3, মেলে।
  3. A ভাঙা। মুদির তিন সন্তান চাল, মসলা, তেল (A1, A2, A3). একটি leaf (A) internal হলো আর তিনটি নতুন leaf এল: n বাড়ে 3, l বাড়ে 3 − 1 = 2. এখন i = 2, n = 7, l = 5; height 2.
  4. C ভাঙা। ইলেকট্রনিক্সের তিন সন্তান ফোন, ল্যাপটপ, চার্জার। i = 3, n = 10, l = 7. নতুন vertex level 2-এ, তাই height 2-ই থাকে।
  5. A2 ভাঙা। মসলার তিন সন্তান হলুদ, মরিচ, জিরা (x, y, z), level 3-এ। i = 4, n = 13 = 3 · 4 + 1, l = 9 = 2 · 4 + 1. Height 3.
  6. সরাসরি যাচাই। সন্তান গুনুন: r, A, C, A2 প্রত্যেকে 3, মোট 12 = n − 1, কারণ root ছাড়া সবাই কারও সন্তান। Leaf নয়টি: B, A1, A3, C1, C2, C3, x, y, z. প্রতিটি ধাপে n বাড়ে m, l বাড়ে m − 1; এটিই induction-এ আরেকটি প্রমাণ।

৫ · Height-এর সীমা, balanced tree আর decision tree

বাজারঘরের পণ্য-তালিকায় একজন ক্রেতা কয়টি ক্লিকে একটি পণ্যে পৌঁছান? যত ক্লিক, তত level, আর সবচেয়ে খারাপ ক্ষেত্রে height-এর সমান। প্রতিটি পাতায় বড়জোর mটি উপ-বিভাগ রাখলে l-টি পণ্যের জন্য height অন্তত কত হতে হবে?

উপপাদ্য ৫.১ (leaf-এর ঊর্ধ্বসীমা)

Height h-এর একটি m-ary tree-তে বড়জোর mhটি leaf. ফলে l-টি leaf-এর m-ary tree-এর height h ≥ ⌈logm l⌉.

প্রমাণ (h-এর ওপর strong induction):

  1. h = 0: tree-টি শুধু root, leaf 1 = m0. — base case
  2. h ≥ 1: root-এর সন্তান k ≤ mটি। প্রতিটি সন্তান-root subtree একটি m-ary tree, height বড়জোর h − 1, তাই hypothesis অনুযায়ী প্রতিটিতে leaf বড়জোর mh−1. — inductive hypothesis
  3. পুরো tree-এর leaf ঠিক এই subtree-গুলোর leaf-এর যোগফল (root নিজে leaf নয়), তাই l ≤ k · mh−1 ≤ mh. — যোগ
  4. l ≤ mh থেকে logm l ≤ h, আর h পূর্ণসংখ্যা, তাই h ≥ ⌈logm l⌉. ∎

সমতা কখন কাছাকাছি আসে? যখন tree “ভারসাম্যপূর্ণ”। একটি height-h rooted tree balanced, যদি সব leaf level h বা h − 1-এ থাকে।

উপপাদ্য ৫.২ (balanced full tree-এর height)

Height h ≥ 1-এর একটি balanced full m-ary tree-তে (m ≥ 2) l-টি leaf থাকলে h = ⌈logm l⌉.

  1. উপপাদ্য ৫.১ থেকে l ≤ mh. — ঊর্ধ্বসীমা
  2. Balanced বলে level h − 2 বা তার ওপরে কোনো leaf নেই; full বলে সেখানকার প্রতিটি vertex-এর mটি সন্তান। তাই level h − 1-এ ঠিক mh−1টি vertex. — level ধরে গোনা
  3. Height h বলে level h − 1-এর অন্তত একটি vertex internal, তার mটি leaf সন্তান; বাকি mh−1 − 1টি vertex হয় leaf, নয় আরও leaf দেয়। তাই l ≥ (mh−1 − 1) + m > mh−1. — m ≥ 2
  4. mh−1 < l ≤ mh, অর্থাৎ h − 1 < logm l ≤ h, তাই h = ⌈logm l⌉. ∎

ছবির পণ্য-তালিকাটি balanced নয়: B level 1-এ, x level 3-এ। তার l = 9, আর ⌈log3 9⌉ = 2, অথচ h = 3. নয়টি পণ্য level 2-এ সাজালে (root-এর তিন সন্তান, প্রত্যেকের তিনটি) প্রতিটি পণ্য দুই ক্লিকে পাওয়া যেত। ডেটাবেসের index-এ ব্যবহৃত B-tree অনেক বড় m নিয়ে ঠিক এই সুবিধা নেয় (DSA কোর্সের পর্ব ৩০), আর binary search tree-কে ভারসাম্যে রাখার কৌশল DSA কোর্সের পর্ব ২৮-এ।

Decision tree আর sorting-এর নিম্নসীমা

একটি decision tree একটি প্রশ্ন-উত্তর প্রক্রিয়ার ছবি: প্রতিটি internal vertex একটি প্রশ্ন, প্রতিটি শাখা একটি সম্ভাব্য উত্তর, আর প্রতিটি leaf একটি চূড়ান্ত সিদ্ধান্ত। দাঁড়িপাল্লায় (তিনটি ফল: বাঁ ভারী, সমান, ডান ভারী) ternary tree আসে; দুটি সংখ্যার তুলনায় (a < b কি না) binary tree. সবচেয়ে খারাপ ক্ষেত্রে প্রশ্নের সংখ্যা tree-এর height, তাই উপপাদ্য ৫.১ সরাসরি নিম্নসীমা দেয়। মেশিন লার্নিং-এর “decision tree” মডেল একই কাঠামো, তবে সেখানে লক্ষ্য ডেটা থেকে প্রশ্ন শেখা (Machine Learning কোর্সের পর্ব ৩২)।

উপপাদ্য ৫.৩ (comparison sort-এর নিম্নসীমা)

যে sorting algorithm উপাদান সম্পর্কে শুধু দুটির তুলনা করে জানে, সে n ≥ 1টি আলাদা key সাজাতে সবচেয়ে খারাপ ক্ষেত্রে অন্তত ⌈log2 n!⌉টি তুলনা করে; আর log2 n! ≥ (n/2) log2(n/2), তাই এই সংখ্যা Ω(n log n).

  1. নির্দিষ্ট n-এর জন্য algorithm-টির আচরণ একটি binary decision tree: প্রতিটি internal vertex “ai < aj?” প্রশ্ন, উত্তর অনুযায়ী বাঁ বা ডান শাখা, আর leaf-এ algorithm থামে ও একটি সাজানোর নিয়ম (একটি permutation) দেয়। — মডেল
  2. n!টি ইনপুট-ক্রমের প্রতিটির জন্য সঠিক উত্তর আলাদা permutation. দুটি আলাদা ইনপুট-ক্রম একই leaf-এ পৌঁছালে algorithm দুটিতে একই permutation প্রয়োগ করে, তাই অন্তত একটির উত্তর ভুল। অতএব অন্তত n!টি leaf. — pigeonhole
  3. উপপাদ্য ৫.১ (m = 2) অনুযায়ী height h ≥ ⌈log2 n!⌉, আর height = সবচেয়ে খারাপ ক্ষেত্রে তুলনার সংখ্যা। — leaf-এর সীমা
  4. n! = 1 · 2 ⋯ n-এর শেষ ⌈n/2⌉টি গুণনীয়কের প্রত্যেকটি ≥ n/2, তাই n! ≥ (n/2)n/2 আর log2 n! ≥ (n/2) log2(n/2), যা n ≥ 4-এ অন্তত (n/4) log2 n. অতএব Ω(n log n). ∎

এই প্রমাণ একটি নির্দিষ্ট algorithm নয়, সব সম্ভাব্য algorithm-এর ওপর একসঙ্গে খাটে: এটাই lower bound-এর শক্তি। Merge sort-এর (DSA কোর্সের পর্ব ২১) সবচেয়ে খারাপ ক্ষেত্রের তুলনা-সংখ্যা জানা সূত্রে W(n) = n⌈log2 n⌉ − 2⌈log2 n⌉ + 1 (পর্ব ৪৭-এর recurrence থেকে আসে)। নিচের ব্লক দুটি সংখ্যা পাশাপাশি রাখে, আর ছোট n-এ সব ইনপুট-ক্রমে merge sort চালিয়ে সূত্রটিও মেলায়।

import math
from itertools import permutations
def merge_sort(a, cnt):                 # counts key comparisons in cnt[0]
    if len(a) <= 1: return a
    mid = (len(a) + 1) // 2
    L, R = merge_sort(a[:mid], cnt), merge_sort(a[mid:], cnt)
    out, i, j = [], 0, 0
    while i < len(L) and j < len(R):
        cnt[0] += 1
        if L[i] <= R[j]: out.append(L[i]); i += 1
        else: out.append(R[j]); j += 1
    return out + L[i:] + R[j:]
def worst_bruteforce(n):                # try every input order
    best = 0
    for p in permutations(range(n)):
        cnt = [0]; merge_sort(list(p), cnt); best = max(best, cnt[0])
    return best
def worst_formula(n):                   # n*ceil(lg n) - 2^ceil(lg n) + 1
    k = (n - 1).bit_length()            # exact ceil(log2 n) for n >= 1
    return n * k - 2 ** k + 1
print(" n  ceil(lg n!)  merge sort worst  (brute force)")
for n in range(2, 13):
    lb = (math.factorial(n) - 1).bit_length()   # exact ceil(log2 n!)
    bf = worst_bruteforce(n) if n <= 7 else "-"
    print(f"{n:2}  {lb:11}  {worst_formula(n):16}  {bf:>13}")
for n in (10**3, 10**6):
    lg_fact = math.lgamma(n + 1) / math.log(2)  # log2(n!) without n!
    print(f"n={n}: log2(n!) = {lg_fact:.0f}, n*log2(n) = {n*math.log2(n):.0f}")

আসল আউটপুট (ব্রাউজারের Python):

 n  ceil(lg n!)  merge sort worst  (brute force)
 2            1                 1              1
 3            3                 3              3
 4            5                 5              5
 5            7                 8              8
 6           10                11             11
 7           13                14             14
 8           16                17              -
 9           19                21              -
10           22                25              -
11           26                29              -
12           29                33              -
n=1000: log2(n!) = 8529, n*log2(n) = 9966
n=1000000: log2(n!) = 18488885, n*log2(n) = 19931569

n ≤ 4-এ merge sort নিম্নসীমা ছুঁয়ে ফেলে; তারপর সামান্য বেশি, n = 12-এ 33 বনাম 29. n ≤ 7-এ সব n!টি ক্রম চালিয়ে পাওয়া সর্বোচ্চ সংখ্যা সূত্রের সঙ্গে হুবহু মেলে। শেষ দুই লাইন দেখায় log2 n! আর n log2 n একই মাত্রার, অনুপাত ধীরে ধীরে 1-এর দিকে যায় (log2 n! ≈ n log2 n − 1.44n, Stirling-এর সূত্র)। দুটি কৌশল লক্ষ করুন: (x - 1).bit_length() পূর্ণসংখ্যায় নির্ভুল ⌈log2 x⌉ দেয়, float-এর গোলমাল ছাড়া; আর lgamma বিশাল n! না বানিয়েই তার লগ দেয়।

সীমাটি শুধু তুলনা-ভিত্তিক পদ্ধতির জন্য। Counting sort বা radix sort key-এর অঙ্ক সরাসরি পড়ে, তুলনা করে না, তাই নির্দিষ্ট পরিসরের key-এ রৈখিক সময়ে চলতে পারে (DSA কোর্সের পর্ব ২৪)। আর গড় ক্ষেত্রেও একই ধরনের নিম্নসীমা খাটে: n!টি leaf-এর binary tree-এ leaf-এর গড় depth অন্তত log2 n!.

৬ · Labelled tree গোনা — Cayley-র সূত্র আর Prüfer code

বাজারঘরের সাতটি হাব (নাম 1 থেকে 7) ছয়টি রাস্তায় এমনভাবে জুড়তে হবে, যাতে সব হাব connected থাকে; উপপাদ্য ২.৩(e) অনুযায়ী প্রতিটি এমন নকশা একটি tree. কয়টি আলাদা নকশা সম্ভব? এখানে vertex-এর নাম আছে, তাই 1–2 রাস্তা আর 1–3 রাস্তা আলাদা নকশা, যদিও ছবির আকার একই। এমন গোনাকে বলে labelled গণনা; নাম মুছে শুধু আকার গুনলে unlabelled (পর্ব ৫৮-এর isomorphism প্রশ্ন)।

ছোট ক্ষেত্রে হাতে গুনুন। n = 2: একটি। n = 3: তিনটি path, মাঝের vertex বেছে নেওয়ার ৩ উপায়। n = 4: দুই আকার, path আর তারকা; path-এর 4!/2 = 12টি নামকরণ (উল্টো দিক একই), তারকার কেন্দ্র বাছার ৪ উপায়, মোট 16. ধারা 1, 3, 16 = 20, 31, 42.

উপপাদ্য ৬.১ (Cayley-র সূত্র)

n ≥ 2 হলে {1, 2, …, n} vertex-সেটের ওপর আলাদা tree-এর সংখ্যা nn−2.

প্রমাণের ধারণা: প্রতিটি tree-কে {1, …, n}-এর সংখ্যা দিয়ে গড়া n − 2 দৈর্ঘ্যের একটি সারির সঙ্গে এক-এক মেলানো। এমন সারি ঠিক nn−2টি (প্রতিটি ঘরে n বিকল্প, পর্ব ৩১-এর গুণের নিয়ম), তাই একটি bijection (পর্ব ১৬) পেলেই কাজ শেষ। সেই সারির নাম Prüfer code.

সংজ্ঞা ৬.২ (Prüfer encoding)

n ≥ 2 vertex-এর labelled tree T থেকে: যতক্ষণ দুইয়ের বেশি vertex আছে, সবচেয়ে ছোট নম্বরের leaf-টি নিন, তার (একমাত্র) প্রতিবেশীর নম্বর সারিতে লিখুন, আর leaf-টি মুছে দিন। ঠিক n − 2 বার করার পর দুটি vertex থাকে; তখন থামুন। প্রতিটি ধাপে বাকি graph-টি tree থাকে আর তাতে অন্তত দুটি leaf থাকে (উপপাদ্য ১.২), তাই প্রক্রিয়াটি সবসময় চলে।

সাত হাবের একটি ডেলিভারি নকশা

Edge: 1–5, 3–5, 2–6, 4–6, 5–7, 6–7. সবুজ vertex শুরুর leaf. নিচের stepper এর Prüfer code বের করে।

Labelled tree on vertices 1 to 7 with edges 1-5, 3-5, 2-6, 4-6, 5-7, 6-7; leaves 1, 2, 3, 4 756 1324
1leaf খুঁজুন21 মুছুন, লিখুন 532 মুছুন, লিখুন 643 মুছুন, লিখুন 554 মুছুন, লিখুন 665 মুছুন, লিখুন 77বাকি 6–7: থামুন
সবচেয়ে ছোট leaf মুছে প্রতিবেশী লেখা
leaf-সেট
মোছা
সারি
  1. শুরু। Edge 1–5, 3–5, 2–6, 4–6, 5–7, 6–7. Degree 1 এমন vertex: 1, 2, 3, 4. সারি এখনো খালি।
  2. প্রথম মোছা। সবচেয়ে ছোট leaf 1, তার প্রতিবেশী 5. সারিতে 5 লিখে 1 মুছে দিন। Leaf এখন {2, 3, 4}.
  3. দ্বিতীয়। সবচেয়ে ছোট leaf 2, প্রতিবেশী 6. সারি (5, 6). Leaf এখন {3, 4}.
  4. তৃতীয়। Leaf 3, প্রতিবেশী 5; সারি (5, 6, 5). 1 আর 3 চলে যাওয়ায় 5-এর একমাত্র প্রতিবেশী এখন 7: 5 নতুন leaf. Leaf-সেট {4, 5}.
  5. চতুর্থ। সবচেয়ে ছোট leaf 4 (5 নয়), প্রতিবেশী 6; সারি (5, 6, 5, 6). এখন 6-এরও একমাত্র প্রতিবেশী 7, তাই leaf-সেট {5, 6}.
  6. পঞ্চম। Leaf 5, প্রতিবেশী 7; সারি (5, 6, 5, 6, 7). বাকি vertex 6 আর 7, মাত্র দুটি।
  7. থামা। n − 2 = 5 বার হয়ে গেছে, শেষ edge 6–7 লেখা হয় না। Prüfer code (5, 6, 5, 6, 7). লক্ষ করুন: 5 আর 6 দুইবার করে আছে (degree 3), 7 একবার (degree 2), আর কোনো leaf সারিতে নেই।

শেষ ধাপের পর্যবেক্ষণটিই চাবি।

লেমা ৬.৩ (degree আর উপস্থিতি)

Tree T-এর Prüfer code-এ প্রতিটি vertex v ঠিক deg(v) − 1 বার আসে। বিশেষ করে, T-এর leaf ঠিক সেই vertex-গুলো, যারা সারিতে নেই।

  1. v সারিতে লেখা হয় ঠিক তখন, যখন v-এর একটি প্রতিবেশী leaf হিসেবে মোছা হয়; অর্থাৎ v-এর গায়ে লাগা একটি edge সরে যায়, আর মোছা vertex-টি v নয়। — encoding-এর নিয়ম
  2. যদি v নিজে কোনো ধাপে মোছা হয়, তখন তার শেষ edge সরে, কিন্তু সেবার v লেখা হয় না (লেখা হয় প্রতিবেশী)। আগের deg(v) − 1টি edge সরেছিল v-কে লিখে। — ক্ষেত্র ১
  3. যদি v শেষের দুই vertex-এর একটি হয়, তবে শেষ edge কখনো সরে না, আর বাকি deg(v) − 1টি সরেছে v-কে লিখে। — ক্ষেত্র ২
  4. দুই ক্ষেত্রেই উপস্থিতি deg(v) − 1; leaf-এর জন্য 0. ∎

এখন উল্টো দিক। সারি (a1, …, an−2) দেওয়া আছে। লেমা বলে প্রথম মোছা leaf হলো সারিতে নেই এমন সবচেয়ে ছোট vertex b1, আর তার প্রতিবেশী a1. তাই প্রথম edge b1–a1 নিশ্চিতভাবে জানা যায়। b1 সরালে বাকি tree-এর code হলো (a2, …, an−2), আর একই যুক্তি চলতে থাকে।

সংজ্ঞা ৬.৪ (Prüfer decoding)

শুরুতে প্রতিটি v-এর “বাকি degree” = সারিতে v-এর উপস্থিতি + 1. সারির প্রতিটি ak-এর জন্য ক্রমে: বাকি degree 1 এমন সবচেয়ে ছোট vertex b নিন, edge b–ak যোগ করুন, b-কে বাদ দিন, আর ak-এর বাকি degree 1 কমান। শেষে বাদ না পড়া দুটি vertex-এর মধ্যে একটি edge যোগ করুন।

1degree = উপস্থিতি + 12a1 = 33a2 = 14a3 = 35a4 = 76a5 = 77শেষ দুটি জোড়া
n = 7, সারি (3, 1, 3, 7, 7)
leaf-সেট
নতুন edge
edge-সংখ্যা
  1. Degree গোনা। সারিতে 3 দুইবার, 7 দুইবার, 1 একবার। তাই degree: 3 আর 7-এর 3, 1-এর 2, আর 2, 4, 5, 6-এর 1. Leaf {2, 4, 5, 6}, যারা সারিতে নেই।
  2. a1 = 3. সবচেয়ে ছোট leaf 2. Edge 2–3; 2 বাদ, 3-এর বাকি degree 3 থেকে 2.
  3. a2 = 1. Leaf {4, 5, 6}, সবচেয়ে ছোট 4. Edge 4–1; 1-এর বাকি degree 2 থেকে 1, তাই 1 এখন leaf.
  4. a3 = 3. Leaf {1, 5, 6}, সবচেয়ে ছোট 1. Edge 1–3; 3-এর বাকি degree 1, এখন 3 leaf.
  5. a4 = 7. Leaf {3, 5, 6}, সবচেয়ে ছোট 3. Edge 3–7; 7-এর বাকি degree 3 থেকে 2.
  6. a5 = 7. Leaf {5, 6}, সবচেয়ে ছোট 5. Edge 5–7; 7-এর বাকি degree 1.
  7. শেষ জোড়া। বাকি দুই vertex 6 আর 7: edge 6–7. মোট ৬টি edge: 2–3, 1–4, 1–3, 3–7, 5–7, 6–7, যা 7 vertex-এর একটি tree. এই tree-কে আবার encode করলে ঠিক (3, 1, 3, 7, 7) ফেরে (নিচের ব্লক আর scratch-এ মেলানো)।

উপপাদ্য ৬.১-এর প্রমাণ (bijection, n-এর ওপর induction): দাবি: n ≥ 2 হলে যেকোনো n-উপাদানের ক্রমযুক্ত vertex-সেট V-এর জন্য encoding হলো V-এর ওপর সব tree থেকে Vn−2-এ একটি bijection.

  1. ভিত্তি n = 2: V-এর ওপর tree একটিই (একক edge), আর সারি একটিই (খালি)। — base case
  2. One-to-one: দুটি tree T, T′-এর code একই (a1, …) ধরুন। লেমা ৬.৩ অনুযায়ী দুটির leaf-সেট একই (সারিতে নেই এমন vertex), তাই প্রথম মোছা leaf b1 একই, আর দুটিতেই b1-এর প্রতিবেশী a1. T − b1 আর T′ − b1-এর code (a2, …) একই, তাই hypothesis অনুযায়ী তারা একই tree; edge b1a1 ফিরিয়ে দিলে T = T′. — inductive hypothesis
  3. Onto: যেকোনো সারি (a1, …, an−2) নিন; b1 = সারিতে নেই এমন সবচেয়ে ছোট vertex (n − 2 < n বলে আছে)। (a2, …) সারির সব সংখ্যা V − {b1}-এ, তাই hypothesis অনুযায়ী V − {b1}-এর ওপর একটি tree T′ আছে যার code (a2, …). T = T′ + edge b1a1. — নির্মাণ
  4. T-এর leaf: b1, আর T′-এর leaf-গুলো a1 বাদে। লেমা অনুযায়ী T′-এর leaf = সারি (a2, …)-তে নেই এমন vertex. তাই T-এর leaf-সেট = V − {a1, …, an−2}, যার সবচেয়ে ছোটটি b1. ফলে T-এর encoding প্রথমে b1 মুছে a1 লেখে, তারপর T′-এর মতো চলে: code (a1, a2, …). — লেমা ৬.৩
  5. Bijection পাওয়া গেল, তাই tree-সংখ্যা = |V|n−2 = nn−2. ∎

লেমা ৬.৩ একটি বাড়তি উপহার দেয়: নির্দিষ্ট degree-তালিকা d1, …, dn (যোগফল 2n − 2, প্রতিটি ≥ 1) মেনে labelled tree-এর সংখ্যা হলো সারিতে v-কে ঠিক dv − 1 বার রাখার উপায়, অর্থাৎ multinomial (n − 2)! / ∏(dv − 1)! (পর্ব ৩৫)। যেমন vertex 1-এর degree n − 1 (তারকা) হলে সারি (1, 1, …, 1), উত্তর 1. নিচের ব্লক n ≤ 6-এ Kn-এর সব (n − 1)-edge উপসেট থেকে tree বেছে গোনে, প্রতিটিকে encode আর decode করে মূল tree ফেরে কি না দেখে, আর সব nn−2টি সারি decode করে আবার encode করে।

from itertools import combinations, product
def encode(n, edges):                   # tree on 1..n  ->  Prufer sequence
    adj = {v: set() for v in range(1, n + 1)}
    for a, b in edges: adj[a].add(b); adj[b].add(a)
    seq = []
    for _ in range(n - 2):
        leaf = min(v for v in adj if len(adj[v]) == 1)   # smallest leaf
        nb = adj.pop(leaf).pop()
        seq.append(nb); adj[nb].discard(leaf)
    return tuple(seq)
def decode(seq):                        # Prufer sequence -> edge set
    n = len(seq) + 2
    deg, edges = {v: 1 + seq.count(v) for v in range(1, n + 1)}, []
    for a in seq:
        leaf = min(v for v in deg if deg[v] == 1)
        edges.append(frozenset((leaf, a))); deg[a] -= 1; del deg[leaf]
    edges.append(frozenset(deg))        # the last two vertices
    return frozenset(edges)
def is_tree(n, edges):                  # n-1 edges, no cycle (union-find)
    p = list(range(n + 1))
    def find(x): return x if p[x] == x else find(p[x])
    for a, b in edges:
        if find(a) == find(b): return False
        p[find(a)] = find(b)
    return True
print(encode(7, [(1, 5), (2, 6), (3, 5), (4, 6), (5, 7), (6, 7)]))
print(sorted(tuple(sorted(e)) for e in decode((3, 1, 3, 7, 7))))
for n in range(2, 7):
    trees = [t for t in combinations(combinations(range(1, n + 1), 2), n - 1)
             if is_tree(n, t)]
    codes = {encode(n, t) for t in trees}           # encode is one-to-one?
    ok = all(decode(encode(n, t)) == frozenset(map(frozenset, t)) for t in trees)
    onto = all(encode(n, [tuple(e) for e in decode(s)]) == s
               for s in product(range(1, n + 1), repeat=n - 2))
    print(n, len(trees), n ** (n - 2), len(codes), ok, onto)

আসল আউটপুট (ব্রাউজারের Python):

(5, 6, 5, 6, 7)
[(1, 3), (1, 4), (2, 3), (3, 7), (5, 7), (6, 7)]
2 1 1 1 True True
3 3 3 3 True True
4 16 16 16 True True
5 125 125 125 True True
6 1296 1296 1296 True True

প্রথম দুই লাইন দুই stepper-এর ফল হুবহু দেয়। পরের প্রতিটি লাইনে: n, brute force-এ পাওয়া tree-সংখ্যা, nn−2, আলাদা code-এর সংখ্যা (tree-সংখ্যার সমান মানে encoding one-to-one), decode(encode(T)) = T সবার জন্য, আর প্রতিটি সারি একটি tree থেকে আসে (onto হওয়া)। n = 6-এ C(15, 5) = 3003টি edge-উপসেটের মধ্যে ১২৯৬টি tree. n = 7-এ scratch-এ একই পরীক্ষায় 16807 = 75 মিলেছে; তার পর থেকে brute force দ্রুত ভারী হয়, কিন্তু Prüfer decoding যেকোনো n-এ একটি এলোমেলো tree বানানোর সহজ উপায় (পরের ব্লক এটিই ব্যবহার করে)।

৭ · Binary tree গোনা, tree-এর কেন্দ্র, আর BST ও heap

Binary tree আর unlabelled tree গোনা

Cayley-র সূত্র নামযুক্ত vertex গোনে। Ordered binary tree (বাঁ আর ডান সন্তান আলাদা, vertex-এর নাম নেই) গোনা আরেক প্রশ্ন: n vertex-এর binary tree ঠিক Catalan সংখ্যা Cn = C(2n, n)/(n + 1)টি: 1, 1, 2, 5, 14, 42, … (n = 0 থেকে)। Root বাদে বাকি n − 1টি vertex বাঁ আর ডান subtree-তে ভাগ হয়, তাই Cn = ∑ CiCn−1−i; প্রমাণ আর বন্ধনীর সঙ্গে bijection পর্ব ৩৭ “গণনার কৌশল — lattice path, bijection আর double counting”-এ, আর generating function-এ সমাধান পর্ব ৫০-এ। আর নাম ও ক্রম দুটিই মুছে শুধু আকার গুনলে (unlabelled free tree) n = 1 থেকে 8-এ সংখ্যা 1, 1, 1, 2, 3, 6, 11, 23 (scratch-এ Prüfer দিয়ে সব labelled tree বানিয়ে আকার মিলিয়ে গোনা); এর কোনো সরল বদ্ধ সূত্র জানা নেই, symmetry-র কারণে গণনা কঠিন (পর্ব ৫১-এর Pólya-র পদ্ধতি এখানে কাজে লাগে)।

Tree-এর কেন্দ্র

বাজারঘর সাত হাবের tree-আকৃতির নেটওয়ার্কে একটি কেন্দ্রীয় গুদাম বসাতে চায়, যাতে সবচেয়ে দূরের হাবও যতটা সম্ভব কাছে থাকে। Vertex v-এর eccentricity e(v) = v থেকে সবচেয়ে দূরের vertex-এর দূরত্ব (edge-সংখ্যায়)। সবচেয়ে কম eccentricity-ওয়ালা vertex-গুলো মিলে কেন্দ্র (center) বলে। সাধারণ graph-এ কেন্দ্রে অনেক vertex থাকতে পারে (cycle Cn-এ সবাই কেন্দ্র), কিন্তু tree-তে নয়।

উপপাদ্য ৭.১ (Jordan, ১৮৬৯)

প্রতিটি tree-এর কেন্দ্র হয় একটি vertex, নয়তো একটি edge-এর দুই প্রান্ত।

প্রমাণের রূপরেখা (পাতা ছাঁটাই):

  1. n ≤ 2 হলে দাবি সরাসরি সত্য। n ≥ 3 হলে T-এর সব leaf একসঙ্গে মুছে T′ পান; T′ একটি tree (একটি leaf মুছলে connected আর acyclic থাকে), আর n ≥ 3-এ T′ খালি নয়। — উপপাদ্য ২.৩
  2. যেকোনো v থেকে সবচেয়ে দূরের vertex w একটি leaf: w-এর আরেকটি প্রতিবেশী থাকলে v থেকে তার দূরত্ব আরও বেশি হতো, কারণ tree-তে path অনন্য। — অনন্য path
  3. তাই T′-এর প্রতিটি v-এর জন্য eT′(v) = eT(v) − 1: সবচেয়ে দূরের leaf-গুলো চলে যায়, আর তাদের ঠিক আগের vertex (যা leaf নয়) এক কম দূরত্বে থেকে যায়। — দূরত্ব এক কমে
  4. n ≥ 3-এ একটি leaf-এর eccentricity তার প্রতিবেশীর চেয়ে ঠিক 1 বেশি, তাই leaf কখনো কেন্দ্রে থাকে না। ফলে কেন্দ্র(T) = কেন্দ্র(T′). — সবার eccentricity সমান হারে কমে
  5. বারবার ছাঁটাই করলে vertex-সংখ্যা কমতে কমতে 1 বা 2-এ থামে; 2 হলে তারা tree-তে সংলগ্ন। সেটিই কেন্দ্র। ∎

ছাঁটাইয়ের এই পদ্ধতি algorithm-ও: “প্রতিবার সব leaf মুছুন” রৈখিক সময়ে কেন্দ্র দেয়। Tree-এর diameter (সবচেয়ে বড় দূরত্ব) আর radius (কেন্দ্রের eccentricity) মেনে চলে diameter ∈ {2 · radius − 1, 2 · radius}, আর দুটি tree isomorphic কি না দেখতে কেন্দ্রকে root ধরা হয়, ভাবনার প্রশ্ন ৫ দেখুন। DSA কোর্সের পর্ব ৪৮-এ diameter বের করার দুই-BFS কৌশল আছে। নিচের ব্লক এলোমেলো tree-তে (এলোমেলো Prüfer সারি decode করে) কেন্দ্রের সংজ্ঞা আর ছাঁটাইয়ের ফল মেলায়, আর §১-এর leaf-সূত্রও পরীক্ষা করে।

import random
from collections import Counter
def random_tree(n):                     # decode a random Prufer sequence
    seq = [random.randint(1, n) for _ in range(n - 2)]
    V = range(1, n + 1)
    deg, adj = {v: 1 + seq.count(v) for v in V}, {v: set() for v in V}
    for a in seq:
        leaf = min(v for v in deg if deg[v] == 1)
        adj[leaf].add(a); adj[a].add(leaf); deg[a] -= 1; del deg[leaf]
    u, w = deg; adj[u].add(w); adj[w].add(u)
    return adj
def ecc(adj, s):                        # eccentricity by BFS
    dist, frontier = {s: 0}, [s]
    for u in frontier:
        for w in adj[u]:
            if w not in dist: dist[w] = dist[u] + 1; frontier.append(w)
    return max(dist.values())
def strip_leaves(adj):                  # peel off all leaves until 1 or 2 remain
    alive = set(adj)
    while len(alive) > 2:
        leaves = {v for v in alive if len(adj[v] & alive) == 1}
        alive -= leaves
    return alive
random.seed(65); sizes = Counter()
for trial in range(300):
    adj = random_tree(random.randint(2, 15))
    e = {v: ecc(adj, v) for v in adj}
    centre = {v for v in adj if e[v] == min(e.values())}
    assert centre == strip_leaves(adj)                       # Jordan
    assert len(centre) == 1 or max(centre) in adj[min(centre)]   # or an edge
    deg = [len(adj[v]) for v in adj]
    assert deg.count(1) == 2 + sum(d - 2 for d in deg if d >= 3)
    sizes[len(centre)] += 1
print("300 random trees: centre = leaf-stripping result; sizes", dict(sizes))

আসল আউটপুট (ব্রাউজারের Python):

300 random trees: centre = leaf-stripping result; sizes {1: 138, 2: 162}

২ থেকে ১৫ vertex-এর ৩০০টি এলোমেলো labelled tree-এর প্রতিটিতে তিনটি assert টিকে গেছে: BFS দিয়ে সংজ্ঞা অনুযায়ী পাওয়া কেন্দ্র আর ছাঁটাইয়ের ফল একই; কেন্দ্র একটি vertex বা একটি edge; আর leaf-সংখ্যা = 2 + ∑(deg − 2). ১৩৮টিতে কেন্দ্র একটি vertex, ১৬২টিতে একটি edge. Prüfer সারি সমসম্ভাব্যভাবে বাছলে (n স্থির রেখে) প্রতিটি labelled tree সমান সম্ভাবনায় আসে, কারণ encoding একটি bijection; তাই এটি “এলোমেলো tree”-এর সৎ নমুনা।

BST আর heap — data structure হিসেবে tree

প্রোগ্রামিংয়ে সবচেয়ে বেশি দেখা দুটি tree এই পর্বের হিসাবেই চলে। Binary search tree-এ প্রতিটি vertex-এ একটি key, আর বাঁ subtree-র সব key ছোট, ডান subtree-র সব key বড়; একটি key খুঁজতে root থেকে নিচে নামতে হয়, তাই সবচেয়ে খারাপ খরচ height + 1টি তুলনা। পর্ব ১২-এর n ≤ 2h+1 − 1 (একই প্রমাণ যেকোনো binary tree-তে খাটে) থেকে h ≥ ⌈log2(n + 1)⌉ − 1, তাই n key-এর কোনো BST log-এর চেয়ে ভালো নিশ্চয়তা দিতে পারে না, আর key ক্রমানুসারে ঢোকালে tree একটি path হয়ে h = n − 1 (DSA কোর্সের পর্ব ২৭)। Heap একটি complete binary tree (শেষ level ছাড়া সব level ভরা, শেষটি বাঁ থেকে ভরা), তাই তার height ঠিক ⌊log2 n⌋, আর insert বা সবচেয়ে ছোটটি তুলে নেওয়া O(log n) (DSA কোর্সের পর্ব ২৩)। Tree-এর সাধারণ পরিভাষা আর কোডে উপস্থাপন DSA কোর্সের পর্ব ২৬-এ।

৮ · পরীক্ষার অঙ্ক আর একটি ভুল প্রমাণ

অঙ্ক ১: Degree থেকে leaf

একটি tree-তে দুটি vertex-এর degree 4, একটির 3, তিনটির 2, আর বাকি সব leaf. কয়টি leaf, কয়টি vertex, কয়টি edge?

  1. সূত্র: l = 2 + (4 − 2) + (4 − 2) + (3 − 2) = 7.
  2. n = 7 + 2 + 1 + 3 = 13, m = n − 1 = 12.
  3. যাচাই (handshake): 4 + 4 + 3 + 3 · 2 + 7 · 1 = 24 = 2 · 12. এমন একটি tree সত্যিই বানানো যায় (scratch-এ একটি নির্দিষ্ট tree গড়ে মেলানো)।

অঙ্ক ২: Forest-এর edge

২০টি vertex-এর একটি forest-এ ৩টি component. কয়টি edge? উত্তর: লেমা ২.২ অনুযায়ী m = n − c = 20 − 3 = 17. উল্টো দিকে, ২০ vertex আর ১৭ edge-এর graph acyclic হলে component ঠিক ৩টি; acyclic না হলে c > 3.

অঙ্ক ৩: Full m-ary tree

(ক) একটি full 5-ary tree-তে ১০০টি internal vertex. মোট vertex আর leaf? (খ) একটি full 4-ary tree-তে ১০০টি leaf. Internal vertex কয়টি? (গ) ১০০টি leaf-এর full ternary tree সম্ভব?

  1. (ক) n = 5 · 100 + 1 = 501, l = 4 · 100 + 1 = 401.
  2. (খ) i = (100 − 1)/3 = 33, n = 4 · 33 + 1 = 133.
  3. (গ) i = 99/2 পূর্ণসংখ্যা নয়, তাই অসম্ভব.

অঙ্ক ৪: দাঁড়িপাল্লার decision tree

১২টি দেখতে একই রকম মুদ্রার একটি নকল, যা বাকিদের চেয়ে হয় ভারী, নয় হালকা (কোনটি জানা নেই)। দাঁড়িপাল্লায় অন্তত কয়বার মাপতে হবে, নকলটি আর সেটি ভারী না হালকা বের করতে?

  1. সম্ভাব্য উত্তর 12 · 2 = 24টি; প্রতিটির জন্য decision tree-এ আলাদা leaf লাগে। — উপপাদ্য ৫.৩-এর যুক্তি
  2. প্রতিবার মাপায় তিনটি ফল, তাই tree ternary; h বার মাপায় বড়জোর 3h leaf. 32 = 9 < 24, তাই h ≥ ⌈log3 24⌉ = 3. — উপপাদ্য ৫.১
  3. তিনবারে সত্যিই হয়; এটি একটি পুরোনো বিখ্যাত ধাঁধা, যার সমাধান নিম্নসীমাকে ঠিক ছুঁয়ে দেয়। তুলনায় ৮টি মুদ্রার একটি শুধু ভারী হলে ⌈log3 8⌉ = 2: প্রথমে 3 বনাম 3; সমান হলে বাকি দুটি মাপুন, নইলে ভারী দিকের তিনটির দুটি মাপুন।

প্রমাণের ভুল ধরুন: “n − 1টি edge থাকলেই tree”

একজন লিখলেন: “দাবি: n vertex আর n − 1 edge-এর প্রতিটি graph একটি tree. n-এর ওপর induction. n = 1-এ সত্য। ধরুন n-এর জন্য সত্য, আর G-তে n + 1টি vertex, nটি edge. গড় degree 2n/(n + 1) < 2, তাই কোনো vertex v-এর degree ≤ 1; ধরি deg(v) = 1. v সরালে nটি vertex আর n − 1টি edge, hypothesis অনুযায়ী tree; v-কে একটি edge দিয়ে ফিরিয়ে দিলে আবার tree. ∎”

ভুল কোথায়: “degree ≤ 1” থেকে “ধরি deg(v) = 1” লাফটি অবৈধ: degree 0-ও হতে পারে। তখন v একা vertex, আর v সরালে nটি vertex কিন্তু n টি edge থাকে, hypothesis খাটেই না। দাবিটিই মিথ্যা: পাল্টা-উদাহরণ একটি ত্রিভুজ আর একটি একা vertex, যেখানে n = 4 আর m = 3. সঠিক উপপাদ্যে (উপপাদ্য ২.৩(e) বা (f)) “connected” বা “acyclic” শর্ত লাগে। দ্বিতীয় সতর্কতা: ছোট graph-এ একটি vertex যোগ করে বড় graph “গড়ার” induction সব বড় graph-কে ধরে কি না, তা আলাদা করে দেখাতে হয়; নিরাপদ পথ হলো যেকোনো বড় graph থেকে শুরু করে ছোট graph-এ নামা, যেমন লেমা ২.২-এ করা হয়েছে।

আরও গভীরে: Prüfer bijection-এর একটি সুন্দর ফল: একটি নির্দিষ্ট vertex leaf হওয়ার সম্ভাবনা ((n − 1)/n)n−2, যা n বাড়লে 1/e-এর দিকে যায়; অর্থাৎ বড় এলোমেলো labelled tree-এর প্রায় 37% vertex leaf. Cayley-র সূত্রের আরও কয়েকটি বিখ্যাত প্রমাণ আছে, যেমন Joyal-এর (১৯৮১) bijection আর Pitman-এর double counting, যা Aigner আর Ziegler-এর “Proofs from THE BOOK”-এ একসঙ্গে আলোচিত বলে জানা যায়। আর nn−2 আসলে complete graph Kn-এর spanning tree-এর সংখ্যা; যেকোনো graph-এর spanning tree গোনার সূত্র Kirchhoff-এর matrix-tree উপপাদ্য, যা পর্ব ৬৭-এ আসবে।

পরীক্ষা ও ইন্টারভিউতে: বিশ্ববিদ্যালয়, চাকরি বা ইন্টারভিউর পরীক্ষায় এই ধরনের প্রশ্ন প্রায়ই আসে:

  • n vertex-এর tree-তে কয়টি edge, আর কেন? n − 1; লেমা ২.২ বা leaf মুছে induction.
  • Full m-ary tree-এ i internal হলে n আর l? n = mi + 1, l = (m − 1)i + 1.
  • ৩২ দলের নকআউটে কয়টি ম্যাচ আর কয় রাউন্ড? 31টি ম্যাচ, log2 32 = 5 রাউন্ড.
  • Comparison sort কেন n log n-এর চেয়ে দ্রুত হয় না? Decision tree-এ n! leaf, height ≥ log2 n! = Θ(n log n).
  • Prüfer code (2, 2, 2) কোন tree? 5 vertex-এর তারকা, কেন্দ্র 2 (degree 4).

৯ · AI-এর যুগে — tree-এর প্রমাণ আর গণনা যাচাই

Tree-এর প্রশ্নে AI সহকারী সংজ্ঞা আর বিখ্যাত সূত্র সাধারণত ঠিক বলে, কিন্তু শর্ত বাদ দেয়, labelled আর unlabelled গুলিয়ে ফেলে, height আর level-এ এক কম-বেশি করে। কয়েকটি নিয়মিত ভুল আর ধরার উপায়:

AI-এর ভুলকীভাবে ধরবেন
“n − 1টি edge থাকলেই tree”ত্রিভুজ আর একটি একা vertex: ৪ vertex, ৩ edge, tree নয়
n vertex-এর আলাদা আকারের tree nn−2টি বলাn = 4: labelled 16, অথচ আকার মাত্র ২টি (path আর তারকা)
Full নয় এমন tree-তে n = mi + 1 প্রয়োগ৩ vertex-এর path (root-এর একটি সন্তান): i = 2, n = 3 ≠ 2 · 2 + 1
Height-এর সীমায় logm n আর logm l গুলিয়ে ফেলা, বা level 1 থেকে গোনাসংজ্ঞা লিখিয়ে নিন; ছোট perfect tree-তে মিলিয়ে দেখুন
“যেকোনো sort-এ n log n লাগে”Counting sort তুলনা করে না; নিম্নসীমা শুধু comparison sort-এর
Prüfer decoding-এ “সারির সবচেয়ে ছোট সংখ্যা” নেওয়ানিতে হয় সারিতে নেই এমন সবচেয়ে ছোট vertex; decode করে আবার encode করুন

AI-এর কাছে tree-এর প্রমাণ বা হিসাব চাইলে এমন কাঠামো দিন, যাতে প্রতিটি ধাপ আলাদা করে যাচাই করা যায়:

নিচের tree-সংক্রান্ত দাবিটি ধাপে ধাপে প্রমাণ করো বা খণ্ডন করো: দাবি: [যেমন "প্রতিটি tree-তে leaf-এর সংখ্যা অন্তত সর্বোচ্চ degree-র সমান"] ১. শুরুতেই লেখো tree rooted কি না, height আর level edge-এ না vertex-এ মাপছ, আর labelled না unlabelled গুনছ। ২. প্রতিটি ধাপের পাশে কারণ লেখো (সংজ্ঞা, handshake lemma, কোন উপপাদ্য), আর উপপাদ্যের সব শর্ত (connected, acyclic, full) আলাদা করে মিলিয়ে দেখাও। ৩. Induction করলে base case, hypothesis আর step আলাদা শিরোনামে লেখো; step-এ যেকোনো বড় tree থেকে শুরু করে ছোট tree-তে নামো। ৪. দাবি মিথ্যা হলে সবচেয়ে ছোট পাল্টা-উদাহরণ edge-তালিকা আকারে দাও। ৫. ৮টি vertex পর্যন্ত দাবিটি brute force-এ পরীক্ষা করার একটি Python কোড দাও (শুধু itertools আর math), যাতে আমি নিজে চালাতে পারি। ৬. কোনো সূত্র, নাম বা সাল নিয়ে নিশ্চিত না হলে তা স্পষ্ট বলো, বানিয়ে উৎস দিয়ো না।

AI-এর উত্তর হাতে পেলে — যাচাইয়ের চেকলিস্ট

Tree-এর বেশির ভাগ দাবি ৭–৮টি vertex পর্যন্ত সব tree তৈরি করে (Prüfer দিয়ে সহজেই) পরীক্ষা করা যায়; AI-এর প্রমাণ পড়ার আগেই এই পরীক্ষা চালিয়ে নিলে ভুল দাবি দ্রুত ধরা পড়ে।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন, তারপর “→ উত্তর” চাপুন।

প্র ০১“Tree” নামটি কোথা থেকে এল, আর nn−2 সূত্রটির নাম Cayley-র হলেও তিনিই কি প্রথম?

গণিতে “tree” শব্দটির প্রথম ব্যবহার সাধারণত Arthur Cayley-র ১৮৫৭ সালের একটি প্রবন্ধের সঙ্গে যুক্ত করা হয়। সেখানে প্রসঙ্গ graph নয়, differential operator: বারবার derivative নেওয়ার পদ সাজাতে গিয়ে তিনি শাখা-প্রশাখার আকৃতি পান আর rooted tree গুনতে শুরু করেন। তার আগেও এমন গঠন লুকিয়ে ছিল; বৈদ্যুতিক বর্তনীর সমীকরণ নিয়ে Gustav Kirchhoff-এর ১৮৪৭ সালের কাজে spanning tree-এর ধারণা কার্যত ব্যবহৃত হয়েছিল বলে উল্লেখ করা হয়।

Labelled tree-এর সংখ্যা nn−2, এই সূত্রটি Cayley ১৮৮৯ সালে “A theorem on trees” নামের ছোট প্রবন্ধে প্রকাশ করেন, তবে তিনি নিজেই আগের কাজের উল্লেখ করেছিলেন। Carl Wilhelm Borchardt ১৮৬০ সালের দিকে determinant ব্যবহার করে একই ফল পেয়েছিলেন বলে ইতিহাসে বলা হয়, আর কোনো কোনো সূত্রে Sylvester-এর নামও আসে। Cayley-র প্রবন্ধে পূর্ণ সাধারণ প্রমাণ ছিল না, ছোট ক্ষেত্রে যুক্তি দেখিয়ে সাধারণ দাবি করা হয়েছিল। এজন্য অনেক বই একে “Cayley-র সূত্র” বলেও Borchardt-এর নাম পাশে রাখে।

এই পর্বের bijection-প্রমাণটি এসেছে Heinz Prüfer-এর ১৯১৮ সালের প্রবন্ধ থেকে। তাঁর চিন্তাটি এত সরল যে এটি আজ প্রায় সব পাঠ্যবইয়ে প্রধান প্রমাণ: n − 2 দৈর্ঘ্যের একটি সারি, যার প্রতিটি ঘরে nটি বিকল্প। পরে আরও অনেক প্রমাণ এসেছে: André Joyal-এর ১৯৮১ সালের প্রমাণ “doubly rooted” tree আর function-এর মধ্যে bijection দেয়; Jim Pitman-এর double counting প্রমাণ forest গোনে দুইভাবে; আর Kirchhoff-এর matrix-tree উপপাদ্য Kn-এর একটি determinant থেকে সরাসরি nn−2 দেয় (পর্ব ৬৭)। একটি সূত্রের এতগুলো আলাদা প্রমাণ থাকা গণিতে একটি সাধারণ ঘটনা: প্রতিটি প্রমাণ নতুন দিক দেখায়, যেমন Prüfer-এর প্রমাণ থেকে degree-তালিকা ধরে গোনা আর এলোমেলো tree বানানো সহজ হয়ে যায়।

মূল উপলব্ধি: “Tree” এসেছে ১৮৫৭ সালে Cayley-র differential operator-এর কাজ থেকে; nn−2 সূত্রটি Borchardt আগে পেয়েছিলেন বলে জানা যায়, আর আজকের সবচেয়ে প্রচলিত প্রমাণটি Prüfer-এর ১৯১৮ সালের bijection.

প্র ০২উনিশ শতকে রসায়নবিদরা কেন tree গুনতে চেয়েছিলেন? Butane থেকে octane পর্যন্ত isomer-এর সংখ্যা কীভাবে tree গণনার প্রশ্ন?

একটি alkane-এর সংকেত CnH2n+2: nটি কার্বন পরমাণু একে অপরের সঙ্গে একক বন্ধনে যুক্ত, আর বাকি জায়গা হাইড্রোজেনে ভরা। কার্বনের যোজ্যতা 4, হাইড্রোজেনের 1. হিসাব করুন: কার্বনগুলোর মধ্যে যদি kটি বন্ধন থাকে, তবে হাইড্রোজেনের জন্য বাকি থাকে 4n − 2k জায়গা। হাইড্রোজেন 2n + 2টি হলে 4n − 2k = 2n + 2, অর্থাৎ k = n − 1. কার্বনগুলো connected, আর nটি vertex-এ n − 1টি edge, তাই উপপাদ্য ২.৩(e) অনুযায়ী কার্বন-কাঠামোটি একটি tree, যার প্রতিটি vertex-এর degree বড়জোর 4.

অতএব “CnH2n+2-এর কয়টি গঠনগত isomer (একই সংকেত, আলাদা গঠন)?” প্রশ্নটি হলো: n vertex-এর কয়টি আলাদা আকারের (unlabelled) tree আছে, যার সব degree ≤ 4? Scratch-এ সব labelled tree তৈরি করে আকার মিলিয়ে গুনলে n = 4, 5, 6, 7, 8-এ পাওয়া যায় 2, 3, 5, 9, 18; এগুলো butane, pentane, hexane, heptane আর octane-এর পরিচিত isomer-সংখ্যার সঙ্গে মেলে। n = 6-এ মোট unlabelled tree ৬টি, কিন্তু তারকা K1,5-এর কেন্দ্রের degree 5, যা কার্বনের পক্ষে অসম্ভব; তাই ৫টি।

Cayley ১৮৭০-এর দশকে এই প্রশ্নে tree গণনার পদ্ধতি প্রয়োগ করেন বলে উল্লেখ করা হয়, তবে তাঁর বড় n-এর কিছু সংখ্যা পরে ভুল প্রমাণিত হয়। কারণ আকার গোনা মানে symmetry বাদ দিয়ে গোনা, আর এক আকারের নামকরণের সংখ্যা আকারভেদে আলাদা। ১৯৩৭ সালে George Pólya-র বিখ্যাত প্রবন্ধে group আর generating function মিলিয়ে এমন গণনার একটি সাধারণ পদ্ধতি আসে, যার মূল ধারণা পর্ব ৫১-এ দেখেছেন; রাসায়নিক যৌগ গোনা ছিল তাঁর প্রধান প্রয়োগগুলোর একটি।

আজকের cheminformatics-এ অণুকে graph হিসেবে রাখা আর isomer তালিকা তৈরি নিয়মিত কাজ, যার ভিত্তি এই গণনা।

মূল উপলব্ধি: যোজ্যতার হিসাব আর “n vertex, n − 1 edge, connected ⇒ tree” মিলে alkane isomer গোনা হয়ে যায় degree ≤ 4-এর unlabelled tree গোনা; symmetry-র কারণে এটি labelled গণনার চেয়ে অনেক কঠিন, আর Pólya-র তত্ত্ব সেখান থেকেই বড় হয়েছে।

প্র ০৩⌈log2 n!⌉ নিম্নসীমা কি সবসময় অর্জন করা যায়? সবচেয়ে কম তুলনায় sort করার খোঁজ কতদূর এগিয়েছে?

Decision-tree যুক্তি বলে, n টি key সাজাতে worst case-এ অন্তত ⌈log2 n!⌉টি তুলনা লাগে। কিন্তু নিম্নসীমা মানেই যে এত কম তুলনায় সত্যিই সাজানো যায়, তা নয়। ব্লকের সারণিতে merge sort n = 5-এ 8টি তুলনা নেয়, অথচ নিম্নসীমা 7. তাহলে কি ৭টিতে সম্ভব? হ্যাঁ, ৫টি key ৭টি তুলনায় সাজানোর একটি চতুর পদ্ধতি জানা আছে।

১৯৫৯ সালে Lester Ford আর Selmer Johnson “merge insertion” নামে একটি পদ্ধতি প্রকাশ করেন, যা ছোট n-এ merge sort-এর চেয়ে কম তুলনা নেয়; মূল কৌশল হলো জোড়ায় জোড়ায় তুলনা করে বড়গুলো আগে সাজানো, তারপর ছোটগুলো এমন ক্রমে ঢোকানো যাতে binary search-এর প্রতিটি ধাপ পুরো কাজে লাগে (২-এর ঘাতের ঠিক নিচের আকারের তালিকায়)। অনেক দিন এটিকে সর্বোত্তম ভাবা হয়েছিল, পরে কিছু n-এ আরও ভালো পদ্ধতি পাওয়া যায় বলে উল্লেখ করা হয়।

আরেকটি বিস্ময়: নিম্নসীমা সবসময় অর্জনযোগ্য নয়। n = 12-এ ⌈log2 12!⌉ = 29, কিন্তু কম্পিউটারে বিস্তৃত অনুসন্ধান করে দেখানো হয়েছে যে 30টির কমে ১২টি key সাজানো যায় না; Donald Knuth-এর “The Art of Computer Programming”-এর sorting খণ্ডে এই ফল (Mark Wells, ১৯৬০-এর দশক) উল্লেখ আছে বলে জানা যায়। অর্থাৎ decision tree-তে n!টি leaf রাখার জায়গা থাকলেও, তুলনার কাঠামো সব leaf-কে নিখুঁতভাবে সাজাতে দেয় না। ঠিক কতগুলো তুলনা লাগে, সেই সংখ্যা মাত্র কিছু ছোট n-এর জন্য জানা।

বাস্তবের লাইব্রেরি সর্বনিম্ন তুলনার পেছনে ছোটে না; Python-এর built-in sort আগে থেকে সাজানো অংশ (run) চিনে বাস্তব ডেটায় দ্রুত চলে। তবু তাত্ত্বিক নিম্নসীমা একটি দিকনির্দেশক: কেউ যদি দাবি করেন তাঁর তুলনা-ভিত্তিক sort সব ইনপুটে রৈখিক সময়ে চলে, তবে আপনি প্রমাণ ছাড়াই জানেন দাবিটি ভুল। বিস্তারিত বাস্তব প্রয়োগ DSA কোর্সের পর্ব ২৫-এ।

মূল উপলব্ধি: log2 n! একটি নিম্নসীমা, সব n-এ অর্জনযোগ্য নয় (n = 12-এ 29 বনাম প্রকৃত 30); merge insertion ছোট n-এ কাছাকাছি যায়, আর বাস্তবের sort তুলনা-সংখ্যার চেয়ে ডেটার গঠন আর memory-কে বেশি গুরুত্ব দেয়।

প্র ০৪ডেটাবেস কেন binary tree নয়, শত শত সন্তানওয়ালা B-tree ব্যবহার করে? m-ary tree-এর height-সূত্র এখানে কী বলে?

উপপাদ্য ৫.১ আর ৫.২ বলে, l টি leaf-এর ভারসাম্যপূর্ণ m-ary tree-এর height প্রায় logm l. Binary tree-তে এক কোটি (107) key রাখলে height অন্তত ⌈log2 107⌉ = 24; অর্থাৎ একটি key খুঁজতে প্রায় ২৪টি vertex দেখতে হয়। Memory-তে এটি কিছুই নয়। কিন্তু ডেটা যদি disk-এ থাকে, প্রতিটি vertex পড়া মানে একটি আলাদা block পড়া, আর disk থেকে একটি block আনতে memory থেকে পড়ার চেয়ে বহু গুণ বেশি সময় লাগে।

সমাধান: প্রতিটি vertex-কে একটি পুরো disk block-এর সমান বড় করুন, আর তাতে শত শত key ও সন্তান-pointer রাখুন। m = 200 হলে log200 107 ≈ 3.04, তাই height ৪-এর মতো; আর ওপরের দুই level প্রায় সবসময় memory-তে থাকে, ফলে একটি খোঁজে disk পড়তে হয় মাত্র এক-দুইবার। এটাই B-tree-এর মূল ধারণা। Rudolf Bayer আর Edward McCreight ১৯৭০ সালের দিকে Boeing-এর গবেষণাগারে এটি তৈরি করেন, আর ১৯৭২ সালে প্রবন্ধ আকারে প্রকাশ করেন বলে উল্লেখ করা হয়। “B” অক্ষরের মানে কী, তা নিয়ে নানা মত প্রচলিত, লেখকেরা নিজেরাও নির্দিষ্ট করে বলেননি বলে গল্প আছে।

গাণিতিকভাবে B-tree একটি m-ary tree, যেখানে প্রতিটি internal vertex-এর সন্তান-সংখ্যা ⌈m/2⌉ থেকে m-এর মধ্যে রাখা হয় (root ছাড়া), আর সব leaf একই level-এ। এই নিয়ম height-কে log⌈m/2⌉ n-এর কাছাকাছি সীমায় বাঁধে, insert আর delete-এর পরেও। প্রায় সব relational database আর অনেক file system আজ B-tree বা তার রূপ B+ tree ব্যবহার করে বলে বর্ণনা পাওয়া যায়। বিস্তারিত অপারেশন DSA কোর্সের পর্ব ৩০-এ।

লক্ষ করুন, এখানে খরচের মাপ বদলে যাওয়ায় সর্বোত্তম m বদলে গেছে। তুলনার সংখ্যা গুনলে binary tree ভালো (প্রতিটি vertex-এ m-ary খোঁজ log2 m তুলনা নেয়, মোট প্রায় সমান), কিন্তু block-পড়া গুনলে বড় m অনেক ভালো। গণিত একই, প্রশ্নটা কোন খরচ গোনা হচ্ছে।

মূল উপলব্ধি: m-ary tree-এর height logm n, তাই disk-এর মতো ধীর মাধ্যমে বড় m height আর disk-পড়া কমায়; B-tree এই হিসাবের ওপর দাঁড়িয়ে, আর কোন m সেরা তা নির্ভর করে কোন খরচ গোনা হচ্ছে তার ওপর।

প্র ০৫দুটি graph একই আকারের কি না (isomorphism) বের করা সাধারণভাবে কঠিন প্রশ্ন, অথচ tree-তে তা রৈখিক সময়ে হয়। কেন?

দুটি graph isomorphic, যদি vertex-গুলোর নাম বদলে একটিকে অন্যটি বানানো যায় (পর্ব ৫৮)। সাধারণ graph-এর জন্য এই প্রশ্নের polynomial সময়ের algorithm জানা নেই, আবার NP-complete-ও প্রমাণিত নয়; এটি জটিলতা-তত্ত্বের এক বিরল “মাঝামাঝি” সমস্যা। ২০১৫ সালে László Babai একটি quasi-polynomial সময়ের algorithm ঘোষণা করেন, যা গবেষণা-জগতে বড় খবর ছিল; প্রমাণে একটি ভুল ধরা পড়ার পর ২০১৭ সালে তিনি সংশোধন ঘোষণা করেন বলে উল্লেখ করা হয়।

Tree-তে কাজটি সহজ, কারণ tree-এর একটি স্বাভাবিক “শুরুর জায়গা” আছে: উপপাদ্য ৭.১-এর কেন্দ্র, যা একটি vertex বা একটি edge. Isomorphism কেন্দ্রকে কেন্দ্রে পাঠাতে বাধ্য (দূরত্ব রক্ষা পায়), তাই কেন্দ্রকে root ধরে rooted tree তুলনা করলেই চলে (কেন্দ্র edge হলে দুই প্রান্তের যেকোনোটিকে root ধরে দুইবার)। Rooted tree-এর জন্য নিচ থেকে ওপরে একটি “স্বাক্ষর” গড়া যায়: leaf-এর স্বাক্ষর “()”, আর internal vertex-এর স্বাক্ষর তার সন্তানদের স্বাক্ষর বর্ণানুক্রমে সাজিয়ে বন্ধনীর ভেতরে জোড়া। দুটি rooted tree isomorphic ঠিক তখন, যখন root-এর স্বাক্ষর একই; প্রমাণ structural induction (পর্ব ১২)। এই পর্বের scratch-গণনায় unlabelled tree গোনার জন্য এই কৌশলই ব্যবহার করা হয়েছে।

Aho, Hopcroft আর Ullman ১৯৭৪ সালের তাঁদের বিখ্যাত algorithm বইয়ে এই ধারণার একটি রৈখিক-সময়ের রূপ দেন বলে উল্লেখ করা হয়; সেজন্য একে প্রায়ই “AHU algorithm” বলা হয়। কৌশলটি হলো level ধরে কাজ করা আর স্বাক্ষরকে লম্বা string না রেখে ছোট পূর্ণসংখ্যায় বদলে নেওয়া।

আকার গোনার প্রশ্নও একই কারণে কঠিন-সহজের মাঝে: unlabelled tree-এর সংখ্যা 1, 1, 1, 2, 3, 6, 11, 23, … দ্রুত বাড়ে, কিন্তু কোনো সরল বদ্ধ সূত্র নেই। Richard Otter ১৯৪৮ সালে দেখান যে এই সংখ্যা মোটামুটি C · αn · n−5/2 হারে বাড়ে, যেখানে α প্রায় 2.9558, বলে জানা যায়। Labelled সংখ্যা nn−2 এর চেয়ে অনেক দ্রুত বাড়ে।

মূল উপলব্ধি: সাধারণ graph-এ isomorphism-এর জটিলতা এখনো অমীমাংসিত, কিন্তু tree-এর কেন্দ্র একটি স্বাভাবিক root দেয়, আর নিচ থেকে গড়া স্বাক্ষর তুলনা রৈখিক সময়ে isomorphism ঠিক করে।

অনুশীলন

আগে খাতায় করুন, তারপর উত্তর খুলে মেলান।

  1. প্রমাণ: n ≥ 2 vertex-এর একটি tree-এর সর্বোচ্চ degree Δ হলে প্রমাণ করুন যে তাতে অন্তত Δটি leaf আছে। কোন tree-তে সমতা হয়?
    1. Δ ≤ 2 হলে উপপাদ্য ১.২ অনুযায়ী leaf ≥ 2 ≥ Δ. — সহজ ক্ষেত্র
    2. Δ ≥ 3 হলে degree Δ-এর একটি vertex u নিন। §১-এর সূত্রে l = 2 + ∑deg(v) ≥ 3(deg(v) − 2) ≥ 2 + (Δ − 2) = Δ, কারণ যোগফলের প্রতিটি পদ অ-ঋণাত্মক আর u-এর পদ Δ − 2. — handshake থেকে পাওয়া সূত্র
    3. সমতা হয় যখন u ছাড়া আর কোনো vertex-এর degree 3 বা বেশি নয়: u থেকে Δটি path বেরিয়েছে (একটি “মাকড়সা”)। তারকা আর path এর বিশেষ রূপ। ∎

    বিকল্প প্রমাণ: u সরালে Δটি component থাকে, প্রতিটি একটি tree; প্রতিটিতে u থেকে সবচেয়ে দূরের একটি vertex T-এর leaf. ৩০০টি এলোমেলো tree-তে scratch-এ দাবিটি মেলানো।

  2. গণনা: (ক) {1, …, 7}-এর ওপর কয়টি tree-তে vertex 1-এর degree ঠিক 3? (খ) {1, …, 6}-এর ওপর কয়টি tree-তে vertex 1 একটি leaf? (গ) {1, …, 5}-এর ওপর কয়টি tree একটি path, আর কয়টি তারকা?

    (ক) লেমা ৬.৩ অনুযায়ী Prüfer সারিতে (দৈর্ঘ্য 5) 1 ঠিক দুইবার: অবস্থান বাছাই C(5, 2) = 10, বাকি ৩টি ঘরে 1 বাদে ৬টি বিকল্প, 63 = 216. মোট 10 · 216 = 2160. (খ) সারিতে (দৈর্ঘ্য 4) 1 একবারও নেই: 54 = 625, মোট 1296-এর মধ্যে। (গ) Path: ৫টি vertex-এর একটি ক্রম, উল্টো ক্রম একই path, তাই 5!/2 = 60; তারকা: কেন্দ্র বাছাই, 5. চারটি উত্তরই scratch-এ সব labelled tree গুনে মেলানো।

  3. আউটপুট অনুমান: কোড না চালিয়ে বলুন কী ছাপা হবে। তারপর যেকোনো Python-এ চালিয়ে মেলান।
    adj = {v: set() for v in range(1, 7)}
    for a, b in [(1, 4), (2, 4), (4, 5), (3, 5), (5, 6)]:
        adj[a].add(b); adj[b].add(a)
    seq = []
    while len(adj) > 2:
        leaf = min(v for v in adj if len(adj[v]) == 1)
        nb = adj.pop(leaf).pop()
        adj[nb].discard(leaf); seq.append(nb)
    print(seq, sorted(adj))
    [4, 4, 5, 5] [5, 6]

    কোডটি Prüfer encoding. শুরুর leaf 1, 2, 3, 6. 1 মুছে 4 লেখা হয়; 2 মুছে 4; এখন 4-এর একমাত্র প্রতিবেশী 5, তাই 4 leaf, leaf-সেট {3, 4, 6}; 3 মুছে 5; তারপর 4 মুছে 5; বাকি 5 আর 6. লেমা ৬.৩ মেলে: 4 আর 5-এর degree 3, তাই দুইবার করে। CPython 3.14-এ চালিয়ে মেলানো।

  4. সংখ্যাগত সমস্যা: (ক) একটি নকআউট টুর্নামেন্টে ২৭টি দল; বাই (খেলা ছাড়া পরের রাউন্ডে ওঠা) দেওয়া যায়। কয়টি ম্যাচ হবে, আর বিজয়ী দলকে সবচেয়ে কম কয়টি রাউন্ড খেলতে হবে, সবচেয়ে খারাপ ক্ষেত্রে? (খ) একটি full ternary tree-এর height 4. সর্বোচ্চ কয়টি leaf, আর তখন কয়টি internal vertex?

    (ক) টুর্নামেন্টটি ২৭টি leaf-এর একটি full binary tree, প্রতিটি internal vertex একটি ম্যাচ: i = (27 − 1)/(2 − 1) = 26টি ম্যাচ। Height h হলে leaf ≤ 2h, তাই h ≥ ⌈log2 27⌉ = 5: অন্তত একটি দলকে ৫টি ম্যাচ খেলতে হয়, আর ভারসাম্যপূর্ণ সূচিতে (৫টি বাই দিয়ে প্রথম রাউন্ডে ১১টি ম্যাচ) ৫-ই যথেষ্ট। (খ) উপপাদ্য ৫.১: l ≤ 34 = 81, সমতা হয় সব leaf level 4-এ থাকলে; তখন i = (81 − 1)/2 = 40, যা level 0 থেকে 3-এর মোট vertex-সংখ্যা 1 + 3 + 9 + 27-এর সমান। সব সংখ্যা scratch-এ মেলানো।

নিজেকে যাচাই করুন

ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।

ক্রমPrüfer সারি (a1, …, an−2) থেকে labelled tree ফিরিয়ে আনার ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. প্রতিটি vertex-এর degree ধরুন সারিতে তার উপস্থিতি + 1
  2. সারির পরের সংখ্যা a পড়ুন
  3. Degree 1 এমন সবচেয়ে ছোট vertex b বেছে edge b–a যোগ করুন
  4. b বাদ দিন আর a-এর degree এক কমান; সারি শেষ না হলে আবার পড়ুন
  5. সারি শেষ হলে বাকি দুটি vertex একটি edge দিয়ে জুড়ুন

২৫টি vertex আর ৩টি component-এর একটি forest-এ কয়টি edge আছে?

  • 24
  • 22
  • 23
  • vertex-এর degree না জেনে বলা যায় না

একটি full ternary (3-ary) tree-তে ১৩টি internal vertex. তাতে কয়টি leaf?

  • 27
  • 40
  • 39
  • 26

{1, 2, 3, 4, 5} vertex-সেটের ওপর কয়টি আলাদা tree (labelled tree) আছে?

  • 3
  • 120
  • 60
  • 125

৫টি vertex-এর একটি labelled tree-এর Prüfer code (2, 2, 2). Tree-টি কেমন?

  • একটি path, যার মাঝে 2
  • একটি tree, যেখানে 2 একটি leaf
  • একটি তারকা, যার কেন্দ্র 2
  • এমন কোনো tree নেই

শুধু তুলনা ব্যবহার করে ৫টি আলাদা সংখ্যা সাজাতে যেকোনো algorithm-কে সবচেয়ে খারাপ ক্ষেত্রে অন্তত কয়টি তুলনা করতে হয়, decision-tree যুক্তি অনুযায়ী?

  • 4
  • 7
  • 10
  • 120

৬টি vertex-এর path 1–2–3–4–5–6-এর কেন্দ্র (সবচেয়ে কম eccentricity-ওয়ালা vertex) কী?

  • vertex 3 আর 4, একটি edge-এর দুই প্রান্ত
  • শুধু vertex 3
  • vertex 1 আর 6
  • সব vertex, কারণ path সমমিত

শব্দকোষ

লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন, অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:

মনে রাখুন

  1. ছয় রূপ: tree ⇔ অনন্য path ⇔ minimally connected ⇔ maximally acyclic ⇔ connected + n − 1 edge ⇔ acyclic + n − 1 edge; forest-এ m = n − c.
  2. Leaf: n ≥ 2-এ অন্তত দুটি; ঠিক হিসাবে l = 2 + ∑deg ≥ 3(deg − 2).
  3. Full m-ary: n = mi + 1, l = (m − 1)i + 1; height h-এ l ≤ mh, তাই h ≥ ⌈logm l⌉, balanced full tree-এ সমান।
  4. Decision tree: comparison sort-এ n! leaf, তাই worst case ≥ ⌈log2 n!⌉ = Ω(n log n).
  5. Cayley আর Prüfer: labelled tree nn−2টি; v সারিতে deg(v) − 1 বার, leaf সারিতে নেই; কেন্দ্র একটি vertex বা একটি edge.

পরের পর্বে: পর্ব ৬৬ “Tree traversal আর expression tree — prefix, infix, postfix”: ক্রমযুক্ত tree-এর vertex একটি একটি করে পরিদর্শনের preorder, inorder আর postorder নিয়ম, আর গাণিতিক রাশিকে tree আকারে লিখে stack দিয়ে মান বের করার কৌশল।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

এই পর্বটি শেয়ার করুন:
এই পর্বে ভুল পেয়েছেন? জানান