---
# System prepended metadata

title: hw1 - find the inflection point in bitonic array

---

## Problem:
Given an array A[0:n] that is bitonic, there is an index 0⩽k<n, such that the slice A[0:k+1] is strictly increasing, and slice A[k:n] is strictly decreasing. We call the index k inflection point. Note that by this definition, an increasing array is bitoinic as well (with k=n-1), and a decreasing array is bitoinic (with k=0). 

For example, say a Bitonic array A = [1, 3, 4, 3], then its inflection point of A, the  k is 2. 

Given a bitonic array A[0:n], write an algorithm (in rhythmic pseudocode) that computes the k defined above. The time complexity must be O(log n). 


Note: the slice notation is like Python: left open and right closed; i.e. A[0:n] means A[0], A[1],  . . . , A[n-1]. Also note that A[i:i] is empty array. 



## Algorithm Description
For this problem, I use a getInflectionPoint recursive algorithm. The array is split into two halves by taking the middle index m. The algorithm compares the middle element A[m] with its right neighbor A[m+1]:
If A[m] < A[m+1], the array is still increasing, so the peak must be in the right half [m+1, j).
Otherwise, the array is falling or already at the peak, so the peak is in the left half [i, m+1), which still includes m.
This process continues until only one element remains, which is returned as the inflection (peak) point.

---

```
#input: bitonic array A, search window [i,j)
#output: index k of the peak point (inflection point)


fun inflectionPoint(A, i, j) {
if (j - i == 0) return null;        # base case: empty window
if (j - i == 1) return i;           # base case: exactly one element → that's the peak


var m = floor((i + j) / 2);         # compare middle with its right only 
                                    cause when j-i >= 2, m+1 <= j-1 (in bounds)

if (A[m] < A[m + 1]) {
    return inflectionPoint(A, m + 1, j)          # rising → peak is to the RIGHT
    
} else {
    return inflectionPoint(A, i, m + 1)          # falling/at peak → keep m in the window
}

```


## Correctness Proof

**Given.** `A` is bitonic: strictly increasing up to a unique peak `k`, then strictly decreasing.

**Goal.** `inflectionPoint(A, i, j)` returns the unique peak index `k ∈ [i, j)` when `[i, j)` is a bitonic slice containing the peak.

**Induction on window length L = j − i (numbers of elts)**

- **Base cases.**
    - `L = 0`: empty window → function returns `null`.
    - `L = 1`: window has a single index `i`; that element is the (unique) peak in this window → function returns `i`.
        
        Both bases are correct.
        
- **Inductive step (L ≥ 2).**
    
    Let `m = ⌊(i + j)/2⌋`. Since `L ≥ 2`, `m + 1 ≤ j − 1` is valid.
    
    - **Case 1: `A[m] < A[m+1]`.**
        
        The sequence is **ascending** at `m → m+1`. In a bitonic array, once you are ascending at `m`, the global peak must lie **to the right** of `m`. Hence the peak lies in `[m+1, j)`. The recursive call keeps the peak and reduces the length (< L), so by the induction hypothesis it returns the correct index.
        
    - **Case 2: `A[m] ≥ A[m+1]`.**
        
        The sequence is **not ascending** at `m → m+1` (falling or at the peak). Therefore the peak is **at `m` or to the left**. The subwindow `[i, m+1)` contains all indices `i … m`, including the possible peak at `m`. Again the length strictly decreases (< L), so by the induction hypothesis the recursive call returns the correct peak.
        
    
    In both cases, the recursive step preserves the peak and reduces the window, so the algorithm eventually hits `L = 1` and returns that index, which is the peak.
    

---

## Time Complexity

- Each call does **O(1)** work.
- Each step cuts problem size about in half.
- Recurrence: `T(n) = T(n/2) + O(1)`.
- Solution: **O(log n)** time.
- Space: **O(log n)** recursive, **O(1)** iterative.

