Pascal's triangle/Puzzle: Difference between revisions
Go solution |
→{{header|C}}: field solver method |
||
Line 490: | Line 490: | ||
=={{header|C}}== |
=={{header|C}}== |
||
{{incorrect|C|Displayed result seems wrong.}} |
|||
This solution is based upon algebraic necessities, namely that a solution exists when (top - 4(a+b))/7 is integral. It also highlights the type difference between floating point numbers and integers in C. |
This solution is based upon algebraic necessities, namely that a solution exists when (top - 4(a+b))/7 is integral. It also highlights the type difference between floating point numbers and integers in C. |
||
Line 538: | Line 538: | ||
x: 3, y: 13, z: 10 |
x: 3, y: 13, z: 10 |
||
</pre> |
</pre> |
||
===Field equation solver=== |
|||
Treating relations between cells as if they were differential equations, and apply negative feedback to each cell at every iteration step. This is how field equations with boundary conditions are solved numerically. It is, of course, not the optimal solution for this particular task. |
|||
<lang c>#include <stdio.h> |
|||
#define E(x, row, col) x[(row) * ((row) + 1) / 2 + col] |
|||
void show(double *x) |
|||
{ |
|||
int i = 0, j = 0; |
|||
for (i = 0; i < 5; i++) |
|||
for (j = 0; j <= i; j++) |
|||
printf("%4d%c", (int)(.5 + *(x++)), j < i ? ' ':'\n'); |
|||
} |
|||
void iterate(double *v, double *diff) |
|||
{ |
|||
int i, j; |
|||
double sum; |
|||
redo: |
|||
/* enforce boundary conditions */ |
|||
E(v, 0, 0) = 151; |
|||
E(v, 2, 0) = 40; |
|||
E(v, 4, 1) = 11; |
|||
E(v, 4, 3) = 4; |
|||
/* calculate difference from equilibrium */ |
|||
for (i = 1; i < 5; i++) { |
|||
for (j = 0; j <= i; j++) { |
|||
E(diff, i, j) = 0; |
|||
if (j < i) |
|||
E(diff, i, j) += E(v, i - 1, j) |
|||
- E(v, i, j + 1) |
|||
- E(v, i, j); |
|||
if (j) E(diff, i, j) += E(v, i - 1, j - 1) |
|||
- E(v, i, j - 1) |
|||
- E(v, i, j); |
|||
} |
|||
} |
|||
for (i = 0; i < 4; i++) |
|||
for (j = 0; j < i; j++) |
|||
E(diff, i, j) += E(v, i + 1, j) |
|||
+ E(v, i + 1, j + 1) |
|||
- E(v, i, j); |
|||
E(diff, 4, 2) += E(v, 4, 0) + E(v, 4, 4) - E(v, 4, 2); |
|||
/* do feedback, check if we are close enough */ |
|||
for (i = 0, sum = 0; i < 15; i++){ |
|||
v[i] += diff[i] / 4; /* 4: scale down the feedback to avoid oscillations */ |
|||
sum += diff[i] * diff[i]; |
|||
} |
|||
printf("dev: %g\n", sum); |
|||
if (sum < .1) return; |
|||
goto redo; |
|||
} |
|||
int main() |
|||
{ |
|||
double v[15] = {0}, diff[15] = {0}; |
|||
iterate(v, diff); |
|||
show(v); |
|||
return 0; |
|||
}</lang>output<lang>dev: 73410 |
|||
dev: 17968.7 |
|||
dev: 6388.46 |
|||
dev: 2883.34 |
|||
...<about a million and half iterations later>... |
|||
dev: 0.116055 |
|||
dev: 0.107006 |
|||
dev: 0.09866 |
|||
151 |
|||
81 70 |
|||
40 41 29 |
|||
16 24 17 12 |
|||
5 11 13 4 8 |
|||
</lang> |
|||
=={{header|Clojure}}== |
=={{header|Clojure}}== |
Revision as of 05:57, 20 July 2011
You are encouraged to solve this task according to the task description, using any language you may know.
This puzzle involves a Pascals Triangle, also known as a Pyramid of Numbers.
[ 151] [ ][ ] [40][ ][ ] [ ][ ][ ][ ] [ X][11][ Y][ 4][ Z]
Each brick of the pyramid is the sum of the two bricks situated below it.
Of the three missing numbers at the base of the pyramid, the middle one is the sum of the other two (that is, Y = X + Z).
Write a program to find a solution to this puzzle.
Ada
The solution makes an upward run symbolically, though excluding Z. After that two blocks (1,1) and (3,1) being known yield a 2x2 linear system, from which X and Y are determined. Finally each block is revisited and printed. <lang ada>with Ada.Text_IO; use Ada.Text_IO;
procedure Pyramid_of_Numbers is
B_X, B_Y, B_Z : Integer := 0; -- Unknown variables
type Block_Value is record Known : Integer := 0; X, Y, Z : Integer := 0; end record; X : constant Block_Value := (0, 1, 0, 0); Y : constant Block_Value := (0, 0, 1, 0); Z : constant Block_Value := (0, 0, 0, 1); procedure Add (L : in out Block_Value; R : Block_Value) is begin -- Symbolically adds one block to another L.Known := L.Known + R.Known; L.X := L.X + R.X - R.Z; -- Z is excluded as n(Y - X - Z) = 0 L.Y := L.Y + R.Y + R.Z; end Add; procedure Add (L : in out Block_Value; R : Integer) is begin -- Symbolically adds a value to the block L.Known := L.Known + R; end Add; function Image (N : Block_Value) return String is begin -- The block value, when X,Y,Z are known return Integer'Image (N.Known + N.X * B_X + N.Y * B_Y + N.Z * B_Z); end Image;
procedure Solve_2x2 (A11, A12, B1, A21, A22, B2 : Integer) is begin -- Don't care about things, supposing an integer solution exists if A22 = 0 then B_X := B2 / A21; B_Y := (B1 - A11*B_X) / A12; else B_X := (B1*A22 - B2*A12) / (A11*A22 - A21*A12); B_Y := (B1 - A11*B_X) / A12; end if; B_Z := B_Y - B_X; end Solve_2x2; B : array (1..5, 1..5) of Block_Value; -- The lower triangle contains blocks
begin
-- The bottom blocks Add (B(5,1),X); Add (B(5,2),11); Add (B(5,3),Y); Add (B(5,4),4); Add (B(5,5),Z);
-- Upward run for Row in reverse 1..4 loop for Column in 1..Row loop Add (B (Row, Column), B (Row + 1, Column)); Add (B (Row, Column), B (Row + 1, Column + 1)); end loop; end loop; -- Now have known blocks 40=(3,1), 151=(1,1) and Y=X+Z to determine X,Y,Z Solve_2x2 ( B(1,1).X, B(1,1).Y, 151 - B(1,1).Known, B(3,1).X, B(3,1).Y, 40 - B(3,1).Known );
-- Print the results for Row in 1..5 loop New_Line; for Column in 1..Row loop Put (Image (B(Row,Column))); end loop; end loop;
end Pyramid_of_Numbers;</lang> Sample output:
151 81 70 40 41 29 16 24 17 12 5 11 13 4 8
ALGOL 68
<lang algol68>MODE
FIELD = REAL, VEC = [0]REAL, MAT = [0,0]REAL;
MODE BRICK = UNION(INT, CHAR);
FLEX[][]BRICK puzzle = (
( 151), ( " ", " "), ( 40, " ", " "), ( " ", " ", " ", " "), ( "x", 11, "y", 4, "z")
);
PROC mat col = (INT row, col)INT: row*(row-1)OVER 2 + col; INT col x = mat col(5,1),
col y = mat col(5,3), col z = mat col(5,5);
OP INIT = (REF VEC vec)VOID: FOR elem FROM LWB vec TO UPB vec DO vec[elem]:=0 OD; OP INIT = (REF MAT mat)VOID: FOR row FROM LWB mat TO UPB mat DO INIT mat[row,] OD;
OP / = (MAT a, MAT b)MAT:( # matrix division #
[LWB b:UPB b]INT p ; INT sign; [,]FIELD lu = lu decomp(b, p, sign); [LWB a:UPB a, 1 LWB a:2 UPB a]FIELD out; FOR col FROM 2 LWB a TO 2 UPB a DO out[,col] := lu solve(b, lu, p, a[,col]) OD; out
);
OP / = (VEC a, MAT b)VEC: ( # vector division #
[LWB a:UPB a,1]FIELD transpose a; transpose a[,1]:=a; (transpose a/b)[,LWB a]
);
INT upb mat = mat col(UPB puzzle, UPB puzzle); [upb mat, upb mat] REAL mat; INIT mat; [upb mat] REAL vec; INIT vec;
INT mat row := LWB mat; INT known row := UPB mat - UPB puzzle + 1;
- build the simultaneous equation to solve #
FOR row FROM LWB puzzle TO UPB puzzle DO
FOR col FROM LWB puzzle[row] TO UPB puzzle[row] DO IF row < UPB puzzle THEN mat[mat row, mat col(row, col)] := 1; mat[mat row, mat col(row+1, col)] := -1; mat[mat row, mat col(row+1, col+1)] := -1; mat row +:= 1 FI; CASE puzzle[row][col] IN (INT value):( mat[known row, mat col(row, col)] := 1; vec[known row] := value; known row +:= 1 ), (CHAR variable):SKIP ESAC OD
OD;
- finally add x - y + z = 0 #
mat[known row, col x] := 1; mat[known row, col y] := -1; mat[known row, col z] := 1;
FORMAT real repr = $g(-5,2)$;
CO # print details of the simultaneous equation being solved # FORMAT
vec repr = $"("n(2 UPB mat-1)(f(real repr)", ")f(real repr)")"$, mat repr = $"("n(1 UPB mat-1)(f(vec repr)", "lx)f(vec repr)")"$;
printf(($"Vec: "l$,vec repr, vec, $l$)); printf(($"Mat: "l$,mat repr, mat, $l$)); END CO
- finally actually solve the equation #
VEC solution vec = vec/mat;
- and wrap up by printing the solution #
FLEX[UPB puzzle]FLEX[0]REAL solution; FOR row FROM LWB puzzle TO UPB puzzle DO
solution[row] := LOC[row]REAL; FOR col FROM LWB puzzle[row] TO UPB puzzle[row] DO solution[row][col] := solution vec[mat col(row, col)] OD; printf(($n(UPB puzzle-row)(4x)$, $x"("f(real repr)")"$, solution[row], $l$))
OD;
FOR var FROM 1 BY 2 TO 5 DO
printf(($5x$,$g$,puzzle[UPB puzzle][var],"=", real repr, solution[UPB puzzle][var]))
OD</lang> Output:
(151.0) (81.00) (70.00) (40.00) (41.00) (29.00) (16.00) (24.00) (17.00) (12.00) ( 5.00) (11.00) (13.00) ( 4.00) ( 8.00) x= 5.00 y=13.00 z= 8.00
AutoHotkey
The main part is this: <lang autohotkey>N1 := 11, N2 := 4, N3 := 40, N4 := 151 Z := (2*N4 - 7*N3 - 8*N2 + 6*N1) / 7 X := (N3 - 2*N1 - Z) / 2 MsgBox,, Pascal's Triangle, %X%`n%Z%</lang> Message box shows:
5.000000 8.000000
The fun part is to create a GUI for entering different values for N1, N2, N3 and N4.
The GUI shows all values in the solved state. <lang autohotkey>;---------------------------------------------------------------------------
- Pascal's triangle.ahk
- by wolf_II
- ---------------------------------------------------------------------------
- http://rosettacode.org/wiki/Pascal's_triangle/Puzzle
- ---------------------------------------------------------------------------
- ---------------------------------------------------------------------------
AutoExecute: ; auto-execute section of the script
- ---------------------------------------------------------------------------
#SingleInstance, Force ; only one instance allowed #NoEnv ; don't check empty variables ;----------------------------------------------------------------------- AppName := "Pascal's triangle" N1 := 11, N2 := 4, N3 := 40, N4 := 151
; monitor MouseMove events OnMessage(0x0200, "WM_MOUSEMOVE")
; GUI Gosub, GuiCreate Gui, Show,, %AppName%
Return
- ---------------------------------------------------------------------------
GuiCreate: ; create the GUI
- ---------------------------------------------------------------------------
Gui, -MinimizeBox Gui, Margin, 8, 8
; 15 edit controls Loop, 5 Loop, % Row := A_Index { xx := 208 + (A_Index - 5) * 50 - (Row - 5) * 25 yy := 8 + (Row - 1) * 22 vv := Row "_" A_Index Gui, Add, Edit, x%xx% y%yy% w50 v%vv% Center ReadOnly -TabStop } GuiControl, -WantReturn, Edit11 GuiControl, -WantReturn, Edit15
; buttons (2 hidden) Gui, Add, Button, x8 w78, &Restart Gui, Add, Button, x+8 wp, &Solve Gui, Add, Button, x+8 wp, &Check Gui, Add, Button, x8 wp, Cle&ar Gui, Add, Button, xp wp Hidden, &Cancel Gui, Add, Button, x+8 wp, &New Gui, Add, Button, xp wp Hidden, &Apply Gui, Add, Button, x+8 wp, E&xit
; status bar Gui, Add, StatusBar
; blue font Gui, Font, bold cBlue GuiControl, Font, Edit11 GuiControl, Font, Edit15 ; falling through
- ---------------------------------------------------------------------------
ButtonRestart: ; restart retaining the blue clues
- ---------------------------------------------------------------------------
Controls(True) ; enable controls Loop, 15 If A_Index Not In 1,4,11,12,14,15 GuiControl,, Edit%A_Index% ; clear GuiControl,, Edit1, %N4% GuiControl,, Edit4, %N3% GuiControl,, Edit12, %N1% GuiControl,, Edit14, %N2% GuiControl,, Edit11, %X% GuiControl,, Edit15, %Z% GreenFont: Gui, Font, bold cGreen GuiControl, Font, Edit1 GuiControl, Font, Edit4 GuiControl, Font, Edit12 GuiControl, Font, Edit14
Return
- ---------------------------------------------------------------------------
ButtonSolve: ; calculate solution
- ---------------------------------------------------------------------------
; N1 := 11 N2 := 4 N3 := 40 N4 := 151 ;----------------------------------------------------------------------- ; Y = X + Z ; 40 = (11+X) + (11+Y) ; A = (11+Y) + (Y+4) ; B = (4+Y) + (4+Z) ; 151 = (40+A) + (A+B) ;----------------------------------------------------------------------- Gosub, GreenFont GuiControl,, Edit15, % Z := Round( (2*N4 - 7*N3 - 8*N2 + 6*N1) / 7 ) GuiControl,, Edit11, % X := Round( (N3 - 2*N1 - Z) / 2 ) ; falling through
- ---------------------------------------------------------------------------
ButtonCheck: ; check the [entry|solution] for errors
- ---------------------------------------------------------------------------
Controls(False) ; disable controls Gui, Submit, NoHide X := 5_1, Z := 5_5 Loop, 5 Loop, % Row := A_Index If (%Row%_%A_Index% = "") %Row%_%A_Index% := 0 GuiControl,, Edit13, % 5_3 := 5_1 + 5_5 GuiControl,, Edit10, % 4_4 := 5_4 + 5_5 GuiControl,, Edit9, % 4_3 := 5_3 + 5_4 GuiControl,, Edit8, % 4_2 := 5_2 + 5_3 GuiControl,, Edit7, % 4_1 := 5_1 + 5_2 GuiControl,, Edit6, % 3_3 := 4_4 + 4_3 GuiControl,, Edit5, % 3_2 := 4_3 + 4_2 GuiControl,, Edit4, % 3_1 := 4_2 + 4_1 GuiControl,, Edit3, % 2_2 := 3_3 + 3_2 GuiControl,, Edit2, % 2_1 := 3_2 + 3_1 GuiControl,, Edit1, % 1_1 := 2_2 + 2_1 Gui, Font, bold cRed If Not 3_1 = N3 GuiControl, Font, Edit4 If Not 1_1 = N4 GuiControl, Font, Edit1
Return
- ---------------------------------------------------------------------------
ButtonClear: ; restart without the blue clues
- ---------------------------------------------------------------------------
X := Z := "" Gosub, ButtonRestart
Return
- ---------------------------------------------------------------------------
ButtonNew: ; enter new numbers for the puzzle
- ---------------------------------------------------------------------------
Gosub, GreenFont Loop, 15 If A_Index Not In 1,4,12,14 GuiControl,, Edit%A_Index% ; clear Controls(False) ; disable controls NewContr(True) ; enable controls for new numbers
Return
- ---------------------------------------------------------------------------
ButtonApply: ; remember the new numbers
- ---------------------------------------------------------------------------
Gui, Submit, NoHide N1 := 5_2, N2 := 5_4, N3 := 3_1, N4 := 1_1 NewContr(False) ; disable controls for new numbers Controls(True) ; enable controls
Return
- ---------------------------------------------------------------------------
ButtonCancel: ; restore the old numbers
- ---------------------------------------------------------------------------
GuiControl,, Edit1, %N4% GuiControl,, Edit4, %N3% GuiControl,, Edit12, %N1% GuiControl,, Edit14, %N2% NewContr(False) ; disable controls for new numbers Controls(True) ; enable controls
Return
- ---------------------------------------------------------------------------
GuiClose:
- ---------------------------------------------------------------------------
GuiEscape:
- ---------------------------------------------------------------------------
ButtonExit:
- ---------------------------------------------------------------------------
; common action ExitApp
Return
- ---------------------------------------------------------------------------
Controls(Bool) { ; [dis|re-en]able some controls
- ---------------------------------------------------------------------------
Enable := Bool ? "+" : "-" Disable := Bool ? "-" : "+"
GuiControl, %Disable%ReadOnly, Edit11 GuiControl, %Disable%ReadOnly, Edit15 GuiControl, %Enable%TabStop, Edit11 GuiControl, %Enable%TabStop, Edit15
GuiControl, %Disable%Default, &Restart GuiControl, %Enable%Default, &Check GuiControl, %Disable%Disabled, &Check GuiControl, %Enable%Disabled, &Restart
}
- ---------------------------------------------------------------------------
NewContr(Bool) { ; [dis|re-en]able control for new numbers
- ---------------------------------------------------------------------------
Enable := Bool ? "+" : "-" Disable := Bool ? "-" : "+"
GuiControl, %Disable%ReadOnly, Edit1 GuiControl, %Disable%ReadOnly, Edit4 GuiControl, %Disable%ReadOnly, Edit12 GuiControl, %Disable%ReadOnly, Edit14
GuiControl, %Enable%TabStop, Edit1 GuiControl, %Enable%TabStop, Edit4 GuiControl, %Enable%TabStop, Edit12 GuiControl, %Enable%TabStop, Edit14
GuiControl, %Enable%Hidden, Button1 GuiControl, %Enable%Hidden, Button2 GuiControl, %Enable%Hidden, Button3 GuiControl, %Enable%Hidden, Button4 GuiControl, %Disable%Hidden, Button5 GuiControl, %Enable%Hidden, Button6 GuiControl, %Disable%Hidden, Button7 GuiControl, %Enable%Hidden, Button8
}
- ---------------------------------------------------------------------------
WM_MOUSEMOVE() { ; monitor MouseMove events
- ---------------------------------------------------------------------------
; display quick help in StatusBar ;----------------------------------------------------------------------- global AppName CurrControl := A_GuiControl IfEqual True,, MsgBox ; dummy
; mouse is over buttons Else If (CurrControl = "&Restart") SB_SetText("restart retaining the blue clues") Else If (CurrControl = "&Solve") SB_SetText("calculate solution") Else If (CurrControl = "&Check") SB_SetText("check if the entries are correct") Else If (CurrControl = "Cle&ar") SB_SetText("restart without the blue clues") Else If (CurrControl = "&New") SB_SetText("enter new numbers for the puzzle") Else If (CurrControl = "E&xit") SB_SetText("exit " AppName)
; delete status bar text Else SB_SetText("")
}</lang>
C
This solution is based upon algebraic necessities, namely that a solution exists when (top - 4(a+b))/7 is integral. It also highlights the type difference between floating point numbers and integers in C.
<lang c> /* Pascal's pyramid solver
* * [top] * [ ] [ ] * [mid] [ ] [ ] * [ ] [ ] [ ] [ ] * [ x ] [ a ] [ y ] [ b ] [ z ] * x + z = y * * This solution makes use of a little bit of mathematical observation, * such as the fact that top = 4(a+b) + 7(x+z) and mid = 2x + 2a + z. */
- include <stdio.h>
- include <math.h>
void pascal(int a, int b, int mid, int top, int* x, int* y, int* z) {
double ytemp = (top - 4 * (a + b)) / 7.; if(fmod(ytemp, 1.) >= 0.0001) { x = 0; return; } *y = ytemp; *x = mid - 2 * a - *y; *z = *y - *x;
} int main() {
int a = 11, b = 4, mid = 40, top = 151; int x, y, z; pascal(a, b, mid, top, &x, &y, &z); if(x != 0) printf("x: %d, y: %d, z: %d\n", x, y, z); else printf("No solution\n");
return 0;
} </lang> Output:
x: 3, y: 13, z: 10
Field equation solver
Treating relations between cells as if they were differential equations, and apply negative feedback to each cell at every iteration step. This is how field equations with boundary conditions are solved numerically. It is, of course, not the optimal solution for this particular task. <lang c>#include <stdio.h>
- define E(x, row, col) x[(row) * ((row) + 1) / 2 + col]
void show(double *x) { int i = 0, j = 0; for (i = 0; i < 5; i++) for (j = 0; j <= i; j++) printf("%4d%c", (int)(.5 + *(x++)), j < i ? ' ':'\n'); }
void iterate(double *v, double *diff) { int i, j; double sum; redo: /* enforce boundary conditions */ E(v, 0, 0) = 151; E(v, 2, 0) = 40; E(v, 4, 1) = 11; E(v, 4, 3) = 4;
/* calculate difference from equilibrium */ for (i = 1; i < 5; i++) { for (j = 0; j <= i; j++) { E(diff, i, j) = 0; if (j < i) E(diff, i, j) += E(v, i - 1, j) - E(v, i, j + 1) - E(v, i, j); if (j) E(diff, i, j) += E(v, i - 1, j - 1) - E(v, i, j - 1) - E(v, i, j); } } for (i = 0; i < 4; i++) for (j = 0; j < i; j++) E(diff, i, j) += E(v, i + 1, j) + E(v, i + 1, j + 1) - E(v, i, j);
E(diff, 4, 2) += E(v, 4, 0) + E(v, 4, 4) - E(v, 4, 2);
/* do feedback, check if we are close enough */ for (i = 0, sum = 0; i < 15; i++){ v[i] += diff[i] / 4; /* 4: scale down the feedback to avoid oscillations */ sum += diff[i] * diff[i]; } printf("dev: %g\n", sum); if (sum < .1) return;
goto redo; }
int main() { double v[15] = {0}, diff[15] = {0}; iterate(v, diff); show(v);
return 0; }</lang>output<lang>dev: 73410 dev: 17968.7 dev: 6388.46 dev: 2883.34 ...<about a million and half iterations later>... dev: 0.116055 dev: 0.107006 dev: 0.09866
151 81 70 40 41 29 16 24 17 12 5 11 13 4 8
</lang>
Clojure
X and Z are the independent variables, so first work bottom up and determine the value of each cell in the form (n0 + n1*X + n2*Z). We'll use a vector [n0 n1 n2] to represent each cell. <lang clojure>(def bottom [ [0 1 0], [11 0 0], [0 1 1], [4 0 0], [0 0 1] ])
(defn plus [v1 v2] (vec (map + v1 v2))) (defn minus [v1 v2] (vec (map - v1 v2))) (defn scale [n v] (vec (map #(* n %) v )))
(defn above [row] (map #(apply plus %) (partition 2 1 row)))
(def rows (reverse (take 5 (iterate above bottom))))</lang> We know the integer value of cells c00 and c20 ( base-0 row then column numbers), so by subtracting these values we get two equations of the form 0=n0+n1*X+n2*Z. <lang clojure>(def c00 (get-in rows [0 0])) (def c20 (get-in rows [2 0]))
(def eqn0 (minus c00 [151 0 0])) (def eqn1 (minus c20 [ 40 0 0]))</lang> In this case, there are only two variables, so solving the system of linear equations is simple. <lang clojure>(defn solve [m]
(assert (<= 1 m 2)) (let [n (- 3 m) v0 (scale (eqn1 n) eqn0) v1 (scale (eqn0 n) eqn1) vd (minus v0 v1)] (assert (zero? (vd n))) (/ (- (vd 0)) (vd m))))
(let [x (solve 1), z (solve 2), y (+ x z)]
(println "x =" x ", y =" y ", z =" z))</lang>
If you want to solve the whole pyramid, just add a call (show-pyramid x z) to the previous let form: <lang clojure>(defn dot [v1 v2] (reduce + (map * v1 v2)))
(defn show-pyramid [x z]
(doseq [row rows] (println (map #(dot [1 x z] %) row)))</lang>
Go
Preferred solution
The following solution is based on several observations on the task:
- The task does not ask for solutions to any generalization of the problem, only this one problem.
- The (twice) linked reference in the task description similarly does not describe any generalizations, but only this one problem.
- The talk page notes that intermediate work need not be coded, and indeed, a number of existing solutions do intermediate work.
- The entire problem is solvable with elementary algebra, thus a decision to actually code any part of the problem is arbitrary.
- Any part coded is not only done frivolously, but represents unnecessary chances for errors.
- Skills needed to develop this solution are prerequisite to any other solution. No other solution is easier.
- The task does not specify program output. This program provides the solution to the problem in a form that is clear to anyone wishing to further adapt the program to their needs.
<lang go>package main
func main() {
// bottom row given: [X] [11] [Y] [4] [Z]
// given sum relation of bricks, // next row up: [x+11] [y+11] [y+4] [z+4] // middle row: [x+y+22] [2y+15] [y+z+8]
// given brick=40 and relation y=x+z, // middle row: [40] [2y+15] [3y-10]
// continuing sum relation of bricks, // next row up: [2y+55] [5y+5] // top brick: [7y+60]
// given top brick = 151, // 7y = 91: y = 13 // x + y = 18: x = 5 // z = y - x: z = 8
}</lang> Alternative solution
Showing a possible data representation of the problem, using the computer to do some arithmetic, and displaying intermediate and final results. <lang go>package main
import "fmt"
// representation of an expression in x, y, and z type expr struct {
x, y, z float64 // coefficients c float64 // constant term
}
// add two expressions func addExpr(a, b expr) expr {
return expr{a.x + b.x, a.y + b.y, a.z + b.z, a.c + b.c}
}
// subtract two expressions func subExpr(a, b expr) expr {
return expr{a.x - b.x, a.y - b.y, a.z - b.z, a.c - b.c}
}
// multiply expression by a constant func mulExpr(a expr, c float64) expr {
return expr{a.x * c, a.y * c, a.z * c, a.c * c}
}
// given a row of expressions, produce the next row up, by the given // sum relation between blocks func addRow(l []expr) []expr {
if len(l) == 0 { panic("wrong") } r := make([]expr, len(l)-1) for i := range r { r[i] = addExpr(l[i], l[i+1]) } return r
}
// given expression b in a variable, and expression a, // take b == 0 and substitute to remove that variable from a. func substX(a, b expr) expr {
if b.x == 0 { panic("wrong") } return subExpr(a, mulExpr(b, a.x/b.x))
}
func substY(a, b expr) expr {
if b.y == 0 { panic("wrong") } return subExpr(a, mulExpr(b, a.y/b.y))
}
func substZ(a, b expr) expr {
if b.z == 0 { panic("wrong") } return subExpr(a, mulExpr(b, a.z/b.z))
}
// given an expression in a single variable, return value of that variable func solveX(a expr) float64 {
if a.x == 0 || a.y != 0 || a.z != 0 { panic("wrong") } return -a.c / a.x
}
func solveY(a expr) float64 {
if a.x != 0 || a.y == 0 || a.z != 0 { panic("wrong") } return -a.c / a.y
}
func solveZ(a expr) float64 {
if a.x != 0 || a.y != 0 || a.z == 0 { panic("wrong") } return -a.c / a.z
}
func main() {
// representation of given information for bottom row r5 := []expr{{x: 1}, {c: 11}, {y: 1}, {c: 4}, {z: 1}} fmt.Println("bottom row:", r5)
// given definition of brick sum relation r4 := addRow(r5) fmt.Println("next row up:", r4) r3 := addRow(r4) fmt.Println("middle row:", r3)
// given relation y = x + z xyz := subExpr(expr{y: 1}, expr{x: 1, z: 1}) fmt.Println("xyz relation:", xyz) // remove z from third cell using xyz relation r3[2] = substZ(r3[2], xyz) fmt.Println("middle row after substituting for z:", r3)
// given cell = 40, b := expr{c: 40} // this gives an xy relation xy := subExpr(r3[0], b) fmt.Println("xy relation:", xy) // substitute 40 for cell r3[0] = b
// remove x from third cell using xy relation r3[2] = substX(r3[2], xy) fmt.Println("middle row after substituting for x:", r3) // continue applying brick sum relation to get top cell r2 := addRow(r3) fmt.Println("next row up:", r2) r1 := addRow(r2) fmt.Println("top row:", r1)
// given top cell = 151, we have an equation in y y := subExpr(r1[0], expr{c: 151}) fmt.Println("y relation:", y) // using xy relation, we get an equation in x x := substY(xy, y) fmt.Println("x relation:", x) // using xyz relation, we get an equation in z z := substX(substY(xyz, y), x) fmt.Println("z relation:", z)
// show final answers fmt.Println("x =", solveX(x)) fmt.Println("y =", solveY(y)) fmt.Println("z =", solveZ(z))
}</lang> Output:
bottom row: [{1 0 0 0} {0 0 0 11} {0 1 0 0} {0 0 0 4} {0 0 1 0}] next row up: [{1 0 0 11} {0 1 0 11} {0 1 0 4} {0 0 1 4}] middle row: [{1 1 0 22} {0 2 0 15} {0 1 1 8}] xyz relation: {-1 1 -1 0} middle row after substituting for z: [{1 1 0 22} {0 2 0 15} {-1 2 0 8}] xy relation: {1 1 0 -18} middle row after substituting for x: [{0 0 0 40} {0 2 0 15} {0 3 0 -10}] next row up: [{0 2 0 55} {0 5 0 5}] top row: [{0 7 0 60}] y relation: {0 7 0 -91} x relation: {1 0 0 -5} z relation: {0 0 -1 8} x = 5 y = 13 z = 8
Haskell
I assume the task is to solve any such puzzle, i.e. given some data
<lang haskell>puzzle = [["151"],["",""],["40","",""],["","","",""],["X","11","Y","4","Z"]]</lang>
one should calculate all possible values that fit. That just means solving a linear system of equations. We use the first three variables as placeholders for X, Y and Z. Then we can produce the matrix of equations:
<lang haskell>triangle n = n * (n+1) `div` 2
coeff xys x = maybe 0 id $ lookup x xys
row n cs = [coeff cs k | k <- [1..n]]
eqXYZ n = [(0, 1:(-1):1:replicate n 0)]
eqPyramid n h = do
a <- [1..h-1] x <- [triangle (a-1) + 1 .. triangle a] let y = x+a return $ (0, 0:0:0:row n [(x,-1),(y,1),(y+1,1)])
eqConst n fields = do
(k,s) <- zip [1..] fields guard $ not $ null s return $ case s of "X" - (0, 1:0:0:row n [(k,-1)]) "Y" - (0, 0:1:0:row n [(k,-1)]) "Z" - (0, 0:0:1:row n [(k,-1)]) _ - (fromInteger $ read s, 0:0:0:row n [(k,1)])
equations :: String - ([Rational], Rational) equations puzzle = unzip eqs where
fields = concat puzzle eqs = eqXYZ n ++ eqPyramid n h ++ eqConst n fields h = length puzzle n = length fields</lang>
To solve the system, any linear algebra library will do (e.g hmatrix). For this example, we assume there are functions decompose for LR-decomposition, kernel to solve the homogenous system and solve to find a special solution for an imhomogenous system. Then
<lang haskell>normalize :: [Rational] - [Integer] normalize xs = [numerator (x * v) | x <- xs] where
v = fromInteger $ foldr1 lcm $ map denominator $ xs
run puzzle = map (normalize . drop 3) $ answer where
(a, m) = equations puzzle lr = decompose 0 m answer = case solve 0 lr a of Nothing - [] Just x - x : kernel lr</lang>
will output one special solution and modifications that lead to more solutions, as in
<lang haskell>*Main run puzzle 151,81,70,40,41,29,16,24,17,12,5,11,13,4,8
- Main run [[""],["2",""],["X","Y","Z"]]
[[3,2,1,1,1,0],[3,0,3,-1,1,2]]</lang>
so for the second puzzle, not only X=1 Y=1 Z=0 is a solution, but also X=1-1=0, Y=1+1=2 Z=0+2=2 etc.
Note that the program doesn't attempt to verify that the puzzle is in correct form.
J
Fixed points in the pyramid are 40 and 151, which I use to check a resulting pyramid for selection:
<lang j>chk=:40 151&-:@(2 4{{."1)</lang>
verb for the base of the pyramid:
<lang j>base=: [,11,+,4,]</lang>
the height of the pyramid:
<lang j>ord=:5</lang>
=> 'chk', 'base' and 'ord' are the knowledge rules abstracted from the problem definition.
The J-sentence that solves the puzzle is:
<lang j> |."2(#~chk"2) 2(+/\)^:(<ord)"1 base/"1>,{ ;~i:28</lang>
151 0 0 0 0 81 70 0 0 0 40 41 29 0 0 16 24 17 12 0 5 11 13 4 8
Get rid of zeros:
<lang j>,.(1+i.5)<@{."0 1{.|."2(#~chk"2) 2(+/\)^:(<ord)"1 base/"1>,{ ;~i:28</lang> or <lang j>,.(<@{."0 1~1+i.@#){.|."2(#~chk"2) 2(+/\)^:(<ord)"1 base/"1>,{ ;~i:28</lang>
+-----------+ |151 | +-----------+ |81 70 | +-----------+ |40 41 29 | +-----------+ |16 24 17 12| +-----------+ |5 11 13 4 8| +-----------+
Mathematica
We assign a variable to each block starting on top with a, then on the second row b,c et cetera. k,m, and o are replaced by X, Y, and Z. We can write the following equations: <lang Mathematica>b+c==a d+e==b e+f==c g+h==d h+i==e i+j==f l+X==g l+Y==h n+Y==i n+Z==j X+Z==Y</lang> And we have the knowns <lang Mathematica>a->151 d->40 l->11 n->4</lang> Giving us 10 equations with 10 unknowns; i.e. solvable. So we can do so by: <lang Mathematica>eqs={a==b+c,d+e==b,e+f==c,g+h==d,h+i==e,i+j==f,l+X==g,l+Y==h,n+Y==i,n+Z==j,Y==X+Z}; knowns={a->151,d->40,l->11,n->4}; Solve[eqs/.knowns,{b,c,e,f,g,h,i,j,X,Y,Z}]</lang> gives back: <lang Mathematica>{{b -> 81, c -> 70, e -> 41, f -> 29, g -> 16, h -> 24, i -> 17, j -> 12, X -> 5, Y -> 13, Z -> 8}}</lang> In pyramid form that would be: <lang Mathematica> 151 81 70 40 41 29 16 24 17 12 5 11 13 4 8</lang>
Oz
<lang oz>%% to compile : ozc -x <file.oz> functor
import
System Application FD Search
define
proc{Quest Root Rules}
proc{Limit Rc Ls} case Ls of nil then skip [] X|Xs then {Limit Rc Xs} case X of N#V then Rc.N =: V [] N1#N2#N3 then Rc.N1 =: Rc.N2 + Rc.N3 end end end
proc {Pyramid R} {FD.tuple solution 15 0#FD.sup R} %% non-negative integers domain %% 01 , pyramid format %% 02 03 %% 04 05 06 %% 07 08 09 10 %% 11 12 13 14 15 R.1 =: R.2 + R.3 %% constraints of Pyramid of numbers R.2 =: R.4 + R.5 R.3 =: R.5 + R.6 R.4 =: R.7 + R.8 R.5 =: R.8 + R.9 R.6 =: R.9 + R.10 R.7 =: R.11 + R.12 R.8 =: R.12 + R.13 R.9 =: R.13 + R.14 R.10 =: R.14 + R.15 {Limit R Rules} %% additional constraints {FD.distribute ff R} end in {Search.base.one Pyramid Root} %% search for solution end
local Root R in {Quest Root [1#151 4#40 12#11 14#4 13#11#15]} %% supply additional constraint rules if {Length Root} >= 1 then R = Root.1 {For 1 15 1 proc{$ I} if {Member I [1 3 6 10]} then {System.printInfo R.I#'\n'} else {System.printInfo R.I#' '} end end } else {System.showInfo 'No solution found.'} end end
{Application.exit 0}
end</lang>
PARI/GP
[ 6y+x+z+4a[2]+4a[4]= 7y +4a[2]+4a[4]] [3y+x+37 ][3y+z+23] [40=x+y+22][ 2y+15][ y+z+8 ] [ x+11 ][y+11 ][y+4 ][z+4 ] [ X][11][ Y][ 4][ Z]
this helped me... <lang parigp> Pascals_triangle_puzzle(topvalue=151,leftsidevalue=40,bottomvalue1=11,bottomvalue2=4) = { y=(topvalue-(4*(bottomvalue1+bottomvalue2)))/7; x=leftsidevalue-(y+2*bottomvalue1); z=y-x; print(x","y","z); } </lang>
I'm thinking of one to solve all puzzles regardless of size and positions. but the objective was to solve this puzzle.
PicoLisp
<lang PicoLisp>(be number (@N @Max)
(@C box 0) (repeat) (or ((@ >= (val (-> @C)) (-> @Max)) T (fail)) ((@N inc (-> @C))) ) )
(be + (@A @B @Sum)
(@ -> @A) (@ -> @B) (@Sum + (-> @A) (-> @B)) )
(be + (@A @B @Sum)
(@ -> @A) (@ -> @Sum) (@B - (-> @Sum) (-> @A)) T (@ ge0 (-> @B)) )
(be + (@A @B @Sum)
(number @A @Sum) (@B - (-> @Sum) (-> @A)) )
- {
151 A B 40 C D E F G H X 11 Y 4 Z
}#
(be puzzle (@X @Y @Z)
(+ @A @B 151) (+ 40 @C @A) (+ @C @D @B) (+ @E @F 40) (+ @F @G @C) (+ @G @H @D) (+ @X 11 @E) (+ 11 @Y @F) (+ @Y 4 @G) (+ 4 @Z @H) (+ @X @Z @Y) )</lang>
Output:
: (? (puzzle @X @Y @Z)) @X=5 @Y=13 @Z=8
Prolog
<lang prolog>:- use_module(library(clpfd)).
puzzle(Ts, X, Y, Z) :-
Ts = [ [151], [_, _], [40, _, _], [_, _, _, _], [X, 11, Y, 4, Z]], Y #= X + Z, triangle(Ts), append(Ts, Vs), Vs ins 0..sup, label(Vs).
triangle([T|Ts]) :- ( Ts = [N|_] -> triangle_(T, N), triangle(Ts) ; true ).
triangle_([], _). triangle_([T|Ts], [A,B|Rest]) :- T #= A + B, triangle_(Ts, [B|Rest]).
% ?- puzzle(_,X,Y,Z). % X = 5, % Y = 13, % Z = 8 ;</lang>
PureBasic
Brute force solution. <lang PureBasic>; Known;
- A.
- [ 151]
- [a ][b ]
- [40][c ][d ]
- [e ][f ][g ][h ]
- [ X][11][ Y][ 4][ Z]
- B.
- Y = X + Z
Procedure.i SolveForZ(x)
Protected a,b,c,d,e,f,g,h,z For z=0 To 20 e=x+11: f=11+(x+z): g=(x+z)+4: h=4+z If e+f=40 c=f+g : d=g+h: a=40+c: b=c+d If a+b=151 ProcedureReturn z EndIf EndIf Next z ProcedureReturn -1
EndProcedure
Define x=-1, z=0, title$="Pascal's triangle/Puzzle in PureBasic" Repeat
x+1 z=SolveForZ(x)
Until z>=0 MessageRequester(title$,"X="+Str(x)+#CRLF$+"Y="+Str(x+z)+#CRLF$+"Z="+Str(z))</lang>
Python
<lang python># Pyramid solver
- [151]
- [ ] [ ]
- [ 40] [ ] [ ]
- [ ] [ ] [ ] [ ]
- [ X ] [ 11] [ Y ] [ 4 ] [ Z ]
- X -Y + Z = 0
def combine( snl, snr ):
cl = {} if isinstance(snl, int): cl['1'] = snl elif isinstance(snl, string): cl[snl] = 1 else: cl.update( snl)
if isinstance(snr, int): n = cl.get('1', 0) cl['1'] = n + snr elif isinstance(snr, string): n = cl.get(snr, 0) cl[snr] = n + 1 else: for k,v in snr.items(): n = cl.get(k, 0) cl[k] = n+v return cl
def constrain(nsum, vn ):
nn = {}
nn.update(vn)
n = nn.get('1', 0)
nn['1'] = n - nsum
return nn
def makeMatrix( constraints ): vmap = set() for c in constraints: vmap.update( c.keys()) vmap.remove('1') nvars = len(vmap) vmap = sorted(vmap) # sort here so output is in sorted order mtx = [] for c in constraints: row = [] for vv in vmap: row.append(float(c.get(vv, 0))) row.append(-float(c.get('1',0))) mtx.append(row)
if len(constraints) == nvars: print 'System appears solvable' elif len(constraints) < nvars: print 'System is not solvable - needs more constraints.' return mtx, vmap
def SolvePyramid( vl, cnstr ):
vl.reverse() constraints = [cnstr] lvls = len(vl) for lvln in range(1,lvls): lvd = vl[lvln] for k in range(lvls - lvln): sn = lvd[k] ll = vl[lvln-1] vn = combine(ll[k], ll[k+1]) if sn is None: lvd[k] = vn else: constraints.append(constrain( sn, vn ))
print 'Constraint Equations:' for cstr in constraints: fset = ('%d*%s'%(v,k) for k,v in cstr.items() ) print ' + '.join(fset), ' = 0'
mtx,vmap = makeMatrix(constraints)
MtxSolve(mtx)
d = len(vmap) for j in range(d): print vmap[j],'=', mtx[j][d]
def MtxSolve(mtx):
# Simple Matrix solver...
mDim = len(mtx) # dimension--- for j in range(mDim): rw0= mtx[j] f = 1.0/rw0[j] for k in range(j, mDim+1): rw0[k] *= f
for l in range(1+j,mDim): rwl = mtx[l] f = -rwl[j] for k in range(j, mDim+1): rwl[k] += f * rw0[k]
# backsolve part --- for j1 in range(1,mDim): j = mDim - j1 rw0= mtx[j] for l in range(0, j): rwl = mtx[l] f = -rwl[j] rwl[j] += f * rw0[j] rwl[mDim] += f * rw0[mDim]
return mtx
p = [ [151], [None,None], [40,None,None], [None,None,None,None], ['X', 11, 'Y', 4, 'Z'] ]
addlConstraint = { 'X':1, 'Y':-1, 'Z':1, '1':0 }
SolvePyramid( p, addlConstraint)</lang>
Output:
Constraint Equations: -1*Y + 1*X + 0*1 + 1*Z = 0 -18*1 + 1*X + 1*Y = 0 -73*1 + 5*Y + 1*Z = 0 System appears solvable X = 5.0 Y = 13.0 Z = 8.0
The Pyramid solver is not restricted to solving for 3 variables, or just this particular pyramid.
Alternative solution using the csp module (based on code by Gustavo Niemeyerby): http://www.fantascienza.net/leonardo/so/csp.zip
<lang python>from csp import Problem
p = Problem() pvars = "R2 R3 R5 R6 R7 R8 R9 R10 X Y Z".split()
- 0-151 is the possible finite range of the variables
p.addvars(pvars, xrange(152)) p.addrule("R7 == X + 11") p.addrule("R8 == Y + 11") p.addrule("R9 == Y + 4") p.addrule("R10 == Z + 4") p.addrule("R7 + R8 == 40") p.addrule("R5 == R8 + R9") p.addrule("R6 == R9 + R10") p.addrule("R2 == 40 + R5") p.addrule("R3 == R5 + R6") p.addrule("R2 + R3 == 151") p.addrule("Y == X + Z") for sol in p.xsolutions():
print [sol[k] for k in "XYZ"]</lang>
Sample output <lang python>[5, 13, 8]</lang>
REXX
<lang rexx> /*REXX program to solve a "Pyramid of Numbers" puzzle. */
/*┌──────────────────────────────────────────────┐ ┌─┘ └─┐ │ answer │ │ mid / │ │ \ / │ │ \ 151 │ │ \ --- --- │ │ 40 --- --- │ │ --- --- --- --- │ │ x 11 y 4 z │ │ / \ │ │ / \ │ │ / \ │ │ B D │ │ │ └─┐ ┌─┘ └──────────────────────────────────────────────┘*/
arg x b y d z mid answer . /*get some values, others, just X*/
top=answer- 4*b - 4*d /*calculate the top # - constants*/
middle=mid- 2*b /*calculate the mod # - constants*/
do x=-top to top do y=-top to top if x+y\==middle then iterate /*40 = x+2B+Y -or- 40-2*11 =x+y*/ y6=y*6 /*calculate a short cut. */ do z=-top to top if z\==y-x then iterate /*z has to equal y-x (y=x+z) */ if x+y6+z==top then say 'x=' x ' y=' y ' z=' z end end end
</lang> Output for the following input: x 11 y 4 z 40 151
x= 5 y= 13 z= 8
Ruby
uses Reduced row echelon form#Ruby <lang ruby>require 'rref'
pyramid = [
[ 151], [nil,nil], [40,nil,nil], [nil,nil,nil,nil], ["x", 11,"y", 4,"z"]
] p pyramid equations = 1,-1,1,0 # y = x + z
def parse_equation(str)
eqn = [0] * 4 lhs, rhs = str.split("=") eqn[3] = rhs.to_i for term in lhs.split("+") case term when "x": eqn[0] += 1 when "y": eqn[1] += 1 when "z": eqn[2] += 1 else eqn[3] -= term.to_i end end eqn
end
-2.downto(-5) do |row|
pyramid[row].each_index do |col| val = pyramid[row][col] sum = "%s+%s" % [pyramid[row+1][col].to_s, pyramid[row+1][col+1].to_s] if val.nil? pyramid[row][col] = sum else equations << parse_equation(sum + "=#{val}") end end
end
reduced = convert_to(reduced_row_echelon_form(equations), :to_i)
for eqn in reduced
if eqn[0] + eqn[1] + eqn[2] != 1 fail "no unique solution! #{equations.inspect} ==> #{reduced.inspect}" elsif eqn[0] == 1: x = eqn[3] elsif eqn[1] == 1: y = eqn[3] elsif eqn[2] == 1: z = eqn[3] end
end
puts "x == #{x}" puts "y == #{y}" puts "z == #{z}"
answer = [] for row in pyramid
answer << row.collect {|cell| eval cell.to_s}
end p answer</lang>
[[151], [nil, nil], [40, nil, nil], [nil, nil, nil, nil], ["x", 11, "y", 4, "z"]] x == 5 y == 13 z == 8 [[151], [81, 70], [40, 41, 29], [16, 24, 17, 12], [5, 11, 13, 4, 8]]
SystemVerilog
We can view this as a problem of generating a set of random numbers that satisfy the constraints. Because there is only one solution, the result isn't very random... <lang SystemVerilog>program main;
class Triangle; rand bit [7:0] a,b,c,d,e,f,g,h,X,Y,Z;
function new(); randomize; $display(" [%0d]", 151); $display(" [%0d][%0d]", a, b); $display(" [%0d][%0d][%0d]", 40,c,d); $display(" [%0d][%0d][%0d][%0d]", e,f,g,h); $display(" [%0d][%0d][%0d][%0d][%0d]",X,11,Y,4,Z); endfunction
constraint structure { 151 == a + b;
a == 40 + c; b == c + d;
40 == e + f; c == f + g; d == g + h;
e == X + 11; f == 11 + Y; g == Y + 4; h == 4 + Z; };
constraint extra { Y == X + Z; };
endclass
Triangle answer = new;
endprogram</lang>
[151] [81][70] [40][41][29] [16][24][17][12] [5][11][13][4][8]
Tcl
using code from Reduced row echelon form#Tcl <lang tcl>package require Tcl 8.5 namespace path ::tcl::mathop
set pyramid {
{151.0 "" "" "" ""} {"" "" "" "" ""} {40.0 "" "" "" ""} {"" "" "" "" ""} {x 11.0 y 4.0 z}
}
set equations Template:1 -1 1 0
proc simplify {terms val} {
set vars {0 0 0} set x 0 set y 1 set z 2 foreach term $terms { switch -exact -- $term { x - y - z { lset vars [set $term] [+ 1 [lindex $vars [set $term]]] } default { set val [- $val $term] } } } return [concat $vars $val]
}
for {set row [+ [llength $pyramid] -2]} {$row >= 0} {incr row -1} {
for {set cell 0} {$cell <= $row} {incr cell } {
set sum [concat [lindex $pyramid [+ 1 $row] $cell] [lindex $pyramid [+ 1 $row] [+ 1 $cell]]] if {[set val [lindex $pyramid $row $cell]] ne ""} {
lappend equations [simplify $sum $val]
} else {
lset pyramid $row $cell $sum } }
}
set solution [toRREF $equations] foreach row $solution {
lassign $row a b c d if {$a + $b + $c > 1} { error "problem does not have a unique solution" } if {$a} {set x $d} if {$b} {set y $d} if {$c} {set z $d}
} puts "x=$x" puts "y=$y" puts "z=$z"
foreach row $pyramid {
set newrow {} foreach cell $row { if {$cell eq ""} { lappend newrow "" } else { lappend newrow [expr [join [string map [list x $x y $y z $z] $cell] +]] } } lappend solved $newrow
} print_matrix $solved</lang>
x=5.0 y=13.0 z=8.0 151.0 81.0 70.0 40.0 41.0 29.0 16.0 24.0 17.0 12.0 5.0 11.0 13.0 4.0 8.0