Programming Skills — Solution Formulation, Correctness, Clarity & Optimisation
Next Topic(s):
Created:
29th of August 2025
12:35:27 AM
Modified:
30th of August 2025
08:21:37 AM
Programming Skills — Solution Formulation, Correctness, Clarity & Optimisation
Learning Objectives
- Formulate an equivalence problem in code: given two models
f
andg
, define the crossing wheref(x) = g(x)
, identify inputs/outputs and constraints, and state clearly which parameter is the unknown. - Translate mathematics to a computable pipeline: use exact expressions, recognise and eliminate constants that cancel, choose control variables, and set safe numeric ranges for search.
- Implement bisection with
while
loops only (no imports/functions in Option 1): maintain a valid bracket[lo, hi]
, update it via the midpoint test, and stop whenhi - lo < eps
. - Build a single-scenario comparator: evaluate both models at fixed parameters, decide which outcome is larger, and report the absolute difference.
- Follow a strict output contract: keep full precision internally, round only for display, apply consistent number formatting (decimals and separators), and print exactly the required lines in the specified template.
- Justify existence and uniqueness: use monotonicity/continuity to guarantee a single root, track loop invariants, and reason about convergence and iteration counts.
- Generalise the pattern: vary constants, switch which parameter you solve for, and (in a modular version) refactor to small reusable functions while preserving the flat-script variant.
Tip: Keep full precision inside loops; round only for display to avoid logic errors.
Breakeven: Simple vs Compound Interest
Breakeven analysis — what it is and why it matters
Breakeven analysis identifies the point where two return formulas deliver the same maturity. Here we compare a simple-interest offer (linear growth with time) against a compound-interest offer (exponential growth with compounding). At the crossing, an investor is indifferent; on one side of the crossing simple interest pays more, and on the other side compound interest does.
Breakeven condition is given here for practice; in tests, derive it.
Engineers are often faced with financial decisions, each of which hold good under specific conditions. This problem should introduce you to a scenario where you will be required to have different constraints, and you ease one while keeping the other constraints constant. Ofcourse, with improved computing power, scenarios with more than one constraint being modified at the same time is possible, however we will use such scenarios for later.
Phrasing of the question.
This question was phrased thus.
Assignment — Option 1 (while-loop & bisection only)
Use the formulas and bisection idea explained above. Do not define functions and do not import any libraries. Work only with variables and while
loops plus simple comparisons.
Given (fixed for Part A)
- Principal
P = ₹1,50,000
(store as150000.0
in code). - Compound offer: nominal rate
R_c = 8.4
(% p.a.), compoundingm = 4
times per year. - Reference tenure for Tasks 1 & 3:
N0 = 6.75
(years).
Programming constraints
- No function definitions; keep the file as a straight script.
- Use bisection inside
while
loops with a bracket that encloses the solution. - Use
eps = 1e-12
(or tighter) as the stopping tolerance. - Rate-search bracket:
lo = 0.0
,hi = 50.0
(% p.a.). - Tenure-search bracket:
loN = 0.0
,hiN = 50.0
(years).
Part A — Core tasks (use the exact constants above)
- Task 1 — Solve for the matching simple rate
R_s*
atN = N0
.- Compute the compound growth factor at
N0
:target = (1 + R_c/(100*m))**(m*N0)
(this equalsA_CI/P
atN0
). - Bisect on
R_s
in[0, 50]
untilhi - lo < eps
, comparinglhs = 1 + N0*mid/100
withtarget
. - Report
R_s*
to two decimals and the common maturityA* = P*(1 + N0*R_s*/100)
rounded to the nearest rupee.
- Compute the compound growth factor at
- Task 2 — Solve for the matching tenure
N*
at fixed simple rateR_s = 11.2
.- Bisect on
N
in[0, 50]
untilhiN - loN < eps
, comparinglhs = 1 + N*R_s/100
withrhs = (1 + R_c/(100*m))**(m*N)
. - Report
N*
to two decimals and the common maturityA* = P*(1 + R_s*N*/100)
rounded to the nearest rupee.
- Bisect on
- Task 3 — One-shot comparison at the quoted rate.
- Take
R_s = 10.5
andN = N0
. - Compute
A_SI = P*(1 + R_s*N0/100)
andA_CI = P*((1 + R_c/(100*m))**(m*N0))
. - Print which option is higher and by how many rupees (absolute difference, rounded to nearest rupee).
- Take
Printing & rounding rules (match exactly)
- Round
R_s*
andN*
to two decimals for display only (keep full precision inside loops). - Round rupee amounts with
round()
and print with comma separators, e.g.₹{round(value):,}
. - Produce exactly three lines, one per task, in this format (values will vary):
Task 1: Rs* ≈ XX.XX% ; A* ≈ ₹Y,YYY
Task 2: N* ≈ ZZ.ZZ years ; A* ≈ ₹W,WWW
Task 3: <SI higher by|CI higher by> ₹V,VVV
Sanity checks (build them into your thinking; optional assert
s allowed)
- If
N = 0
, both maturities equal₹P
. - The bisection invariant holds: after each iteration the true solution remains within the current bracket.
- At the reported
R_s*
orN*
, the two sides match to within your tolerance.
What to submit
- A single file named
si_ci_breakeven_while.py
that runs without input and prints the three lines above. - No extra prompts, no additional output, no function definitions, no imports.
Part B — Short variations (practice, same code pattern; do not submit unless asked)
- Find
R_s*
&A*
:P = ₹2,25,000
,N = 5.80
; CIR_c = 9.0
,m = 12
. - Find
N*
&A*
:P = ₹3,00,000
,R_s = 10.1
; CIR_c = 7.4
,m = 2
. - Find
R_s*
&A*
:P = ₹4,00,000
, capN = 6.25
; CIR_c = 8.6
,m = 4
. - Report each
N*
:P = ₹1,20,000
,R_s = 10.8
; CIR_c = 8.4
withm ∈ {1,4,12}
. - Find minimal tenure:
P = ₹2,75,000
,R_s = 13.0
; CIR_c = 9.2
,m = 12
— compute the smallestN
(to two decimals) for which CI ≥ SI.
It is now our responsibility to get a solution.
Scenario
You are responsible for negotiating the best return for some extra money that you have lying with you from your business. You wish to have the information on what rates or duration that you should keep as the levels below which you should not go below when negotiationg with different financial institutions.
Principal: ₹1,50,000. Two offers:
- Option A — Simple Interest (SI): rate
R_s
is negotiable. - Option B — Compound Interest (CI):
R_c = 8.4%
p.a., compounded quarterly (m = 4
).
Formulas
$$ \text{SI}=\frac{P\,N\,R_s}{100}, \qquad A_{\mathrm{SI}}=P+\text{SI}=P\!\left(1+\frac{N\,R_s}{100}\right) $$ $$ A_{\mathrm{CI}}=P\!\left(1+\frac{R_c}{100\,m}\right)^{mN} $$ $$ \textbf{Breakeven:}\quad P\!\left(1+\frac{N\,R_s}{100}\right)=P\!\left(1+\frac{R_c}{100\,m}\right)^{mN} $$
What to find
- Unknown rate. With
N = 6.75
years, findR_s*
(% p.a., two decimals) and the common maturityA*
. - Unknown years. With
R_s = 11.2%
, findN*
(years, two decimals) andA*
. - Quick check. With
N = 6.75
andR_s = 10.5%
, which option is higher and by how many rupees?
As an exercise, we are restricting to fixed values of input. However, try entering different values to find the ideal solution for the scenario that you are envisioning. In practice, these values become constraints.
Option 1: Python (Using the while-loop iteration only)
We use the while loop to still work out a solution, however the algorithm uses a bisection method which will aim to achieve covergence with fewer iterations.
# si_ci_breakeven_while.py
# Breakeven (SI vs CI) using only while loops (no function defs)
# Inputs
P = 150000.0 # rupees
Rc = 8.4 # % p.a. (compound)
m = 4 # compounding per year
N0 = 6.75 # years for Task 1 and Task 3
# ---------- Task 1: find Rs* for fixed N0 (bisection) ----------
target = (1.0 + Rc/(100.0*m))**(m*N0) # = A_CI/P at N0
lo, hi = 0.0, 50.0 # search Rs in [0%, 50%]
eps = 1e-12
while (hi - lo) > eps:
mid = 0.5*(lo + hi)
lhs = 1.0 + N0*mid/100.0 # = A_SI/P at N0
if lhs >= target:
hi = mid
else:
lo = mid
Rs_star = 0.5*(lo + hi)
A_star1 = P*(1.0 + N0*Rs_star/100.0)
print(f"Task 1: Rs* ≈ {Rs_star:.2f}% ; A* ≈ ₹{round(A_star1):,}")
# ---------- Task 2: find N* for fixed Rs (bisection) ----------
Rs_fixed = 11.2 # % p.a. (simple)
loN, hiN = 0.0, 50.0 # years bracket
while (hiN - loN) > eps:
midN = 0.5*(loN + hiN)
lhs = 1.0 + midN*Rs_fixed/100.0
rhs = (1.0 + Rc/(100.0*m))**(m*midN)
if lhs >= rhs:
hiN = midN
else:
loN = midN
N_star = 0.5*(loN + hiN)
A_star2 = P*(1.0 + Rs_fixed*N_star/100.0)
print(f"Task 2: N* ≈ {N_star:.2f} years ; A* ≈ ₹{round(A_star2):,}")
# ---------- Task 3: single-horizon comparison ----------
Rs_offer = 10.5
A_si = P*(1.0 + Rs_offer*N0/100.0)
A_ci = P*((1.0 + Rc/(100.0*m))**(m*N0))
diff = A_si - A_ci
print(f"Task 3: {'SI higher by' if diff>=0 else 'CI higher by'} ₹{abs(round(diff)):,}")
Option 2: Python (Defining functions to create more reusable code)
A similar approach, but functions are defined to achieve modularity.
# Inputs
P = 150000.0 # rupees
Rc = 8.4 # % p.a. (compound)
m = 4 # compounding per year
N0 = 6.75 # years for Task 1 and Task 3
import math
def comp_factor(N, Rc, m):
# This function returns the Maturity Amount at the
# end of the period of investment for a given rate
# of interest, using Compound Interest.
return (1.0 + Rc/(100.0*m))**(m*N)
def A_SI(P, N, Rs):
# This function returns the Maturity Amount at the
# end of the period of investment for a given rate
# of interest, using Simple Interest.
return P*(1.0 + N*Rs/100.0) # uses PNR/100
def A_CI(P, N, Rc, m):
return P*comp_factor(N, Rc, m)
# ---------- Task 1: solve for Rs* with bisection (while loop) ----------
target = comp_factor(N0, Rc, m) # equals A_CI/P at N0
lo, hi = 0.0, 50.0 # search Rs in [0%, 50%]
eps = 1e-10
while hi - lo > 1e-10:
mid = 0.5*(lo + hi)
lhs = 1.0 + N0*mid/100.0 # equals A_SI/P at N0
if lhs >= target:
hi = mid
else:
lo = mid
Rs_star = 0.5*(lo + hi)
A_star1 = A_SI(P, N0, Rs_star) # common maturity
print(f"Task 1: Rs* ≈ {Rs_star:.2f}% ; A* ≈ ₹{round(A_star1):,}")
# ---------- Task 2: solve for N* with bisection (while loop) ----------
Rs_fixed = 11.2
loN, hiN = 0.0, 50.0 # years bracket
while hiN - loN > 1e-10:
midN = 0.5*(loN + hiN)
lhs = 1.0 + midN*Rs_fixed/100.0
rhs = comp_factor(midN, Rc, m)
if lhs >= rhs:
hiN = midN
else:
loN = midN
N_star = 0.5*(loN + hiN)
A_star2 = A_SI(P, N_star, Rs_fixed)
print(f"Task 2: N* ≈ {N_star:.2f} years ; A* ≈ ₹{round(A_star2):,}")
# ---------- Task 3: quick comparison ----------
Rs_offer = 10.5
diff = A_SI(P, N0, Rs_offer) - A_CI(P, N0, Rc, m)
who = "SI higher by" if diff >= 0 else "CI higher by"
print(f"Task 3: {who} ₹{abs(round(diff)):,}")
Five short practice items
- Given
P = ₹2,25,000
,N = 5.80
, CIR_c = 9.0%
monthly (m=12
): findR_s*
andA*
. P = ₹3,00,000
,R_s = 10.1%
, CIR_c = 7.4%
semi-annual (m=2
): findN*
andA*
.P = ₹4,00,000
, capN = 6.25
, CIR_c = 8.6%
quarterly (m=4
): findR_s*
andA*
.P = ₹1,20,000
,R_s = 10.8%
, CIR_c = 8.4%
withm = 1, 4, 12
: report eachN*
.P = ₹2,75,000
,R_s = 13.0%
, CIR_c = 9.2%
monthly (m=12
): find minimal
Some
With principal ₹1,50,000, years N
, simple rate R_s
% p.a., compound rate R_c
% p.a., and compounding frequency m
per year, the formulas are:
$$ \text{SI}=\frac{P\,N\,R_s}{100}, \qquad A_{\mathrm{SI}}=P+\text{SI}=P\!\left(1+\frac{N\,R_s}{100}\right) $$ $$ A_{\mathrm{CI}}=P\!\left(1+\frac{R_c}{100\,m}\right)^{mN} $$
Breakeven (given here for practice):
$$ P\!\left(1+\frac{N\,R_s}{100}\right) = P\!\left(1+\frac{R_c}{100\,m}\right)^{mN} $$
The principal P
cancels from the breakeven equation, so the crossing depends on rates, compounding frequency, and time. This gives two natural “negotiation levers”: (i) fix the tenure N
and solve for the matching rate R_s^*
; or (ii) fix the quoted simple rate R_s
and solve for the matching tenure N^*
.
Monotonicity guarantees (existence and uniqueness at a glance)
- Unknown rate, time fixed. For fixed
N>0
, the left-hand side \(1+\frac{N\,R_s}{100}\) increases strictly and smoothly asR_s
increases, whereas the right-hand side is a positive constant. There is exactly one solution, and it has the closed form $$ R_s^{*}(N)=\frac{100}{N}\left[\left(1+\frac{R_c}{100\,m}\right)^{mN}-1\right]. $$ - Unknown time, rate fixed. With
R_s
fixed, \(g(N)=1+\frac{N\,R_s}{100}\) and \(h(N)=\left(1+\frac{R_c}{100\,m}\right)^{mN}\) are both continuous and strictly increasing inN
. For smallN
the linear term can exceed the compound term; for largeN
the exponential dominates. Continuity plus opposing behaviours ensures a single crossing \(N^*>0\). Convexity of the exponential rules out multiple positive roots.
How the Python while-loop code works
The script computes three items using only while
loops and simple comparisons: (1) the matching simple rate R_s^*
for a fixed tenure, (2) the matching tenure N^*
for a fixed simple rate, and (3) a one-shot comparison at a given rate and tenure. The common technique is bisection: keep a lower and upper bound that straddle the solution, test the midpoint, and halve the bracket until it is sufficiently tight.
- Task 1 — find
R_s^*
forN=N0
. Compute the compound growth factor at the given tenure: $$ \text{target}=\left(1+\frac{R_c}{100\,m}\right)^{mN_0}. $$ Start with a safe rate bracket, e.g.[0, 50]
(% p.a.). At each step setmid=(lo+hi)/2
, form the linear left side \(1+\frac{N_0\,\text{mid}}{100}\), and compare it totarget
. If the left side is too large, move the upper bound down; otherwise move the lower bound up. Stop oncehi − lo
is below a tiny tolerance. The midpoint isR_s^*
. The common maturity is \(A^*=P\!\left(1+\frac{N_0\,R_s^*}{100}\right)\). - Task 2 — find
N^*
for fixedR_s
. Use the same bracket-and-halve logic on years, e.g.[0, 50]
. Compare the linear left side \(1+\frac{N\,R_s}{100}\) with the exponential right side \(\left(1+\frac{R_c}{100\,m}\right)^{mN}\). Move the bound that keeps the (unique) root inside the bracket and stop when the interval is tight. Report \(N^*\) and \(A^*=P\!\left(1+\frac{N^*\,R_s}{100}\right)\). - Task 3 — straight comparison. Evaluate \(A_{\mathrm{SI}}=P\!\left(1+\frac{N_0\,R_s}{100}\right)\) and \(A_{\mathrm{CI}}=P\!\left(1+\frac{R_c}{100\,m}\right)^{mN_0}\), then print which is larger and by how many rupees.
Why this works smoothly: the quantities you are solving for are monotone in their unknowns. That guarantees a single solution in any bracket that encloses it, and bisection will reach it with steady, predictable convergence—no derivatives, no fragile steps.
Key Takeaways — Principles to work on
You now have a practical way to compare a simple-interest quote with a quarterly-compounded quote. The breakeven condition tells you exactly when both offers pay the same maturity; on either side of that point, you can state which offer is better and why. Because the principal cancels, the decision rests only on rates, compounding frequency, and time—the three levers you can negotiate.
How to read your results in plain language
- Fix tenure
N
and solve forR_s^*
: if a bank’s simple rateR_s
is aboveR_s^*
, simple interest wins at that tenure; if it is below, compound interest wins. - Fix simple rate
R_s
and solve forN^*
: for tenures shorter thanN^*
, simple interest pays more; for tenures longer thanN^*
, compound interest overtakes. - Single-horizon check: computing both maturities at your chosen
N
andR_s
gives a clear rupee difference to justify your choice.
What good programming looks like here
- Correctness by design: use a bracket that safely contains the answer and keep the invariant “the solution lies between
lo
andhi
”. - Predictable convergence: stop when
hi − lo
is below a sensible tolerance (match it to the number of decimals you will report). - Clean separation: compute in full precision; round only for display (₹ with commas).
- Small, named functions: keep
A_SI
,comp_factor
, andA_CI
focused and reusable.
Sanity checks before you submit
- If
N = 0
, both maturities equal ₹P
. - For fixed
N
, your computedR_s^*
makes \(A_{\mathrm{SI}}=A_{\mathrm{CI}}\) to within your tolerance. - For fixed
R_s
, yourN^*
makes the linear and exponential sides match and gives a single crossing (not multiple roots). - Changing
m
from 1 → 4 → 12 should not decrease the compound maturity at fixed nominalR_c
andN
.
Common pitfalls to avoid
- Letting rounding drive logic: never compare rounded values inside the loop.
- Seed Values that do not enclose the solution: choose wide, realistic bounds (e.g., rates/years in
[0, 50]
) before tightening. Seed values are the initial values chosen to start an iterative process. - Confusing nominal and effective rates: remember the code uses nominal
R_c
with frequencym
.
Reflect and extend (for curious minds)
- Vary
m
across 1, 2, 4, 12 and note howN^*
shifts. Why do brochures emphasise compounding frequency? - Hold
N
fixed and sweepR_s
to sketch where SI beats CI. What pattern do you see? - Rewrite your script so the tolerance is an input, then measure how many iterations bisection needs as you tighten it.
In short: you have modelled a real negotiation, coded a robust method to find the crossing, and learnt to justify a choice with numbers. That blend—clear question, careful code, and defensible decision—is the habit to carry into larger finance and engineering problems.