2

To identify an object in many different tabular environments, I want to create a pseudo random hash string with 5 digits (only capital letters). A continuously increasing number is no possibility in this case.

I'm searching for a macro which gives me a different hash string every time I call it. But the value must be the same independent of the compile cycle.

The result should look like within this MWE:

\documentclass{article}%
\usepackage[a4paper,left=10mm,right=10mm,top=10mm,bottom=10mm]{geometry}%

\begin{document} \begin{tabular}{|c|c|} \hline \textbf{HASH} & \textbf{Description} \ \hline A4X75 & This is the first Hash \ \hline 7T0LE & This is the second Hash \ \hline \end{tabular} \end{document}

Tabular

PascalS
  • 826

2 Answers2

6

If you don't use pseudorandom numbers elsewhere,

\documentclass{article}

\ExplSyntaxOn

\sys_gset_rand_seed:n { 0 }% or whatever

\NewExpandableDocumentCommand{\hash}{} { \int_to_Base:nn { \int_rand:nn { 36 } { 1295 } } { 36 } % two digits __pascals_hash_three:e { \int_to_Base:nn { \int_rand:n { 46655 } } { 36 } } % three digits } \cs_new:Nn __pascals_hash_three:n { \prg_replicate:nn { 3 - \tl_count:n { #1 } } { 0 } #1 } \cs_generate_variant:Nn __pascals_hash_three:n { e }

\ExplSyntaxOff

\begin{document}

\begin{tabular}{|c|c|} \hline \textbf{HASH} & \textbf{Description} \ \hline \hash & This is the first Hash \ \hline \hash & This is the second Hash \ \hline \end{tabular}

\end{document}

Fixing a seed will allow exact reproduction at every run.

enter image description here

You compute the probability of getting duplicates.

If you need to avoid duplicates because you have too many hashes compared to the number of digits, you lose expandability.

\documentclass{article}

\ExplSyntaxOn

\sys_gset_rand_seed:n { 0 }% or whatever

\NewDocumentCommand{\hash}{} { \pascals_hash: }

\tl_new:N \l__pascals_hash_try_tl \seq_new:N \g_pascals_hash_used_seq

\cs_new_protected:Nn \pascals_hash: { % generate a tentative hash \tl_set:Ne \l__pascals_hash_try_tl { \int_to_Base:nn { \int_rand:nn { 36 } { 1295 } } { 36 } % two digits __pascals_hash_three:e { \int_to_Base:nn { \int_rand:n { 46655 } } { 36 } } % three digits } % check for duplicate \seq_if_in:NVTF \g_pascals_hash_used_seq \l__pascals_hash_try_tl {% it's a duplicate, retry \pascals_hash: } {% not a duplicate \seq_gput_right:NV \g_pascals_hash_used_seq \l__pascals_hash_try_tl \tl_use:N \l__pascals_hash_try_tl } }

\cs_new:Nn __pascals_hash_three:n { \prg_replicate:nn { 3 - \tl_count:n { #1 } } { 0 } #1 } \cs_generate_variant:Nn __pascals_hash_three:n { e }

\ExplSyntaxOff

\begin{document}

\begin{tabular}{|c|c|} \hline \textbf{HASH} & \textbf{Description} \ \hline \hash & This is the first Hash \ \hline \hash & This is the second Hash \ \hline \end{tabular}

\end{document}

egreg
  • 1,121,712
  • Nice, thanks for this fast response! Exactly what I searched for! – PascalS Feb 17 '24 at 17:01
  • I'm thinking about reducing my digits to 3 or 4 because lack of space. So the probability to create duplicates will increase. Is there an easy way to check for duplicates and in case create a new one? Maybe encapsulated within the hash macro? – PascalS Feb 18 '24 at 08:47
  • @PascalS Added the code with the check for duplicates – egreg Feb 18 '24 at 09:10
  • Seems to be what I'm searching for, but at overleaf I got errors. It's not compileable. – PascalS Feb 18 '24 at 09:17
  • 2
    When \int_rand:n returns 7 (for example) then you doesn't get three digits. – wipet Feb 18 '24 at 10:11
  • @wipet You're right. I'll fix. – egreg Feb 18 '24 at 10:15
  • @PascalS The check for duplicates is slow. I tried generating 10000 of them and got no duplicate. So I believe you can quite safely use the “nonchecking” version. – egreg Feb 18 '24 at 10:32
  • Okay got it! One last thing, I will add this hash calculation in this context. In this example the hash function will be called twice, once for the \pgfplotstable entry and once for the graphical \pic label. But in case, this should be the same value. So is it possible just to call the \hash routine only every second call or give a parameter to the function if a new one should be generated or the old value should be taken? – PascalS Feb 18 '24 at 13:55
  • @PascalS Sorry, but I don't really get it. Can you please open a new question with the details? – egreg Feb 18 '24 at 14:10
  • I gave my best to describe it in this question now :) – PascalS Feb 18 '24 at 15:00
3

I show what to do with OpTeX. We have no already prepared macro like \_int_to_Base, so we have to use LauTeX primitives \Uchar, \uniformdeviate and we generate six digits in the loop. The probability of the occurrence of a digit in the string is set to 1/3.

\setrandomseed 100 % radom generator initialized, the same for all compilation
\def\randomchar#1#2{\Uchar\numexpr`#1+\uniformdeviate\numexpr`#2-`#1\relax\relax}
\def\randomhash{%
    \fornum 1..6 \do
        {\ifnum\uniformdeviate3 >1 \randomchar 09\else \randomchar AZ\fi}%
}
\def\createhash{\edef\hash{\randomhash}%
   \ifcsname hash:\hash \endcsname \ea\createrandomhash % the hash is already used
   \else \sxdef{hash:\hash}{}\hash \fi
}

\table{|(\tt)c|c|}{\crl \bf HASH & \bf Description \crl \createhash & This is the first Hash \crl \createhash & This is the second Hash \crl }

\bye

wipet
  • 74,238