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.

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}
