Wiki Agenda Contact Version française

Maximize product of adjacent numbers in a matrix

Find in a matrix the four numbers, adjacent in the same direction, that maximize their product.


Authors: Sylvain Dailler

Topics: Matrices

Tools: Why3

References: Project Euler

See also: Maximal sum in a matrix / Euler Project problem 11, SPARK/Ada version

see also the index (by topic, by tool, by reference, by year)


Euler Project, problem11

In the 20×20 grid below, four numbers along a diagonal line have been marked in red.

08 02 22 97 38 15 00 40 00 75 04 05 07 78 52 12 50 77 91 08
49 49 99 40 17 81 18 57 60 87 17 40 98 43 69 48 04 56 62 00
81 49 31 73 55 79 14 29 93 71 40 67 53 88 30 03 49 13 36 65
52 70 95 23 04 60 11 42 69 24 68 56 01 32 56 71 37 02 36 91
22 31 16 71 51 67 63 89 41 92 36 54 22 40 40 28 66 33 13 80
24 47 32 60 99 03 45 02 44 75 33 53 78 36 84 20 35 17 12 50
32 98 81 28 64 23 67 10 26 38 40 67 59 54 70 66 18 38 64 70
67 26 20 68 02 62 12 20 95 63 94 39 63 08 40 91 66 49 94 21
24 55 58 05 66 73 99 26 97 17 78 78 96 83 14 88 34 89 63 72
21 36 23 09 75 00 76 44 20 45 35 14 00 61 33 97 34 31 33 95
78 17 53 28 22 75 31 67 15 94 03 80 04 62 16 14 09 53 56 92
16 39 05 42 96 35 31 47 55 58 88 24 00 17 54 24 36 29 85 57
86 56 00 48 35 71 89 07 05 44 44 37 44 60 21 58 51 54 17 58
19 80 81 68 05 94 47 69 28 73 92 13 86 52 17 77 04 89 55 40
04 52 08 83 97 35 99 16 07 97 57 32 16 26 26 79 33 27 98 66
88 36 68 87 57 62 20 72 03 46 33 67 46 55 12 32 63 93 53 69
04 42 16 73 38 25 39 11 24 94 72 18 08 46 29 32 40 62 76 36
20 69 36 41 72 30 23 88 34 62 99 69 82 67 59 85 74 04 36 16
20 73 35 29 78 31 90 01 74 31 49 71 48 86 81 16 23 57 05 54
01 70 54 71 83 51 54 69 16 92 33 48 61 43 52 01 89 19 67 48

The product of these numbers is 26 × 63 × 78 × 14 = 1788696.

What is the greatest product of four adjacent numbers in the same direction (up, down, left, right, or diagonally) in the 20×20 grid?

module ProductFour

use int.Int
use ref.Ref
use matrix.Matrix
use option.Option

type direction =
     | Left_bottom
     | Right_bottom
     | Bottom
     | Right

Direction of the product checked

function left_diag (m: matrix int) (i: int) (j: int) : option int =
	 if (i < m.rows /\ j >= 0 /\ i >= 3 /\ j < m.columns - 3) then
	    Some (((m.elts i j) * (m.elts (i - 1) (j + 1)) * (m.elts (i - 2) (j + 2)) * (m.elts (i-3) (j+3))))
	 else
	    None

Math functions about the result of a product. Incomplete product generate None

function right_diag (m: matrix int) (i: int) (j: int) : option int =
	 if (i < m.rows - 3 /\ i >= 0 /\ j < m.columns - 3 /\ j >= 0) then
	    Some (((m.elts i j) * (m.elts (i + 1) (j + 1)) * (m.elts (i + 2) (j + 2)) * (m.elts (i+3) (j+3))))
	 else
	    None

function line (m: matrix int) (i: int) (j: int) : option int =
	 if (0 <= j < m.columns /\ i >= 0 /\ i < m.rows - 3) then
	    Some ((m.elts i j) * (m.elts (i+1) j) * (m.elts (i+2) j) * (m.elts (i+3) j))
	 else
	    None

function column (m: matrix int) (i: int) (j: int) : option int =
	 if (0 <= i < m.rows /\ j >= 0 /\ j < m.columns - 3) then
	    Some ((m.elts i j) * (m.elts i (j+1)) * (m.elts i (j+2)) * (m.elts i (j + 3)))
	 else
	    None

let right_diag_c m i j : option int =
    ensures {result = right_diag m i j}
    if (i < m.rows - 3 && i >= 0 && j < m.columns - 3 && j >= 0) then
       Some (((get m i j) * (get m (i + 1) (j + 1)) * (get m (i + 2) (j + 2)) * (get m (i+3) (j+3))))
    else
       None

Computational functions for the product in each direction

let left_diag_c m i j : option int =
    ensures {result = left_diag m i j}
    if (i < m.rows && j >= 0 && i >= 3 && j < m.columns - 3) then
       Some (((get m i j) * (get m (i - 1) (j + 1)) * (get m (i - 2) (j + 2)) * (get m (i-3) (j+3))))
    else
       None

let line_c m i j : option int =
    ensures {result = line m i j}
    if (0 <= j && j < m.columns &&  i >= 0 && i < m.rows - 3) then
       Some ((get m i j) * (get m (i+1) j) * (get m (i+2) j) * (get m (i+3) j))
    else
       None

let column_c m i j : option int =
    ensures {result = column m i j}
    if (0 <= i && i < m.rows && j >= 0 && j < m.columns - 3) then
       Some ((get m i j) * (get m i (j+1)) * (get m i (j+2)) * (get m i (j + 3)))
    else
       None

(* function compute4 (m: matrix int) (i: int) (j: int) (d: direction) : option int = *)
(*     match d with *)
(*     | Left_bottom -> left_diag m i j *)
(*     | Right_bottom -> right_diag m i j *)
(*     | Bottom -> column m i j *)
(*     | Right -> line m i j *)
(*     end *)

(* TODO Failed attempt at pattern matching above. Combination of mathematical product result functions *)
function compute4 (m: matrix int) (i: int) (j: int) (d: direction) : option int =
	if (d = Left_bottom) then left_diag m i j
	else if (d = Right_bottom) then right_diag m i j
	else if (d = Bottom) then column m i j
	else if (d = Right) then line m i j
	else None

(* predicate is_valid (m: matrix int) (i: int) (j: int) (d: direction) = *)
(*   match d with *)
(*   | Left_bottom -> i < m.rows /\ j >= 0 /\ i >= 3 /\ j < m.columns - 3 *)
(*   | Right_bottom -> i >= 0 /\ j >= 0 /\ i < m.rows - 3 /\ j < m.columns - 3 *)
(*   | Bottom -> 0 <= i /\ i < m.rows /\ j >= 0 /\ j < m.columns - 3 *)
(*   | Right -> 0 <= j /\ j < m.columns /\ i >= 0 /\ i < m.rows - 3 *)
(*   end *)

A product is_valid if each elements of the product is in the matrix

let find_max (m: matrix int) =
    requires {m.rows > 4 /\ m.columns > 4}
    ensures {forall i j d. match (compute4 m i j d) with
    	      	     	     | None -> true
			     | Some v -> v <= result
			   end}
    ensures {exists i j d. Some result = compute4 m i j d}

Return the maximum product of 4 for matrix m

  let cur_max = ref (
      match (line_c m 0 0) with
      | None -> 0 (* TODO should not happen *)
      | Some v -> v
      end) in
  let cur_i = ref 0 in
  let cur_j = ref 0 in
  let cur_d = ref Right in
  for i = 0 to (m.rows - 1) do
(* Cur_max is greater than each product already seen *)
    invariant { forall i' j' d. (0 <= i' < i /\ 0 <= j' < m.columns) ->
    	      match (compute4 m i' j' d) with
	      | None -> true
	      | Some v -> v <= !cur_max
	      end}
(* cur_max is actually the product at (cur_i, cur_j, cur_d) *)
    invariant {Some !cur_max = compute4 m !cur_i !cur_j !cur_d}
    for j = 0 to (m.columns - 1) do
(* cur_max is actually the product at (cur_i, cur_j, cur_d) *)
    invariant {Some !cur_max = compute4 m !cur_i !cur_j !cur_d}
(* Cur_max is greater than each product already seen *)
    invariant { forall i' j' d. ((i' = i /\ 0 <= j' /\ j' < j) \/ (0 <= i' < i /\ 0 <= j' < m.columns)) ->
    	      match (compute4 m i' j' d) with
	      | None -> true
	      | Some v -> v <= !cur_max
	      end}
(* Computation of the product for each direction *)
	 match (left_diag_c m i j) with
	 | None -> ()
	 | Some v ->
	   if (v > !cur_max) then
	   begin
		cur_max := v;
	      	cur_i := i;
	      	cur_j := j;
	      	cur_d := Left_bottom;
           end
	 end;

Current max and element of the matrix for which it is attained

	 match (right_diag_c m i j) with
	 | None -> ()
	 | Some v ->
	   if (v > !cur_max) then
	   begin
		cur_max := v;
	      	cur_i := i;
	      	cur_j := j;
	      	cur_d := Right_bottom;
           end
	 end;

  	 match (line_c m i j) with
	 | None -> ()
	 | Some v ->
	   if (v > !cur_max) then
	   begin
		cur_max := v;
	      	cur_i := i;
	      	cur_j := j;
	      	cur_d := Right;
           end
	 end;

	 match (column_c m i j) with
	 | None -> ()
	 | Some v ->
	   if (v > !cur_max) then
	   begin
		cur_max := v;
	      	cur_i := i;
	      	cur_j := j;
	      	cur_d := Bottom;
           end
	 end;
    done;
  done;

  assert { Some !cur_max = compute4 m !cur_i !cur_j !cur_d};
  !cur_max;;

Assert implying directly the postcondition

end

download ZIP archive

Why3 Proof Results for Project "euler011"

Theory "euler011.ProductFour": fully verified

ObligationsAlt-Ergo 1.01Alt-Ergo 2.0.0CVC4 1.5
VC right_diag_c0.02------
VC left_diag_c0.03------
VC line_c0.03------
VC column_c0.02------
VC find_max---------
split_goal_right
VC find_max.0---------
split_goal_right
VC find_max.0.0------0.02
VC find_max.0.1---------
introduce_premises
VC find_max.0.1.0---------
inline_goal
VC find_max.0.1.0.00.02------
VC find_max.1------0.03
VC find_max.20.01------
VC find_max.30.02------
VC find_max.40.01------
VC find_max.50.01------
VC find_max.60.01------
VC find_max.7---------
split_goal_right
VC find_max.7.0------0.05
VC find_max.7.1---------
introduce_premises
VC find_max.7.1.0---------
inline_goal
VC find_max.7.1.0.00.10------
VC find_max.80.01------
VC find_max.9---------
split_goal_right
VC find_max.9.00.13------
VC find_max.9.1------0.77
VC find_max.100.01------
VC find_max.110.23------
VC find_max.120.02------
VC find_max.130.24------
VC find_max.140.01------
VC find_max.150.35------
VC find_max.160.01------
VC find_max.17---------
split_goal_right
VC find_max.17.0---------
introduce_premises
VC find_max.17.0.0---------
inline_goal
VC find_max.17.0.0.00.050.03---
VC find_max.17.1------0.06
VC find_max.180.01------
VC find_max.190.20------
VC find_max.200.01------
VC find_max.21---------
split_goal_right
VC find_max.21.0------0.11
VC find_max.21.10.14------
VC find_max.220.01------
VC find_max.230.26------
VC find_max.240.01------
VC find_max.25---------
split_goal_right
VC find_max.25.0------0.10
VC find_max.25.1---------
introduce_premises
VC find_max.25.1.0---------
inline_goal
VC find_max.25.1.0.00.09------
VC find_max.260.01------
VC find_max.270.31------
VC find_max.280.02------
VC find_max.29---------
split_goal_right
VC find_max.29.0------0.06
VC find_max.29.10.28------
VC find_max.300.02------
VC find_max.31---------
split_goal_right
VC find_max.31.0---------
introduce_premises
VC find_max.31.0.0---------
inline_goal
VC find_max.31.0.0.00.10------
VC find_max.31.1------0.14
VC find_max.320.02------
VC find_max.330.35------
VC find_max.340.01------
VC find_max.350.31------
VC find_max.360.02------
VC find_max.370.35---0.06
VC find_max.380.01------
VC find_max.39---------
split_goal_right
VC find_max.39.0------0.06
VC find_max.39.1---------
introduce_premises
VC find_max.39.1.0---------
inline_goal
VC find_max.39.1.0.00.12------
VC find_max.400.01------
VC find_max.41---------
split_goal_right
VC find_max.41.00.04------
VC find_max.41.1------0.16
VC find_max.420.01------
VC find_max.430.26------
VC find_max.440.01------
VC find_max.45---------
split_goal_right
VC find_max.45.00.12------
VC find_max.45.1------0.05
VC find_max.460.02------
VC find_max.470.27---0.06
VC find_max.480.02---0.05
VC find_max.490.28------
VC find_max.500.01------
VC find_max.51---------
split_goal_right
VC find_max.51.0---------
introduce_premises
VC find_max.51.0.0---------
inline_goal
VC find_max.51.0.0.00.120.06---
VC find_max.51.1------0.07
VC find_max.520.01------
VC find_max.530.14---0.04
VC find_max.540.02------
VC find_max.55---------
split_goal_right
VC find_max.55.0------0.11
VC find_max.55.10.29---0.05
VC find_max.560.01------
VC find_max.57---------
split_goal_right
VC find_max.57.0---------
introduce_premises
VC find_max.57.0.0---------
inline_goal
VC find_max.57.0.0.00.060.04---
VC find_max.57.1------0.35
VC find_max.580.01------
VC find_max.59---------
split_goal_right
VC find_max.59.0------0.06
VC find_max.59.1---------
introduce_premises
VC find_max.59.1.0---------
inline_goal
VC find_max.59.1.0.00.07------
VC find_max.600.01------
VC find_max.610.22------
VC find_max.620.02------
VC find_max.63---------
split_goal_right
VC find_max.63.0------0.12
VC find_max.63.1---------
introduce_premises
VC find_max.63.1.0---------
inline_goal
VC find_max.63.1.0.00.10------
VC find_max.640.01------
VC find_max.650.38------
VC find_max.660.02------
VC find_max.67---------
split_goal_right
VC find_max.67.0------0.12
VC find_max.67.1---------
introduce_premises
VC find_max.67.1.0---------
inline_goal
VC find_max.67.1.0.00.09------
VC find_max.680.02------
VC find_max.69---------
split_goal_right
VC find_max.69.0------0.06
VC find_max.69.1---------
introduce_premises
VC find_max.69.1.0---------
inline_goal
VC find_max.69.1.0.00.13------
VC find_max.700.01------
VC find_max.710.21---0.04
VC find_max.720.02------
VC find_max.730.22------
VC find_max.740.01------
VC find_max.750.23---0.06
VC find_max.760.02------
VC find_max.770.30------
VC find_max.780.02------
VC find_max.79---------
split_goal_right
VC find_max.79.0------0.07
VC find_max.79.1---------
introduce_premises
VC find_max.79.1.0---------
inline_goal
VC find_max.79.1.0.00.09------
VC find_max.800.02------
VC find_max.81---------
split_goal_right
VC find_max.81.0------0.04
VC find_max.81.1---------
introduce_premises
VC find_max.81.1.0---------
inline_goal
VC find_max.81.1.0.00.16------
VC find_max.820.02------
VC find_max.83---------
split_goal_right
VC find_max.83.0---------
introduce_premises
VC find_max.83.0.0---------
inline_goal
VC find_max.83.0.0.00.13------
VC find_max.83.1------0.06
VC find_max.840.03------
VC find_max.85---------
split_goal_right
VC find_max.85.0------0.07
VC find_max.85.1---------
introduce_premises
VC find_max.85.1.0---------
inline_goal
VC find_max.85.1.0.00.21------
VC find_max.860.02------
VC find_max.870.57---0.13
VC find_max.880.01------
VC find_max.89---------
split_goal_right
VC find_max.89.0---------
introduce_premises
VC find_max.89.0.0---------
inline_goal
VC find_max.89.0.0.00.12------
VC find_max.89.1------0.06
VC find_max.900.02------
VC find_max.91---------
split_goal_right
VC find_max.91.0------0.17
VC find_max.91.1---------
introduce_premises
VC find_max.91.1.0---------
inline_goal
VC find_max.91.1.0.00.200.08---
VC find_max.920.01---0.04
VC find_max.93---------
split_goal_right
VC find_max.93.0------0.12
VC find_max.93.1---------
introduce_premises
VC find_max.93.1.0---------
inline_goal
VC find_max.93.1.0.00.280.07---
VC find_max.940.01---0.03
VC find_max.95---------
split_goal_right
VC find_max.95.0------0.06
VC find_max.95.10.33------
VC find_max.960.02---0.03
VC find_max.970.35---0.06
VC find_max.980.01---0.03
VC find_max.99---------
split_goal_right
VC find_max.99.0------0.07
VC find_max.99.1---------
introduce_premises
VC find_max.99.1.0---------
inline_goal
VC find_max.99.1.0.00.08------
VC find_max.1000.02---0.04
VC find_max.101---------
split_goal_right
VC find_max.101.0------0.06
VC find_max.101.1---------
introduce_premises
VC find_max.101.1.0---------
inline_goal
VC find_max.101.1.0.00.140.08---
VC find_max.1020.02---0.04
VC find_max.103---------
split_goal_right
VC find_max.103.0---------
introduce_premises
VC find_max.103.0.0---------
inline_goal
VC find_max.103.0.0.00.110.06---
VC find_max.103.1------0.13
VC find_max.1040.02---0.03
VC find_max.105---------
split_goal_right
VC find_max.105.0---------
introduce_premises
VC find_max.105.0.0---------
inline_goal
VC find_max.105.0.0.00.140.07---
VC find_max.105.1------0.10
VC find_max.1060.01---0.03
VC find_max.107---------
split_goal_right
VC find_max.107.0------0.08
VC find_max.107.1---------
introduce_premises
VC find_max.107.1.0---------
inline_goal
VC find_max.107.1.0.00.100.04---
VC find_max.1080.02---0.03
VC find_max.109---------
split_goal_right
VC find_max.109.0---------
introduce_premises
VC find_max.109.0.0---------
inline_goal
VC find_max.109.0.0.00.110.07---
VC find_max.109.1------0.14
VC find_max.1100.01---0.03
VC find_max.111---------
split_goal_right
VC find_max.111.0---------
introduce_premises
VC find_max.111.0.0---------
inline_goal
VC find_max.111.0.0.00.090.06---
VC find_max.111.1------0.14
VC find_max.1120.01---0.02
VC find_max.1130.10------
VC find_max.1140.02---0.03
VC find_max.1150.31---0.16
VC find_max.1160.01---0.01
VC find_max.117---------
split_goal_right
VC find_max.117.0---0.05---
VC find_max.117.10.13---0.92
VC find_max.1180.02---0.02
VC find_max.1190.29---0.05
VC find_max.1200.02------
VC find_max.1210.33------
VC find_max.1220.01---0.04
VC find_max.123---------
split_goal_right
VC find_max.123.0------0.13
VC find_max.123.10.34------
VC find_max.1240.01---0.01
VC find_max.125---------
split_goal_right
VC find_max.125.0---------
introduce_premises
VC find_max.125.0.0---------
inline_goal
VC find_max.125.0.0.00.040.04---
VC find_max.125.1------0.05
VC find_max.1260.01---0.02
VC find_max.127---------
split_goal_right
VC find_max.127.00.26------
VC find_max.127.1------0.13
VC find_max.1280.01---0.02
VC find_max.1290.13---0.05
VC find_max.1300.01---0.03
VC find_max.1310.25------
VC find_max.1320.02---0.03
VC find_max.133---------
split_goal_right
VC find_max.133.0------0.08
VC find_max.133.10.40---0.07
VC find_max.1340.01---0.04
VC find_max.135---------
split_goal_right
VC find_max.135.00.20---0.06
VC find_max.135.1------0.05
VC find_max.1360.02---0.03
VC find_max.1370.37---0.08
VC find_max.1380.02---0.02
VC find_max.139---------
split_goal_right
VC find_max.139.0------0.16
VC find_max.139.1---------
introduce_premises
VC find_max.139.1.0---------
inline_goal
VC find_max.139.1.0.00.200.06---
VC find_max.1400.01---0.03
VC find_max.141---------
split_goal_right
VC find_max.141.0---------
introduce_premises
VC find_max.141.0.0---------
inline_goal
VC find_max.141.0.0.00.130.07---
VC find_max.141.1------0.12
VC find_max.1420.02---0.03
VC find_max.143---------
split_goal_right
VC find_max.143.0---------
introduce_premises
VC find_max.143.0.0---------
inline_goal
VC find_max.143.0.0.00.120.06---
VC find_max.143.1------0.06
VC find_max.1440.01---0.02
VC find_max.145---------
split_goal_right
VC find_max.145.0---------
introduce_premises
VC find_max.145.0.0---------
inline_goal
VC find_max.145.0.0.00.080.06---
VC find_max.145.1------0.14
VC find_max.1460.02---0.03
VC find_max.147---------
split_goal_right
VC find_max.147.0---------
introduce_premises
VC find_max.147.0.0---------
inline_goal
VC find_max.147.0.0.00.130.07---
VC find_max.147.1------0.15
VC find_max.1480.01---0.01
VC find_max.149---------
split_goal_right
VC find_max.149.00.12------
VC find_max.149.1------0.04
VC find_max.1500.02---0.03
VC find_max.151---------
split_goal_right
VC find_max.151.00.28---0.05
VC find_max.151.1------0.07
VC find_max.1520.01---0.02
VC find_max.153---------
split_goal_right
VC find_max.153.0------0.04
VC find_max.153.10.17------
VC find_max.1540.01---0.03
VC find_max.155---------
split_goal_right
VC find_max.155.0------0.10
VC find_max.155.10.22---0.04
VC find_max.1560.01---0.03
VC find_max.157---------
split_goal_right
VC find_max.157.0---------
introduce_premises
VC find_max.157.0.0---------
inline_goal
VC find_max.157.0.0.00.100.06---
VC find_max.157.1------0.12
VC find_max.1580.01---0.03
VC find_max.159---------
split_goal_right
VC find_max.159.0---------
introduce_premises
VC find_max.159.0.0---------
inline_goal
VC find_max.159.0.0.00.120.06---
VC find_max.159.1------0.10
VC find_max.1600.01---0.01
VC find_max.161---------
split_goal_right
VC find_max.161.0------0.05
VC find_max.161.10.14---0.06
VC find_max.1620.01---0.04
VC find_max.163---------
split_goal_right
VC find_max.163.0---------
introduce_premises
VC find_max.163.0.0---------
inline_goal
VC find_max.163.0.0.00.080.06---
VC find_max.163.1------0.10
VC find_max.1640.01---0.01
VC find_max.165---------
split_goal_right
VC find_max.165.00.16---0.06
VC find_max.165.1------0.68
VC find_max.1660.01---0.06
VC find_max.1670.01---0.01
VC find_max.1680.01---0.02
VC find_max.1690.01---0.01
VC find_max.1700.120.05---
VC find_max.1710.01---0.02
VC find_max.1720.01---0.02