Mathematical Functions
Problem Statement
Apply mathematical functions to NumPy arrays.
Background
NumPy provides universal functions (ufuncs):
- np.sqrt(): Square root
- np.exp(): Exponential (e^x)
- np.log(): Natural logarithm
- np.abs(): Absolute value
Your Task
Write a function math_functions(arr) that returns a dictionary with:
- "sqrt": Square root of each element (rounded to 2 decimals)
- "square": Square of each element
- "abs": Absolute value of each element
Output Format
Return a dictionary with exactly these three keys. All values as lists.
Example:
[1, 4, 9, 16]
{'sqrt': [1.0, 2.0, 3.0, 4.0], 'square': [1, 16, 81, 256], 'abs': [1, 4, 9, 16]}sqrt: [1, 2, 3, 4], square: [1, 16, 81, 256]
Constraints:
- Input will contain non-negative numbers
- Round sqrt results to 2 decimal places
1. Background Knowledge
NumPy arrays are the foundation for efficient numerical computing in Python, enabling vectorized operations that apply functions element-wise across entire arrays without explicit loops. This leverages compiled C code for speed, avoiding Python's interpreter overhead.
Key NumPy ufuncs (universal functions) for this problem:
- np.sqrt(x): Computes x​ for each element x
- arr ** 2: Element-wise squaring (equivalent to np.square(arr))
- np.abs(x): Absolute value ∣x∣
Prerequisites:
- Basic NumPy array creation: np.array([1, 4, 9, 16])
- Array-to-list conversion: arr.tolist()
- Rounding: np.round(arr, 2) returns floats rounded to 2 decimals
Vectorization principle: Operations broadcast across arrays in O(n) time, where n is array length, making NumPy ideal for mathematical transformations on large datasets.
2. Algorithm Approach
Use element-wise ufunc application followed by dictionary construction:
- Apply ufuncs directly to input array arr
- Round sqrt results: np.round(np.sqrt(arr), 2)
- Convert results to lists
- Package into dict {"sqrt":..., "square":..., "abs":...}
This is a pure functional mapping: arr↦{f1​(\text{arr}),f2​(\text{arr}),f3​(\text{arr})} where f1​=⋅​, f2​=(⋅)2, f3​=∣⋅∣.
No iteration needed—NumPy handles parallelism internally.
3. Step-by-Step Strategy
def math_functions(arr):
sqrt_vals = np.round(np.sqrt(arr), 2).tolist()
square_vals = (arr ** 2).tolist() # or np.square(arr)
abs_vals = np.abs(arr).tolist()
return {
"sqrt": sqrt_vals,
"square": square_vals,
"abs": abs_vals
}
Verification with sample:
- Input: np.array([1, 4, 9, 16])
- sqrt: [1.0, 2.0, 3.0, 4.0] (rounded)
- square: [1, 16, 81, 256]
- abs: [1, 4, 9, 16] (unchanged since non-negative)
Edge cases:
- Empty array: Returns empty lists [], [], []
- Single element: Works identically
4. Common Pitfalls
- Forgetting .tolist(): Returns NumPy arrays instead of Python lists, failing output format.
- Manual loops: for i in range(len(arr)) is O(n) Python overhead—use vectorization.
- Rounding integers: np.round(np.sqrt([1,4]), 2) gives [1.0, 2.0] (floats, correct).
- Negative values: Constraints guarantee non-negative, but np.sqrt raises RuntimeWarning for negatives—np.abs handles them safely.
- Import omission: Always import numpy as np.
- Dict key order: Python 3.7+ preserves insertion order, matching "sqrt", "square", "abs".
5. Time & Space Complexity
Time: O(n) where n=len(arr)
- Each ufunc: O(n) element-wise
- .tolist() and dict creation: O(n)
- Total: O(n), parallelizable on modern hardware.
Space: O(n)
- Input: O(n)
- Three output arrays: O(n) each
- Temporary intermediates garbage-collected
- Peak: O(n)
Scalability: Handles millions of elements efficiently due to contiguous memory and SIMD optimizations.