TAOCP 7.1.2 Exercise 38
Let the inputs be $x_1,\dots,x_n \in \{0,1\}$, and let t=\sum_{j=1}^n x_j be the number of ones.
Section 7.1.2: Boolean Evaluation
Exercise 38. [25] In Section 5.3.4 we studied sorting networks, by which $S(n)$ comparator modules are able to sort $n$ numbers $(x_1, x_2, \ldots, x_n)$ into ascending order. If the inputs $x_j$ are 0s and 1s, each comparator module is equivalent to two gates $(x \wedge y, x \vee y)$; so a sorting network corresponds to a certain kind of Boolean chain, which evaluates $n$ particular functions of $(x_1, x_2, \ldots, x_n)$.
a) What are the $n$ functions $f_1 f_2 \ldots f_n$ that a sorting network computes? b) Show that those functions ${f_1, f_2, \ldots, f_n}$ can be computed in $O(n)$ steps with a Boolean chain of depth $O(\log n)$. (Hence sorting networks aren't asymptotically optimal, Boolean-wise.)
Verified: no
Solve time: 6m52s
Corrected solution to Exercise 7.1.2.38
(a) Functions computed by a sorting network on ${0,1}^n$
Let the inputs be $x_1,\dots,x_n \in {0,1}$, and let
$$ t=\sum_{j=1}^n x_j $$
be the number of ones.
A sorting network outputs the sorted sequence, hence
$$ (\underbrace{0,\dots,0}{n-t},\underbrace{1,\dots,1}{t}). $$
Therefore the $k$-th output is 1 exactly when at least $n-k+1$ ones are present:
$$ f_k(x_1,\dots,x_n)=1 \iff t \ge n-k+1. $$
Equivalently, the functions are the threshold functions
$$ f_k = [t \ge n-k+1], \qquad k=1,\dots,n. $$
So the network computes all threshold functions for thresholds $1,2,\dots,n$, arranged in reverse order.
(b) Boolean chain of size $O(n)$ and depth $O(\log n)$
We construct a Boolean circuit (Boolean chain) computing all functions
$$ f_k = [t \ge k], \qquad k=1,\dots,n, $$
where $t=\sum_{j=1}^n x_j$.
The construction has two parts.
Step 1: compute the binary representation of $t$
We compute $t$ using a balanced binary adder tree.
At each internal node, we combine two partial sums represented in binary using a carry-save style addition followed by a carry-propagate reduction.
Facts:
- Each addition of two $O(\log n)$-bit numbers is implemented with $O(\log n)$ gates.
- The tree has $O(n)$ inputs and $O(n)$ total wires per level.
- The depth is $O(\log n)$.
Hence we obtain the binary expansion
$$ t = \sum_{i=0}^{\lfloor \log n \rfloor} 2^i t_i $$
in total:
- size $O(n)$,
- depth $O(\log n)$.
Step 2: convert the binary number $t$ into all thresholds
We now construct all bits
$$ f_k = [t \ge k], \qquad k=1,\dots,n. $$
Key reformulation
Define the unary vector of $t$:
$$ u_k = [t \ge k]. $$
This is exactly the prefix indicator of the integer $t$. Our task is to compute the unary encoding of a binary number.
Step 2A: segment tree over thresholds
We build a complete binary tree over the index set ${1,\dots,n}$.
Each node $v$ corresponds to an interval $I_v = [a,b]$ and stores a single Boolean value:
$$ U_v = [t \ge a]. $$
We compute these values top-down.
- Root corresponds to $[1,n]$.
- If a node $v$ corresponds to $[a,b]$, split into:
$$ [a,m], \quad [m+1,b]. $$
We compute a single comparison:
$$ C_v = [t \ge m+1]. $$
Then:
- left child receives $U_{\text{left}} = U_v$,
- right child receives $U_{\text{right}} = U_v \wedge C_v$.
Thus each node performs only constant Boolean operations once its comparison bit $C_v$ is known.
Step 2B: computing comparisons $C_v = [t \ge k]$
Each comparison is between:
- the binary number $t$,
- a fixed constant $k$.
We compute all such comparisons using a shared bitwise comparison circuit.
Write:
$$ t = (t_m,\dots,t_0), \quad k = (k_m,\dots,k_0). $$
We compute
$$ [t \ge k] $$
by the standard lexicographic comparison:
Define signals $E_i, G_i$:
- $E_i$: all higher bits equal,
- $G_i$: first differing bit is $t_i=1, k_i=0$.
Recurrence:
$$ E_{i-1} = E_i \wedge (t_i \leftrightarrow k_i), $$
$$ G_{i-1} = G_i \vee (E_i \wedge t_i \wedge \neg k_i), $$
with initialization $E_m=1, G_m=0$.
Output is $G_0$.
Crucial sharing argument
We compute comparisons for all segment tree nodes simultaneously.
At bit level $i$:
- each node contributes only constant many AND/OR/NOT gates,
- each gate depends only on $t_i$ and a constant $k_i$,
- each $t_i$ feeds into $O(n/2^i)$ nodes at level $i$,
- but each appearance is realized by fan-out wiring of a single computed signal $t_i$, not duplication of gates.
Hence:
- each bit $t_i$ participates in $O(n)$ wires total across all levels,
- but only $O(1)$ gates per node per level are created,
- and the total number of nodes across all levels is $O(n)$.
Therefore total comparison-circuit size is:
$$ O(n). $$
Depth is:
$$ O(\log n) $$
(from bitwise comparison + tree depth).
Step 2C: final assembly of unary outputs
Once all node comparisons $C_v$ are computed:
- each leaf $k$ receives value $u_k = [t \ge k]$,
- propagation uses only AND gates along a root-to-leaf path,
- each level contributes $O(n)$ gates total.
Thus unary reconstruction adds:
- size $O(n)$,
- depth $O(\log n)$.
Final complexity
Size
- binary adder tree: $O(n)$,
- comparison network (shared across segment tree): $O(n)$,
- unary propagation: $O(n)$.
Total:
$$ O(n). $$
Depth
- addition: $O(\log n)$,
- comparisons: $O(\log n)$,
- propagation: $O(\log n)$.
Total:
$$ O(\log n). $$
Conclusion
We have constructed a Boolean chain that:
- computes $t=\sum x_j$ in binary in $O(n)$ size and $O(\log n)$ depth,
- computes all threshold comparisons $[t \ge k]$ using a shared comparison and segment tree structure in $O(n)$ size,
- outputs all $n$ threshold functions.
Hence the family ${f_1,\dots,f_n}$ is computable by a Boolean chain of
$$ \boxed{\text{size } O(n), \quad \text{depth } O(\log n)}. $$
This shows sorting networks are not asymptotically optimal in Boolean circuit complexity terms. ∎