This program fits an order-five polynomial to a set of 20000 experimental data points. It demonstrates the power of BBC BASIC's built-in array and matrix operations to perform high-speed calculations. It could be adapted to carry out other least-squares fitting tasks.
Download POLYFIT.BBC | Run POLYFIT.EXE |
---|
REM POLYFIT - fit a polynomial to a set of experimental data
REM demonstrating the use of BBC BASIC's matrix operations.
REM In this version the maximum order of polynomial is five.
REM Richard T. Russell, richard@rtrussell.co.uk, 12-Apr-2011
HIMEM = PAGE + 5000000
INSTALL @lib$+"ARRAYLIB"
INSTALL @lib$+"FNUSING"
INSTALL @lib$+"WINLIB5"
*FLOAT 64
file$ = @dir$+"polyfit.csv"
max% = 20000
REM Initialise screen:
SYS "SetWindowText", @hwnd%, "Least squares polynomial fitting"
VDU 23,22,720;480;8,16,16,128
VDU 5
ORIGIN 140,168
COLOUR 2,0,100,0
PROCaxes
REM Create controls:
button1% = FN_button("Load && plot file", 190, 12, 120, 20, FN_setproc(PROCload), 0)
button2% = FN_button("Fit polynomial", 390, 12, 120, 20, FN_setproc(PROCfit), 0)
SYS "EnableWindow", button2%, 0
REM Declare arrays:
DIM vector(5), matrix(5,5)
DIM x(max%), x2(max%), x3(max%), x4(max%), x5(max%)
DIM x6(max%), x7(max%), x8(max%), x9(max%), x10(max%)
DIM y(max%), xy(max%), x2y(max%), x3y(max%), x4y(max%), x5y(max%)
REM Idle loop:
REPEAT
WAIT 1
UNTIL FALSE
END
REM Draw axes and labels:
DEF PROCaxes
CLS
GCOL 0
FOR x = 0.0 TO 1.0 STEP 0.1
MOVE x*1200, 0 : PLOT 21, x*1200, 700
MOVE x*1200-24, -24 : PRINT FNusing("#.#", x);
NEXT
FOR y = 0 TO 6
MOVE 0, 700*y/6 : PLOT 21, 1198, 700*y/6
MOVE -80, 700*y/6+16 : PRINT FNusing("#.#", y);
NEXT
RECTANGLE 0, 0, 1198, 700
ENDPROC
REM Load and plot file:
DEF PROCload
file% = OPENIN(file$)
IF file% = 0 ERROR 0, "Could not open file " + file$
PROCaxes
SYS "EnableWindow", button2%, 0
index% = 0
x() = 0.0
y() = 0.0
GCOL 4
WHILE NOT EOF#file% AND index% <= max%
record$ = GET$#file%
comma% = INSTR(record$, ",")
IF comma% THEN
x(index%) = VAL(record$)
y(index%) = VAL(MID$(record$,comma%+1))
LINE x(index%)*1200, y(index%)*700/6, x(index%)*1200, y(index%)*700/6
index% += 1
ENDIF
ENDWHILE
CLOSE #file%
npts% = index%
SYS "EnableWindow", button2%, 1
ENDPROC
REM Fit and plot polynomial:
DEF PROCfit
TIME = 0
sum_x = SUM(x())
x2() = x() * x() : sum_x2 = SUM(x2())
x3() = x() * x2() : sum_x3 = SUM(x3())
x4() = x2() * x2() : sum_x4 = SUM(x4())
x5() = x2() * x3() : sum_x5 = SUM(x5())
x6() = x3() * x3() : sum_x6 = SUM(x6())
x7() = x3() * x4() : sum_x7 = SUM(x7())
x8() = x4() * x4() : sum_x8 = SUM(x8())
x9() = x4() * x5() : sum_x9 = SUM(x9())
x10() = x5() * x5() : sum_x10 = SUM(x10())
sum_y = SUM(y())
xy() = x() * y() : sum_xy = SUM(xy())
x2y() = x2() * y() : sum_x2y = SUM(x2y())
x3y() = x3() * y() : sum_x3y = SUM(x3y())
x4y() = x4() * y() : sum_x4y = SUM(x4y())
x5y() = x5() * y() : sum_x5y = SUM(x5y())
matrix() = \
\ npts%, sum_x, sum_x2, sum_x3, sum_x4, sum_x5, \
\ sum_x, sum_x2, sum_x3, sum_x4, sum_x5, sum_x6, \
\ sum_x2, sum_x3, sum_x4, sum_x5, sum_x6, sum_x7, \
\ sum_x3, sum_x4, sum_x5, sum_x6, sum_x7, sum_x8, \
\ sum_x4, sum_x5, sum_x6, sum_x7, sum_x8, sum_x9, \
\ sum_x5, sum_x6, sum_x7, sum_x8, sum_x9, sum_x10
vector() = \
\ sum_y, sum_xy, sum_x2y, sum_x3y, sum_x4y, sum_x5y
PROC_invert(matrix())
vector() = matrix().vector()
took = TIME / 100
GCOL 1
MOVE 80, -72
PRINT "y = " FNusing("##.###", vector(0)) FNusing("+##.###", vector(1)) " x" \
\ FNusing("+##.###", vector(2)) " x^2" FNusing("+##.###", vector(3)) " x^3" \
\ FNusing("+##.###", vector(4)) " x^4" FNusing("+##.###", vector(5)) " x^5"
GCOL 2
MOVE 80, -120
PRINT "Fitting order-five polynomial to " ;npts% " points took " ;
PRINT FNusing("#.##", took) " seconds"
GCOL 9
FOR x = 0.0 TO 1.0 STEP 0.001
y = 0
FOR power% = 0 TO 5
y += vector(power%) * x^power%
NEXT
PLOT x*1200, y*700/6
NEXT x
ENDPROC