Easier way to make permutation diagrams?
I was tempted to use a matrix of nodes
but my solution borrows from Jake's awesome code in Chinese checkers board using TikZ which effectively sets up a matrix of nodes
manually.
This solution needs the tikz
and the xstring
packages.
The syntax
You use the code below as, for example
\drawpermutate{blue/{1/1,3/2}/left,red/{3/4,2/3}/right}
\drawpermutate{green/{1/1,3/5,4/2}/left,orange/{3/4,2/3}/right}
You'll notice that each part of the argument has three parts to it:
colour
: this should be self explanatory{list of coordinates}
: note that this needs to be grouped in its own{}
, and each ordered pair(x,y)
needs to be written asx/y
left or right
: this can either beleft
orright
depending on how you want the legend to be displayed. Actually, it can either beleft
or anything else- if it is notleft
, it'll go on the right (and underneath).
The code
Here's a complete MWE that you can play with; the code is fairly detailed in its comments- it basically uses a few loops to set up the nodes
, and then do the appropriate thing at each node (circle, arrow, or *).
% arara: pdflatex
% !arara: indent: {overwrite: true}
\documentclass{standalone}
\usepackage{tikz}
\usepackage{xstring}
\newcommand{\drawpermutate}[1]{%
\begin{tikzpicture}
% setup the nodes
\foreach [evaluate=\i as \x using int(\i-1)]\i in {0,1,...,8}
{
\foreach [evaluate=\j as \y using int(\j-1)] \j in {0,1,...,8}
{
% \node at (\i,\j)[name=perm-\x-\y,label=\x-\y]{};
\node at (\i,\j)[name=perm-\x-\y,]{};
}
}
% draw numbers 1 to 6 in both x and y direction
\foreach \i in {1,...,6}
{
\node at (perm-\i-0.center){\i};
\node at (perm-0-\i.center){\i};
}
\node at (perm-7-7.center){$S$};
% vertical lines
\draw ([xshift=-5mm]perm-1--1.south west)--([xshift=-5mm]perm-1-7.north west);
\draw ([xshift=-5mm]perm-7--1.south west)--([xshift=-5mm]perm-7-7.north west);
% horizontal lines
\draw ([yshift=5mm]perm--1-0.south west)--([yshift=5mm]perm-7-0.south east);
\draw ([yshift=5mm]perm--1-6.south west)--([yshift=5mm]perm-7-6.south east);
% draw user input
\foreach \mystyle/\coords/\leftorright in {#1}
{
\foreach \x/\y in \coords
{
\node[circle,fill=\mystyle,draw=\mystyle] at (perm-\x-\y){};
\IfStrEq{\leftorright}{left}{%
\node[\mystyle] at (perm--1-\y){*};
\node[\mystyle] at (perm-\x-7){$\downarrow$};
}
{% otherwise put it on the right
\node[\mystyle] at (perm-7-\y){$\leftarrow$};
\node[\mystyle] at (perm-\x--1){*};
}
}
}
\end{tikzpicture}
}
\begin{document}
%\drawpermutate{blue/{1/1,3/5,4/2}/left,red/{3/4,2/3}/right}
\drawpermutate{green/{1/1,3/5,4/2}/left,orange/{3/4,2/3}/right}
\end{document}
TikZ is not mandatory:-)
\documentclass{article}
\usepackage{color}
\makeatletter
\def\pdiag#1{{%
\setlength\unitlength{15pt}%
\begin{picture}(10,10)(-2,-2)%
\put(0,-2){\line(0,1){10}}%
\put(7,-2){\line(0,1){10}}%
\put(-2,-0){\line(1,0){10}}%
\put(-2,7){\line(1,0){10}}%
\put(7.2,7){S}%
\count@\z@
\@for\yc:=#1\do{%
\expandafter\ycdef\yc
\advance\count@\@ne
\put(-1,\count@){\the\count@}%
\put(\count@,-1){\the\count@}%
\put(\count@,\y){\if r\c\color{red}\else\if b\c\color{blue}\fi\fi
\circle*{.3}}%
\if r\c
\put(\count@,-2){\color{red}$\ast$}%
\put(7,\y){\color{red}$\leftarrow$}%
\fi
\if b\c
\put(-2,\y){\color{blue}$\ast$}%
\put(\count@,7.1){\color{blue}$\downarrow$}%
\fi
}%
\end{picture}}}
\def\ycdef#1#2{\def\y{#1}\def\c{#2}}
\begin{document}
\pdiag{6r,3r,2x,4r,1b,5b}
\end{document}
With the Asymptote
module permdiag.asy
typesetting
of this nice little permutation game can be completely automated.
Processing of the following file permdiag-test.asy
import permdiag;
permDiag pd=permDiag(new int[]{6,3,2,4,1,5});
for(int i=1;i<=pd.n;++i){
shipout("diag"+format("%02d",i),pd.board(i));
}
with asy -f pdf permdiag-test.asy
results in 6 files diag01.pdf
..diag06.pdf
,
which were combined together with
\documentclass[a4paper]{article}
\usepackage{graphicx}
\begin{document}
\noindent%
\includegraphics[scale=1]{diag00.pdf}\quad
\includegraphics[scale=1]{diag01.pdf}\quad
\includegraphics[scale=1]{diag02.pdf}\\[10mm]
\noindent%
\includegraphics[scale=1]{diag03.pdf}\quad
\includegraphics[scale=1]{diag04.pdf}\quad
\includegraphics[scale=1]{diag05.pdf}
\end{document}
And this is the main permdiag.asy
, which handles the permDiag
class:
struct permDiag{
int n;
int[] perm;
int[] dotState;
int[][] tperm;
picture boardPic;
guide dotShape;
pen[] dotFill;
void drawNumbers(){
for(int i=0;i<n;++i){
label(boardPic,string(i+1),(i,-1));
label(boardPic,string(n-i),(-1,n-1-i));
}
}
void drawArrows(){
for(int i=0;i<n;++i){
if(dotState[i]>0){
if(dotState[i]==1){
label(boardPic,"$\leftarrow$",(n+0.5,tperm[i][0]-1));
}else{
label(boardPic,"$\downarrow$",(tperm[i][2]-1,n+0.5));
}
}
}
}
void drawStars(){
for(int i=0;i<n;++i){
if(dotState[i]>0){
if(dotState[i]==2){
label(boardPic,"*",(-2,tperm[i][0]-1));
}else{
label(boardPic,"*",(tperm[i][3]-1,-2));
}
}
}
}
void drawLines(){
draw(boardPic,(-2.5,-0.5)--(n+1.5,-0.5));
draw(boardPic,(-2.5,n-0.5)--(n+1.5,n-0.5));
draw(boardPic,(-0.5,-2.5)--(-0.5,n+1.5));
draw(boardPic,(n-0.5,-2.5)--(n-0.5,n+1.5));
}
void play(int hstep){
erase(boardPic);
int tmp;
int redblu=0;
for(int i=0;i<n;++i){
tperm[i][0]=perm[i];
tperm[i][4]=i+1;
}
dotState=array(n,0);
for(int i=0;i<hstep;++i){
for(int j=n-1;j>i;--j){
if(tperm[j][redblu]>tperm[j-1][redblu]){
tmp=tperm[j][redblu];
tperm[j][redblu]=tperm[j-1][redblu];
tperm[j-1][redblu]=tmp;
tmp=tperm[j][1-redblu];
tperm[j][1-redblu]=tperm[j-1][1-redblu];
tperm[j-1][1-redblu]=tmp;
}
}
dotState[i]=1+redblu;
redblu=1-redblu;
}
}
picture board(int move){
assert(move>=0 && move<=n);
// move number, 0 = initial state,
// odd - after red move
// even - after blu move
play(move);
for(int i=0;i<n;++i){
filldraw(boardPic,shift(tperm[i][5]-1,tperm[i][0]-1)*dotShape,dotFill[dotState[i]]);
}
drawNumbers();
drawArrows();
drawStars();
drawLines();
label(boardPic,"$\mathcal{S}$",(n+0.5,n+0.5));
return boardPic;
}
void operator init(int[] perm){
assert(perm.length>0);
this.n=perm.length;
this.perm=copy(perm);
this.dotState=array(n,0);
this.dotShape=scale(0.382)*unitcircle;
this.dotFill=new pen[]{lightyellow,red,blue};
this.tperm=new int[n][6];
boardPic.size(20*n);
}
}
//// Example:
//
// import permdiag;
// permDiag pd=permDiag(new int[]{6,3,2,4,1,5});
//
// for(int i=0;i<=pd.n;++i){
// shipout("diag"+format("%02d",i),pd.board(i));
// }
//
Edit: Fixed picture scaling for different values of n
. Example permdiag-test2.asy
:
import permdiag;
permDiag pda=permDiag(new int[]{3,1,2,4});
permDiag pdb=permDiag(new int[]{9,19,17,1,8,13,18,11,10,4,5,7,2,3,15,16,12,6,20,14});
int i;
i=3;
shipout("diag-"+format("n%d-",pda.n)+format("%02d",i),pda.board(i));
i=12;
shipout("diag-"+format("n%d-",pdb.n)+format("%02d",i),pdb.board(i));
processed with asy -f pdf permdiag-test2.asy
results in two pictures,
diag-n4-03.pdf
and diag-n20-12.pdf
: