#!/usr/bin/perl -w # Matrix chain multiplication # Author: Chao-Kuei Hung http://www.cyut.edu.tw/~ckhung # License: GPL # # This program is part of algotutor. Yet it can also run as a # stand-alone text mode program that requires no other modules. # When used as a stand-alone program, it requires as a list # of arguments alternating numbers and matrix names, like this: # matrixchain -e ansi 32 A 35 B 24 C 30 D 36 E 25 F 40 G 34 H 35 my (@name, $best); sub print_expr { my ($i, $j) = @_; if ($i >= $j) { print($name[$i]); } else { print("("); print_expr($i,$best->[$i][$j]{cut}); print(" * "); print_expr($best->[$i][$j]{cut}+1,$j); print(")"); } } sub matrixchain { my ($chain, $can) = @_; my (@dim, $show, $left, $size, $i, $expr); my ($cell) = sub { return $show->cell( ($_[0]+1) % ($#name+2), ($_[1]+$#name+2) % ($#name+2) ); }; while ($#$chain > 0) { my ($d, $n) = splice @$chain, 0, 2; print " $n:${d}x$chain->[0]"; push @dim, $d; push @name, $n; } print "\n"; push @dim, @$chain; if (ref $can) { $show = Board->new(-canvas=>$can, -width=>$#name+2, -height=>$#name+2, -node_opts=>{ -size=>Vector->new(50,40), -shape=>"rectangle" } ); map { $cell->(-1,$_)->configure(-text=>$name[$_], -status=>"done"); } 0..$#name; map { $cell->($_,-1)->configure(-text=>$name[$_], -status=>"done"); } 0..$#name; $expr = $can->createText(@{ $show->rc2xy($#name+2, int(($#name+1)/2) ) }, -justify=>"center", -text=>"expr"); } for ($left=0; $left<=$#name; ++$left) { $best->[$left][$left] = { val => 0 }; if (ref $can) { $cell->($left,$left)->configure(-text=>0, -status=>"done"); for ($i=$left+1; $i<=$#name; ++$i) { $cell->($i,$left)->configure(-status=>"hidden"); } } } $can->set_mark(2) if ref $can; for ($size=1; $size<=$#name; ++$size) { for ($left=0; $left<=$#name-$size; ++$left) { $cell->($left,$left+$size)->configure(-text=>"?", -status=>"focus") if ref $can; my ($s, $t); $best->[$left][$left+$size] = { val => 1e99 }; for ($i=0; $i<$size; ++$i) { $s = $best->[$left][$left+$i]{val} . " + " . $best->[$left+$i+1][$left+$size]{val} . " + " . $dim[$left] . " * " . $dim[$left+$i+1] . " * " . $dim[$left+$size+1]; $t = eval $s; if (ref $can) { $can->itemconfigure($expr, -text=>"$t = $s"); $cell->($left, $left+$i)->configure(-status=>"focus"); $cell->($left+$i+1, $left+$size)->configure(-status=>"focus"); if ($i >= 1) { $cell->($left, $left+$i-1)->configure(-status=>"done"); $cell->($left+$i, $left+$size)->configure(-status=>"done"); } $can->set_mark(1); } if ($t < $best->[$left][$left+$size]{val}) { $best->[$left][$left+$size] = { val=>$t, cut=>$left+$i }; } } if (ref $can) { $t = $best->[$left][$left+$size]; $t = $t->{val} . "\n" . $name[$t->{cut}] . "|" . $name[$t->{cut}+1]; $cell->($left,$left+$size)->configure(-text=>$t, -status=>"done"); $can->itemconfigure($expr, -text=>""); $cell->($left, $left+$size-1)->configure(-status=>"done"); $cell->($left+$size, $left+$size)->configure(-status=>"done"); $can->set_mark(2); } } } for ($i=1; $i<=$#name; ++$i) { printf " %2s ", $name[$i]; } print "\n"; for ($left=0; $left<$#name; ++$left) { print " " x $left; for ($i=$left+1; $i<=$#name; ++$i) { my ($t) = sprintf "%d:%s|%s", $best->[$left][$i]{val}, $name[$best->[$left][$i]{cut}], $name[$best->[$left][$i]{cut}+1]; printf "%11s", $t; } print " $name[$left]\n"; } print_expr(0, $#name); print "\n"; } if ($0 =~ /matrixchain$/) { my ($chain) = $#ARGV >= 0 ? \@ARGV : [qw(32 A 35 B 24 C 30 D 36 E 25 F 40 G 34 H 35)]; matrixchain($chain); } 1;