12

I need some help with a crazy idea. For a book I want the page numbers to be a random permutation. As an example, if the book would have 5 pages, I want something like (4,1,2,5,3). Optimally, the table of contents and references would work. I don't know enough about TeX to do it myself. I am already happy about some directions in how to proceed.

I use the book LaTeX class and XeLatex.

Thank you!

Eman Resu
  • 143
  • Welcome to TeX.SX! Which TeX engine do you use (pdflatex, xelatex, lualatex) and would it bother you to switch? – TeXnician May 11 '17 at 12:43
  • I use xelatex. yes, i would consider switching if that makes the solution much easier. you thinking about lua? – Eman Resu May 11 '17 at 12:46
  • off topic: i tried felt 5 times to write 'dear tex community' at the beginning of my post, but it always deletes that line. very strange. is the stack overflow editor broken? – Eman Resu May 11 '17 at 12:49
  • @EmanResu I do not know why it is deleted, but you do not need to write 'dear tex community', 'thank you', etc. – Karlo May 11 '17 at 12:52
  • Do you know how many pages you have? With that it is easier to construct a function that does the permutation, and put that in the page number. – StefanH May 11 '17 at 12:55
  • 7
    @Karlo Perhaps there is in automated friendliness deletion algorithm in place ;) – Eman Resu May 11 '17 at 13:08
  • @StefanH Yes, I know how many pages – Eman Resu May 11 '17 at 13:09
  • 1
    @EmanResu See https://meta.stackexchange.com/a/93989 . – Emil Jeřábek May 11 '17 at 17:54
  • As answered below, it is quite possible. However: If your book is something long, and is going to be commercially printed, then the printing service will disallow that. The reason is that it will confuse persons who inspect the finished book for quality assurance. –  May 11 '17 at 20:32
  • 2
    @RobtA The goal is to confuse people ;) – Eman Resu May 11 '17 at 20:36
  • @EmilJeřábek The automated friendliness deletion algorithm is indeed in place ;) – Eman Resu May 11 '17 at 20:40
  • 1
    Gosh, if the goal is to confuse people, then all you have do do is let them read the TeX source document. –  May 11 '17 at 21:21
  • 1
    @RobtA There are multiple ways to confuse people. And some are funnier than others. – Eman Resu May 11 '17 at 21:25

3 Answers3

12

This uses the specified permutation within range, if you have more pages than that it reverts to normal, so you can always modify the definition at the end once you know how many pages you have

enter image description here

\documentclass{book}

\renewcommand\thepage{%
\ifcase\value{page}%
0\or
4\or
1\or
2\or
5\or
6\or
3\else
\arabic{page}%
\fi}

\begin{document}

\tableofcontents

\chapter{Intro}
\section{Zzzz}aaaa
\section{Zzzzz}aaaa \clearpage bbbb
\section{Zzzzz}aaaa


\chapter{Something}
\section{Zzzz zzz}aaaa
\section{Zzzzzz zzz}aaaa \clearpage bbbb
\section{Zzzzzzz zzz}aaaa


\end{document}
David Carlisle
  • 757,742
7

Just for fun an implementation that doesn't require external programs and just needs two LaTeX runs. However, the pages will change at every subsequent run.

\documentclass{article}
\usepackage{xparse}
\usepackage{zref-user,zref-abspage,atveryend}
\usepackage{kantlipsum}

% Coerce zref into not adding the last page number in the .aux file
% which would always issue the warning that labels have changed
\makeatletter
\zref@newlist{lastabs}
\zref@addprop{lastabs}{abspage}
\AfterLastShipout{%
  \if@filesw
    \begingroup
      \advance\c@page\m@ne
      \toks@\expandafter{\Z@L@lastabs}%
      \expandafter\zref@wrapper@immediate\expandafter{%
        \expandafter\ZREF@label\expandafter{\the\toks@}{LastAbsPage}%
      }%
    \endgroup
  \fi
}
\makeatother

\ExplSyntaxOn

\cs_set_eq:Nc \zref_extract:nn { zref@extract }

% a variant of the Knuth shuffle algorithm as implemented
% in https://tex.stackexchange.com/a/224559/4427

\cs_new:Nn \knuthshuffle_get_random:Nnn
 {
  \int_set:Nn #1 { \fp_eval:n { randint(#2,#3) } }
 }

\tl_new:N \l_knuthshuffle_tempa_tl
\tl_new:N \l_knuthshuffle_tempb_tl
\int_new:N \l_knuthshuffle_random_int
\prop_new:N \l_knuthshuffle_newperm_prop
\prop_new:N \g_knuthshuffle_identity_prop % the identity
\seq_new:N \g_knuthshuffle_permutation_seq

\cs_new_protected:Nn \knuthshuffle_generate:n
 {
  \prop_set_eq:NN \l_knuthshuffle_newperm_prop \g_knuthshuffle_identity_prop
  \int_step_inline:nnnn { #1 } { -1 } { 2 }
   {
    \knuthshuffle_get_random:Nnn \l_knuthshuffle_random_int { 1 } { ##1 }
    \prop_get:NnN \l_knuthshuffle_newperm_prop { ##1 } \l_knuthshuffle_tempa_tl 
    \prop_get:NVN \l_knuthshuffle_newperm_prop \l_knuthshuffle_random_int \l_knuthshuffle_tempb_tl 
    \prop_put:NnV \l_knuthshuffle_newperm_prop { ##1 } \l_knuthshuffle_tempb_tl
    \prop_put:NVV \l_knuthshuffle_newperm_prop \l_knuthshuffle_random_int \l_knuthshuffle_tempa_tl
   }
  \seq_clear:N \g_knuthshuffle_permutation_seq
  \int_step_inline:nnnn { 1 } { 1 } { #1 }
   {
    \seq_gput_right:Nx \g_knuthshuffle_permutation_seq
     {
      \prop_item:Nn \l_knuthshuffle_newperm_prop { ##1 }
     }
   }
  %\seq_show:N \g_knuthshuffle_permutation_seq % for debugging
 }
\cs_generate_variant:Nn \knuthshuffle_generate:n { x }

\AtBeginDocument
 {
  \int_compare:nT { \zref_extract:nn {LastAbsPage}{abspage} > 0 }
   {
    \int_step_inline:nnnn { 1 } { 1 } { \zref_extract:nn {LastAbsPage}{abspage} }
     {
      \prop_gput:Nnn \g_knuthshuffle_identity_prop { #1 } { #1 }
     }
    \knuthshuffle_generate:n { \zref_extract:nn {LastAbsPage}{abspage} }
    \renewcommand{\thepage}{\randomarabic{page}}
   }
 }

\NewExpandableDocumentCommand{\randomarabic}{m}
 {
  \seq_item:Nn \g_knuthshuffle_permutation_seq { \value{#1} }
 }

\ExplSyntaxOff

\begin{document}

\kant[1-102]

\ExplSyntaxOn
% for showing the used page numbers
\seq_use:Nn \g_knuthshuffle_permutation_seq { ,~ }
\ExplSyntaxOff

\end{document}

At the end the sequence of the used page numbers has been added by way of example.

Here's the bottom of the last page.

enter image description here

A more complex strategy can cope also with the table of contents. Now the random sequence is only computed if the number of pages has changed since the last LaTeX run, otherwise the previous sequence is used.

\documentclass{article}
\usepackage{xparse}
\usepackage{zref-abspage,atveryend}
\usepackage{kantlipsum}

\makeatletter
\zref@newlist{lastabs}
\zref@addprop{lastabs}{abspage}
\AfterLastShipout{%
  \if@filesw
    \begingroup
      \advance\c@page\m@ne
      \toks@\expandafter{\Z@L@lastabs}%
      \expandafter\zref@wrapper@immediate\expandafter{%
        \expandafter\ZREF@label\expandafter{\the\toks@}{LastAbsPage}%
      }%
    \endgroup
  \fi
}
\makeatother

\ExplSyntaxOn

\cs_set_eq:Nc \zref_extract:nn { zref@extract }

\cs_new:Nn \knuthshuffle_get_random:Nnn
 {
  \int_set:Nn #1 { \fp_eval:n { randint(#2,#3) } }
 }

\tl_new:N \l_knuthshuffle_tempa_tl
\tl_new:N \l_knuthshuffle_tempb_tl
\int_new:N \l_knuthshuffle_random_int
\prop_new:N \l_knuthshuffle_newperm_prop
\prop_new:N \g_knuthshuffle_identity_prop % the identity
\seq_new:N \g_knuthshuffle_permutation_seq

\cs_new_protected:Nn \knuthshuffle_generate:n
 {
  \prop_set_eq:NN \l_knuthshuffle_newperm_prop \g_knuthshuffle_identity_prop
  \int_step_inline:nnnn { #1 } { -1 } { 2 }
   {
    \knuthshuffle_get_random:Nnn \l_knuthshuffle_random_int { 1 } { ##1 }
    \prop_get:NnN \l_knuthshuffle_newperm_prop { ##1 } \l_knuthshuffle_tempa_tl 
    \prop_get:NVN \l_knuthshuffle_newperm_prop \l_knuthshuffle_random_int \l_knuthshuffle_tempb_tl 
    \prop_put:NnV \l_knuthshuffle_newperm_prop { ##1 } \l_knuthshuffle_tempb_tl
    \prop_put:NVV \l_knuthshuffle_newperm_prop \l_knuthshuffle_random_int \l_knuthshuffle_tempa_tl
   }
  \seq_clear:N \g_knuthshuffle_permutation_seq
  \int_step_inline:nnnn { 1 } { 1 } { #1 }
   {
    \seq_gput_right:Nx \g_knuthshuffle_permutation_seq
     {
      \prop_item:Nn \l_knuthshuffle_newperm_prop { ##1 }
     }
   }
  %\seq_show:N \g_knuthshuffle_permutation_seq % for debugging
 }
\cs_generate_variant:Nn \knuthshuffle_generate:n { x }

\AtBeginDocument
 {
  \seq_if_empty:NT \g_knuthshuffle_permutation_seq
   {
    \int_compare:nT { \zref_extract:nn {LastAbsPage}{abspage} > 0 }
     {
      \int_step_inline:nnnn { 1 } { 1 } { \zref_extract:nn {LastAbsPage}{abspage} }
       {
        \prop_gput:Nnn \g_knuthshuffle_identity_prop { #1 } { #1 }
       }
      \knuthshuffle_generate:n { \zref_extract:nn {LastAbsPage}{abspage} }
     }
   }
  \renewcommand{\thepage}{\randomarabic{page}}
 }

\AfterLastShipout
 {
  \int_compare:nT { \value{abspage} = \zref_extract:nn {LastAbsPage}{abspage} }
   {
    \iow_now:cx { @auxout }
     {
      \randompagesequence
       { \zref_extract:nn {LastAbsPage}{abspage} }
       { \seq_use:Nn \g_knuthshuffle_permutation_seq { , } }
     }
   }
 }

\cs_new_protected:Npn \randompagesequence #1 #2
 {
  \int_compare:nT { #1 = \zref_extract:nn {LastAbsPage}{abspage} }
   {
    \seq_gset_from_clist:Nn \g_knuthshuffle_permutation_seq { #2 }
   }
 }
\NewExpandableDocumentCommand{\randomarabic}{m}
 {
  \seq_item:Nn \g_knuthshuffle_permutation_seq { \value{#1} }
 }

\ExplSyntaxOff

\begin{document}

\ExplSyntaxOn
\seq_show:N \g_knuthshuffle_permutation_seq
\ExplSyntaxOff

\tableofcontents

\section{One}
\kant[1-20]
\section{Two}
\kant[21-30]
\section{Three}
\kant[31-53]

\ExplSyntaxOn
% for showing the used page numbers
\seq_use:Nn \g_knuthshuffle_permutation_seq { ,~ }
\ExplSyntaxOff

\end{document}

enter image description here

egreg
  • 1,121,712
  • Yay for the Knuth shuffle! (Obligatory link to the blog http://tex-talk.net/2011/08/do-the-knuth-shuffle/ though I note that the formatting is all messed up) – Andrew Stacey May 11 '17 at 23:14
5

Well, just to play around with it: A solution with expl3 with the only drawback that it will not check for an existing page with that number. The max page number can be set in \l__blob_int.

random

\documentclass{book}

\usepackage{expl3}
\ExplSyntaxOn
\clist_new:N \l__quack_clist
\int_new:N \l__blub_int
\int_gset:Nn \l__blub_int {0}
\int_new:N \l__blob_int
\int_gset:Nn \l__blob_int {10}
\int_do_while:nn {\l__blub_int < \l__blob_int}
    {
        \clist_put_right:NV \l__quack_clist \l__blub_int
        \int_incr:N \l__blub_int
    }
\renewcommand\thepage{%
    \clist_item:Nn \l__quack_clist {\fp_eval:n {randint(\clist_count:N\l__quack_clist)}}
}
\ExplSyntaxOff

\begin{document}

\tableofcontents

\chapter{Intro}
\section{Zzzz}aaaa
\section{Zzzzz}aaaa \clearpage bbbb
\section{Zzzzz}aaaa


\chapter{Something}
\section{Zzzz zzz}aaaa
\section{Zzzzzz zzz}aaaa \clearpage bbbb
\section{Zzzzzzz zzz}aaaa


\end{document}
TeXnician
  • 33,589