Scalar Broadcasting
Problem Statement
Apply scalar operations to arrays using broadcasting.
Background
Broadcasting allows NumPy to work with arrays of different shapes:
- Scalar + array: adds scalar to every element
- Scalar * array: multiplies every element
- This is much faster than Python loops!
Your Task
Write a function scalar_ops(arr, scalar) that performs operations.
Output Format
Return a dictionary with:
- "add": arr + scalar (as list)
- "multiply": arr * scalar (as list)
- "power": arr ** scalar (as list)
- "divide": arr / scalar (rounded to 2 decimals, as list)
Example:
arr = [1, 2, 3, 4], scalar = 2
{'add': [3, 4, 5, 6], 'multiply': [2, 4, 6, 8], 'power': [1, 4, 9, 16], 'divide': [0.5, 1.0, 1.5, 2.0]}Broadcasting applies scalar to each element
Constraints:
- scalar will be non-zero
- Round division to 2 decimal places
More from NumPy Foundations
Background Knowledge
NumPy Broadcasting is a powerful mechanism that enables arithmetic operations between arrays of different shapes without explicit looping. When operating a scalar (0-D array) with an array (e.g., 1-D or higher), NumPy automatically "broadcasts" the scalar to match the array's shape by virtually replicating it across all elements. This vectorization leverages optimized C-level loops, making operations significantly faster than Python for loops—often by orders of magnitude for large arrays.
Key operations include addition (+), multiplication (*), exponentiation (**), and division (/), all of which follow the same broadcasting rules. For example, arr + scalar adds the scalar to every element of arr. NumPy handles the shape alignment implicitly: a scalar has shape (), which is prepended with ones to match the array (e.g., () → (1,) for a 1-D array). This eliminates the need for manual replication like [scalar] * len(arr), promoting concise, efficient code.
Understanding broadcasting is foundational for array programming, as it extends to array-array operations and underpins libraries like Pandas and SciPy. It reduces memory usage (no copies created) and enables elegant expressions for data transformations.
Algorithm/Approach
Use NumPy's built-in vectorized operations with automatic scalar broadcasting to compute each result in O(n) time, where n is the array length. Construct a dictionary mapping operation keys to converted NumPy arrays (as lists), applying rounding only to division for precision control. Avoid explicit loops or list comprehensions to maintain performance and idiomatic NumPy style.
Step-by-Step Strategy
- Input Handling: Accept arr (NumPy array) and scalar (number); ensure arr is 1-D via arr.flatten() if needed.
- Compute Operations:
- "add": arr + scalar
- "multiply": arr * scalar
- "power": arr ** scalar
- "divide": (arr / scalar).round(2)
- Convert to Lists: Use .tolist() on each result array to match output format.
- Build Dictionary: Return {"add": add_list, "multiply": multiply_list,...}.
- Edge Cases: Handle zero/negative scalars (division/power behave as expected in NumPy); test with sample input.
Common Pitfalls
- Forgetting .tolist(): NumPy arrays print differently (e.g., [1 2 3] vs. [1, 2, 3]); always convert for list output.
- Rounding Division: Use .round(2) after division, not before—np.round(arr / scalar, 2) preserves float precision.
- Shape Mismatches: Scalar broadcasting fails only on incompatible multi-D arrays; flatten if input varies.
- Integer Division: Ensure arr is float-type if needed (arr.astype(float)); / yields float by default.
- Zero Scalar: Division by zero yields inf or nan—handle per problem spec (usually propagate).
- Performance: Avoid Python loops like [x + scalar for x in arr]; they defeat broadcasting benefits.
Time & Space Complexity
- Time: O(n) per operation (4 total), dominated by vectorized NumPy ufuncs; constant-time .tolist() and dict creation. Overall: O(n).
- Space: O(n) for each result array (temporary) + O(n) for lists; no extra space beyond input/output due to in-place broadcasting eligibility. Total: O(n). Scales linearly; ideal for large n (~10^6+ elements).