Tag: for statement

  • Nested for Loops: Thinking in Rows and Columns

    Nested for Loops: Thinking in Rows and Columns

    Some problems are naturally two-dimensional: printing grids, building tables, scanning pairs \((i,j)\), summing over regions like \(1 \le j \le i \le N\). A nested loop is just a loop inside another loop—perfect for “for each row, for each column” patterns. The key mental model:

    1. The outer loop picks a row (runs slowly).
    2. For each row, the inner loop runs to completion (runs fast).
    3. After the inner loop finishes, control returns to the outer loop for the next row.

    Once you see nested loops as row × column, the control flow becomes predictable.

    Example 1 — A “visual” triangle of symbols

    Goal: print a left-aligned triangle of * with height N.

    *
    **
    ***
    ****
    
    #include <iostream>
    
    int main() {
        int N;
        std::cout << "Triangle height N: ";
        std::cin >> N;
    
        for (int row = 1; row <= N; row = row + 1) {
            // Print 'row' stars on this line
            for (int k = 1; k <= row; k = k + 1) {
                std::cout << '*';
            }
            std::cout << '\n'; // move to the next line
        }
    
        return 0;
    }

    How it runs:

    • row = 1: inner loop prints 1 star → newline
    • row = 2: inner loop prints 2 stars → newline
      … up to row = N.

    Variant: right-aligned triangle (spaces + stars)

       *
      **
     ***
    ****
    
    #include <iostream>
    
    int main() {
        int N;
        std::cout << "Triangle height N: ";
        std::cin >> N;
    
        for (int row = 1; row <= N; row = row + 1) {
            // First, N-row spaces
            for (int s = 1; s <= N - row; s = s + 1) {
                std::cout << ' ';
            }
            // Then, row stars
            for (int k = 1; k <= row; k = k + 1) {
                std::cout << '*';
            }
            std::cout << '\n';
        }
    
        return 0;
    }

    Two inner loops make the “shape” obvious: spaces, then stars.

    Example 2 — Multiplication table (1…N) as a grid

    We build an \(N\times N\) grid where the entry in row \(i\), column \(j\) is \(i \times j\).

    #include <iostream>
    
    int main() {
        int N;
        std::cout << "Table size N: ";
        std::cin >> N;
    
        // Optional header row
        std::cout << "   ";                // small left margin
        for (int j = 1; j <= N; j = j + 1) {
            std::cout << j << '\t';
        }
        std::cout << '\n';
    
        for (int i = 1; i <= N; i = i + 1) {
            std::cout << i << " | ";       // row label
            for (int j = 1; j <= N; j = j + 1) {
                std::cout << (i * j) << '\t';
            }
            std::cout << '\n';
        }
    
        return 0;
    }

    We use '\t' (a tab) for simple column separation—no extra libraries needed.

    Example 3 — Double sums: full square vs. triangle

    Sum over all pairs \((i,j)\) in a square \(1\le i,j\le N\)

    #include <iostream>
    
    int main() {
        int N;
        std::cout << "N for double sum: ";
        std::cin >> N;
    
        long long S = 0; // use a wider type for safety
        for (int i = 1; i <= N; i = i + 1) {
            for (int j = 1; j <= N; j = j + 1) {
                S = S + (i * j);
            }
        }
    
        std::cout << "Sum_{i=1..N} Sum_{j=1..N} (i*j) = " << S << '\n';
        return 0;
    }

    Sum over a triangle \(1\le j\le i\le N\) (avoid double counting)

    #include <iostream>
    
    int main() {
        int N;
        std::cout << "N for triangular sum: ";
        std::cin >> N;
    
        long long T = 0;
        for (int i = 1; i <= N; i = i + 1) {
            for (int j = 1; j <= i; j = j + 1) { // note j <= i
                T = T + (i + j);
            }
        }
    
        std::cout << "Sum over 1<=j<=i<=N of (i+j) = " << T << '\n';
        return 0;
    }

    By changing the inner loop’s check from j <= N to j <= i, we restrict to the lower triangle.

    Example 4 — Counting lattice points in a disk

    Count integer pairs \((x,y)\) with \(-R \le x,y \le R\) that satisfy \(x^2 + y^2 \le R^2\).

    #include <iostream>
    
    int main() {
        int R;
        std::cout << "Radius R: ";
        std::cin >> R;
    
        int count = 0;
        for (int x = -R; x <= R; x = x + 1) {
            for (int y = -R; y <= R; y = y + 1) {
                if (x*x + y*y <= R*R) {
                    count = count + 1;
                }
            }
        }
    
        std::cout << "Lattice points with x^2 + y^2 <= R^2: " << count << '\n';
        return 0;
    }

    Mathematically, this explores a 2D region using a rectangular scan and an if to test membership.

    Reading the flow (what runs when?)

    For the classic form

    for (int i = 1; i <= A; i = i + 1) {
        for (int j = 1; j <= B; j = j + 1) {
            // body
        }
    }

    the order is:

    1. i = 1, then j runs 1..B completely
    2. i = 2, then j runs 1..B completely
      … up to i = A.

    The inner loop resets each time the outer loop advances.

    Common pitfalls (and fixes)

    • Using the same variable name for both loops.
      • Fix: use distinct names (row/col, i/j).
    • Forgetting the newline after finishing a row in “visual” problems.
      • Fix: print '\n' once per row, after the inner loop completes.
    • Off-by-one in triangular regions (j < i vs j <= i).
      • Fix: write down a tiny case (e.g., N = 3) and list the pairs to confirm.
    • Performance surprises: nested loops over N go like (N^2).
      • Fix: start with small N to test, then scale up.

    Key takeaways

    • A nested loop is a natural way to model row × column or pairwise work.
    • The inner loop runs to completion for each outer iteration.
    • Shapes (rectangle vs. triangle) are controlled by the inner loop’s bounds and simple if tests.
    • Visual tasks (patterns) and mathematical tasks (tables, sums, regions) both become straightforward when you think in terms of rows and columns.
  • Range-Based for Loops: “Do This for Each Value”

    Range-Based for Loops: “Do This for Each Value”

    Sometimes you don’t want to count; you want to act on each element of a known set. The range-based for loop expresses exactly that:

    for (/* element */ : /* range */) {
        // do something with element
    }

    It reads naturally: “for each element in this range.” This is great for finite sums/products, evaluating a function at a handful of sample points, or scanning a short list of special cases.

    We’ll later use range-based loops with arrays and standard containers (vectors, maps, …). For now, our examples use only initializer lists like {1, 3, 5} so we don’t rely on topics we haven’t covered yet.

    Syntax in one glance

    for (int k : {1, 3, 5, 7, 9}) {
        // k takes values 1, 3, 5, 7, 9 (in that order)
    }
    • The part before the colon declares a new variable that receives each element in turn.
    • The part after the colon is any range expression. Here it’s an initializer list { ... }.
    • Use braces {} for the loop body—even for one line—for clarity.

    Example 1 — A finite sum over a selected set

    Compute \(\sum_{k \in {1,3,5,7,9}} k^2\):

    #include <iostream>
    
    int main() {
        int sum = 0;
    
        for (int k : {1, 3, 5, 7, 9}) {
            sum = sum + k * k;
        }
    
        std::cout << "Sum of odd squares = " << sum << '\n';
        return 0;
    }

    This pattern matches math notation directly: pick exactly the indices you care about and add them up.

    Example 2 — Horner’s method via coefficients

    Evaluate \(P(x) = 2x^3 – 3x^2 + 0x + 1\) using Horner’s method (coefficients from highest to lowest):

    #include <iostream>
    
    int main() {
        double x;
        std::cout << "Evaluate P(x)=2x^3-3x^2+0x+1 at x = ";
        std::cin >> x;
    
        double y = 0.0;
        for (double c : {2.0, -3.0, 0.0, 1.0}) {
            y = y * x + c;   // Horner step
        }
    
        std::cout << "P(" << x << ") = " << y << '\n';
        return 0;
    }

    Here the range is the finite list of coefficients.

    Example 3 — Early exit with break: first small divisor test

    Scan a small set of primes and stop at the first hit:

    #include <iostream>
    
    int main() {
        int n;
        std::cout << "Enter n (>= 2): ";
        std::cin >> n;
    
        int first_divisor = -1;
    
        for (int d : {2, 3, 5, 7, 11, 13, 17, 19}) {
            if (n % d == 0) {
                first_divisor = d;
                break;               // we’re done scanning the set
            }
        }
    
        if (first_divisor == -1) {
            std::cout << "No small prime divisor found\n";
        } else {
            std::cout << "First small prime divisor: " << first_divisor << '\n';
        }
        return 0;
    }

    break exits the loop immediately (ties back to the previous post).

    Example 4 — Skip cases with continue: sum only positives

    #include <iostream>
    
    int main() {
        int sum = 0;
    
        for (int v : {3, -1, 4, 0, -2, 5}) {
            if (v <= 0) {
                continue;            // ignore non-positive values
            }
            sum = sum + v;
        }
    
        std::cout << "Sum of positives = " << sum << '\n';
        return 0;
    }

    This matches a common mathematical “filter”: include only values that satisfy a property.

    Example 5 — Sampling a function at chosen points

    Evaluate \(f(x)=x^2\) at a few handpicked points:

    #include <iostream>
    
    int main() {
        double total = 0.0;
    
        for (double x : {0.0, 0.25, 0.5, 0.75, 1.0}) {
            total = total + x * x;
        }
    
        std::cout << "Sum of samples of x^2 = " << total << '\n';
        return 0;
    }

    Range-based loops shine whenever you want to name the points explicitly.

    Notes and gotchas (for now)

    • The loop variable receives a copy of each element here; that’s fine for numbers.
    • With initializer lists, elements are not modifiable (there’s nothing to mutate). We’ll see how to iterate by reference once we introduce arrays and containers.
    • Range-based for doesn’t give you an index automatically. If you need one, keep a counter:
    int i = 0;
    for (int k : {2, 4, 6, 8}) {
        // use i as the position (0,1,2,3)
        i = i + 1;
    }
    • If you simply need to repeat a fixed small number of times, a classic for is clearer. A range like {0,0,0,0,0} also works—but it’s a teaching trick, not a habit.

    Mini-reference

    • Form: for (Type name : {v1, v2, v3}) { ... }
    • Works today with initializer lists; later with arrays and containers.
    • break ends the loop; continue skips to the next element.

    What’s next

    We’ll revisit range-based loops when we cover arrays and standard containers—that’s where they really shine. For now, keep them in mind whenever your problem is naturally “for each chosen value, do X.”

  • Early Exits and Skips: Introducing break and continue

    Early Exits and Skips: Introducing break and continue

    Loops repeat work, but sometimes you can decide earlier than your loop’s end whether more work is needed. Two simple tools help:

    • breakstop the loop immediately (exit the nearest loop).
    • continueskip the rest of this iteration and move on to the next round.

    These are especially handy in mathematical code where a condition tells you you’re done (first divisor found, threshold reached) or where some cases are irrelevant (skip evens, skip multiples of 3).

    We’ll use only concepts we’ve covered: variables, arithmetic, comparisons, if, classic for, and basic console I/O.

    break: exit as soon as you know the answer

    Example 1 — Primality test (stop at the first divisor)

    #include <iostream>
    
    int main() {
        int n;
        std::cout << "Enter n (>= 2): ";
        std::cin >> n;
    
        bool is_prime = true;
    
        // Try divisors up to sqrt(n). Exit early if one divides n.
        for (int d = 2; d * d <= n; d = d + 1) {
            if (n % d == 0) {
                is_prime = false;
                break;                // Found a divisor -> no need to test further
            }
        }
    
        if (is_prime) {
            std::cout << n << " is prime\n";
        } else {
            std::cout << n << " is composite\n";
        }
        return 0;
    }

    Why break helps: once a divisor is found, more tests can’t change the answer. Early exit saves time.

    Example 2 — Smallest k with \(1 + 2 + \dots + k \ge T\)

    We accumulate triangular numbers until we reach a threshold T, then stop.

    #include <iostream>
    
    int main() {
        int T;
        std::cout << "Threshold T: ";
        std::cin >> T;
    
        int sum = 0;
        int k = 0;
    
        for (int i = 1; ; i = i + 1) {  // we'll exit using break
            sum = sum + i;
            k   = i;
    
            if (sum >= T) {
                break;                  // first k where the sum reaches/exceeds T
            }
        }
    
        std::cout << "Smallest k with 1+...+k >= " << T << " is " << k << '\n';
        return 0;
    }

    Here we used a for with an empty condition on purpose. The loop is controlled by the break once the mathematical condition is met.

    continue: skip uninteresting cases, keep looping

    Example 3 — Sum of odd squares up to (N)

    We only want odd indices. continue skips the even ones.

    #include <iostream>
    
    int main() {
        int N;
        std::cout << "Sum of odd squares up to N. Enter N: ";
        std::cin >> N;
    
        int sum = 0;
    
        for (int i = 1; i <= N; i = i + 1) {
            if (i % 2 == 0) {
                continue;            // skip even i
            }
            sum = sum + i * i;       // only odd i reach here
        }
    
        std::cout << "Sum = " << sum << '\n';
        return 0;
    }

    Example 4 — Partial harmonic sum skipping multiples of 3

    \[
    S_N = \sum_{\substack{k=1 \ k \not\equiv 0 \pmod{3}}}^{N} \frac{1}{k}
    \]

    #include <iostream>
    
    int main() {
        int N;
        std::cout << "Harmonic sum excluding multiples of 3. Enter N: ";
        std::cin >> N;
    
        double sum = 0.0;
    
        for (int k = 1; k <= N; k = k + 1) {
            if (k % 3 == 0) {
                continue;            // skip k divisible by 3
            }
            sum = sum + 1.0 / k;
        }
    
        std::cout << "Sum = " << sum << '\n';
        return 0;
    }

    continue keeps the loop structure simple: the “normal work” lives after the guard.

    How control flows (for the classic for loop)

    • On continue: the program jumps to the step, then checks the condition for the next round.
    • On break: the program leaves the loop immediately and resumes after the closing brace.
    • Both only affect the nearest enclosing loop.

    When to use which

    • Use break when you’re searching for the first match or stopping by a threshold (first divisor, first time a sum exceeds (T), first power exceeding a limit).
    • Use continue when many iterations are “noise” and you want to filter them out early (skip evens, skip non-multiples, skip invalid inputs).

    Common pitfalls

    • Code after a break inside the same block won’t run—make sure any needed updates happen before break.
    • With continue, remember the step still runs (e.g., i = i + 1) before the next check.
    • Avoid stacking many unrelated continue checks; prefer a single clear guard if possible.

    Key takeaways

    • break exits the loop immediately; continue skips the rest of the current iteration.
    • They simplify math-driven loops: stop when sufficient, skip when irrelevant.
    • Keep conditions near the top of the loop so the “main work” stays uncluttered and readable.

    Next, we’ll move on to range-based for loops and then to nested loops—both build on this foundation.

  • Beyond “+1 Until N”: Non-Standard Steps and Checks in for Loops

    Beyond “+1 Until N”: Non-Standard Steps and Checks in for Loops

    Once the basic for pattern clicks (i = 0; i < N; i = i + 1), the next hurdle is realizing you can tune both the step and the check to match the problem. Many tasks aren’t “count by ones”; they skip elements, grow geometrically, or stop when a mathematical condition becomes false.

    This post shows practical variations of the step and check parts—still the classic for, just tailored to the job.

    Quick reminder: the three parts

    for (initialization; condition; step) {
        // body
    }
    • initialization runs once,
    • condition is checked before each round,
    • step runs after each round.

    Change the condition to control when the loop stops, and the step to control how the counter moves.

    1) Skipping by more than 1 (arithmetic steps)

    Use-case: odd/even terms, grid sampling, polynomial evaluation at regular gaps.

    #include <iostream>
    
    int main() {
        int N = 10;
    
        // Print odd numbers up to N (1, 3, 5, ...).
        for (int i = 1; i <= N; i = i + 2) {
            std::cout << i << ' ';
        }
        std::cout << '\n';
    
        return 0;
    }
    • Step i = i + 2 skips every other value.
    • The condition i <= N is inclusive; choose < vs <= carefully (off-by-one is the common bug).

    2) Geometric growth (multiplicative steps)

    Use-case: doubling an interval, estimating how many times you must double to exceed a threshold (log₂-type reasoning).

    #include <iostream>
    
    int main() {
        int limit = 200;
        for (int x = 1; x <= limit; x = x * 2) {
            std::cout << x << ' ';   // 1 2 4 8 16 32 64 128
        }
        std::cout << '\n';
        return 0;
    }
    • The step multiplies instead of adding.
    • The check x <= limit keeps the sequence bounded.

    3) Non-linear checks (stop by a property, not by a count)

    Use-case: iterate divisors only up to √n.

    #include <iostream>
    
    int main() {
        int n = 60;
    
        // Test candidates only while i*i <= n
        for (int i = 1; i * i <= n; i = i + 1) {
            if (n % i == 0) {
                std::cout << i << " and " << (n / i) << " are divisors\n";
            }
        }
        return 0;
    }
    • The condition i * i <= n is mathematically driven.
    • This avoids unnecessary iterations beyond √n.

    Note, I have introduced the % operator here. This is the modulo operator that evaluates to the remainder after integer division. I will talk more about operators in a future post.

    4) Two counters that move together

    Use-case: symmetric formulas, pairing terms (i with N−i), walking from both ends toward the middle.

    #include <iostream>
    
    int main() {
        int N = 6;
    
        // i goes up, j goes down
        for (int i = 0, j = N; i <= j; i = i + 1, j = j - 1) {
            std::cout << "pair: " << i << " & " << j << '\n';
        }
        return 0;
    }
    • The for header allows comma-separated expressions in initialization and step.
    • Use this to keep related counters in one place. (Keep it readable—don’t pack unrelated work here.)

    5) Floating-point steps (numerical loops)

    Use-case: Riemann sums and simple time stepping.

    #include <iostream>
    
    int main() {
        double a = 0.0, b = 1.0, h = 0.2;
    
        // Sample points a, a+h, a+2h, ... while t <= b (inclusive bound)
        for (double t = a; t <= b + 1e-12; t = t + h) {
            std::cout << "t = " << t << '\n';
        }
        return 0;
    }
    • Floating comparisons can be sensitive to rounding; a tiny tolerance (+ 1e-12) helps when you expect to land exactly on b.
    • Pick <= vs < consciously depending on whether you want to include the endpoint.

    6) Stopping by accuracy, not by count (adaptive end condition)

    Use-case: Taylor series until the next term is small.

    #include <iostream>
    
    int main() {
        double x = 1.0;        // evaluate e^x at x = 1
        double term = 1.0;     // current term in the series
        double sum  = 1.0;     // starts with 1
        double eps  = 1e-6;
    
        // k counts the term index (1,2,3,...)
        for (int k = 1; term > eps; k = k + 1) {
            term = term * (x / k); // next term: x^k / k!
            sum  = sum + term;
        }
    
        std::cout << "Approx e^1 = " << sum << '\n';
        return 0;
    }
    
    • The check depends on the size of the next contribution (term > eps), not a fixed N.
    • This is common in numerical methods where you stop when “good enough.”

    7) Counting down (custom check + step)

    Use-case: finite difference stencils, reverse traversal, countdown timers.

    #include <iostream>
    
    int main() {
        for (int k = 10; k >= 0; k = k - 2) {  // 10, 8, 6, ..., 0
            std::cout << k << ' ';
        }
        std::cout << '\n';
        return 0;
    }
    
    • Match initialization, condition, and step so the loop makes progress toward stopping.

    Practical guidance

    • Make progress obvious. When you choose the check, choose a step that will eventually make it false.
    • Prefer clarity over cleverness. Non-standard headers are powerful, but don’t hide unrelated side effects in the step.
    • Inclusive vs. exclusive bounds is still the top source of bugs—double-check < vs <=.
    • Floating-point checks may need a tolerance.
    • Multiple counters are fine when they belong together (e.g., i up while j down).

    Key takeaways

    • You can (and should) adapt the step and check to the math of your problem.
    • Steps can add, subtract, multiply, or update multiple counters; checks can be property-based (e.g., i*i <= n) or accuracy-based (e.g., term > eps).
    • Clear, problem-aligned headers reduce code in the body and cut errors.

    In the next posts, we’ll explore break and continue, range-based loops, and nested loops—tools that build on these fundamentals.

  • The for Loop in C++: Repeating Work a Fixed Number of Times

    The for Loop in C++: Repeating Work a Fixed Number of Times

    Sooner or later you need to do the same action many times—check several candidates, accumulate a sum over steps, run a procedure for a fixed number of iterations. Writing the same line ten times won’t scale. A loop solves this by repeating a block of code automatically.

    The classic C++ for loop is the simplest way to say “do this block exactly N times.” It uses a counter (an integer that changes each round) and a condition that decides whether another round should happen. Understanding this control flow is often the main hurdle for new students; once the mental model clicks, loops become routine.

    Anatomy of a for loop

    for (initialization; condition; step) {
        // body: the work to repeat
    }
    • initialization — runs once, before the loop starts (commonly sets a counter).
    • condition — checked before each round; if true, the body runs; if false, the loop ends.
    • step — runs after each round (commonly updates the counter).

    Execution order:

    1. initialization → 2) check condition → 3) body → 4) step → back to (2)

    Always use braces {} for the body, even for a single statement.

    First example: count from 1 to 5

    #include <iostream>
    
    int main() {
        for (int i = 1; i <= 5; i = i + 1) {
            std::cout << i << '\n';
        }
        return 0;
    }
    • Starts with i = 1.
    • Checks i <= 5. If true, prints i, then updates i = i + 1.
    • Repeats until the condition becomes false.
    • The body runs five times, printing 1, 2, 3, 4, 5 on separate lines.

    i = i + 1 is the most explicit way to increment. You’ll often see the shorthand ++i; we’ll return to that later.

    Off-by-one: inclusive vs. exclusive bounds

    Two common patterns:

    Inclusive end (1 through N)

    for (int i = 1; i <= N; i = i + 1) {
        // i takes values: 1, 2, ..., N
    }

    Exclusive end (0 up to N, but not N)

    for (int i = 0; i < N; i = i + 1) {
        // i takes values: 0, 1, ..., N-1
    }

    Pick the one that matches your problem. Many formulas and data structures use the exclusive pattern because “count N things” naturally maps to 0...N-1.

    Reading the count from the user

    #include <iostream>
    
    int main() {
        int N;
        std::cout << "How many lines? ";
        std::cin >> N;
    
        for (int i = 0; i < N; i = i + 1) {
            std::cout << "Line " << (i + 1) << '\n';
        }
        return 0;
    }

    If N is 0 or negative, the condition i < N is false immediately and the loop body doesn’t run at all. Zero iterations are perfectly valid.

    Counting down

    You can run the counter backwards. Just make the initialization, condition, and step match:

    #include <iostream>
    
    int main() {
        for (int i = 5; i >= 1; i = i - 1) {
            std::cout << i << '\n';
        }
        std::cout << "Lift-off!\n";
        return 0;
    }

    Accumulating a result

    Loops often build up a value, like a sum:

    #include <iostream>
    
    int main() {
        int N;
        std::cout << "Sum from 1 to N. Enter N: ";
        std::cin >> N;
    
        int sum = 0;
        for (int i = 1; i <= N; i = i + 1) {
            sum = sum + i;   // add the current i
        }
    
        std::cout << "Sum = " << sum << '\n';
        return 0;
    }

    Scope of the loop variable

    When you declare the counter inside the for parentheses, it belongs to the loop and disappears after it ends:

    for (int i = 0; i < 10; i = i + 1) {
        // i is usable here
    }
    // i is NOT visible here

    If you need the counter afterward, declare it before the loop:

    int i;
    for (i = 0; i < 10; i = i + 1) {
        // ...
    }
    // i is visible here (its final value is 10)

    Practical tips

    • Match the three parts so the loop makes progress toward ending. If the condition can never become false, you’ve made an infinite loop.
    • Keep the condition simple and predictable. Most bugs are off-by-one: re-check your start, comparison (< vs <=), and step.
    • Avoid changing the counter inside the body unless you have a strong reason—that’s a common source of confusion.
    • Prefer explicit i = i + 1 or i = i - 1 when starting out; switch to ++i/--i once the idea is solid.

    Key takeaways

    • A for loop repeats a block using three parts: initialization, condition, step.
    • The execution order is: init → check → body → step → check → …
    • Choose bounds carefully (<= vs <) to avoid off-by-one errors.
    • The loop counter declared in the for header is scoped to the loop.

    Coming up: we’ll extend loops with break/continue, cover range-based loops, and show patterns for nested loops.