![Latex 中的隐写术](https://linux22.com/image/395286/Latex%20%E4%B8%AD%E7%9A%84%E9%9A%90%E5%86%99%E6%9C%AF.png)
我是一名教师。一个持续存在的问题是,学生将作业发布到 chegg.com 等网站上,让其他人为他们写项目。我不知道学生提供的是 JPG 还是 PDF——但我在网站上看到的是作业的文本版本。例如: https://www.chegg.com/homework-help/questions-and-answers/part-2-confidence-intervals-recovery-great-recession-2007-2009-economic-situation-many-fam-q37153607
对于我的作业,作业是用 LaTex 编写的。我想知道如何在每个学生的作业中嵌入一个唯一标识符,这样我可以通过查看作业来判断是谁发布的。假设我正在教 500 名学生(9 位),最好对学期和年份(5 位)进行编码,再加上一个奇偶校验位——所以我们假设我需要编码 16 位。
要求:
- 无法使用字体更改——字体信息在 chegg 发布中丢失。
- 不能使用水印或其他图像——再次强调,这些信息已从帖子中删除。
- 无法使用间距变化 - 主要是因为这与 LaTex 格式化页面的方式背道而驰,而且我不确定细微的间距变化是否会在发布后保留下来。(换句话说,没有 stegsnow)
- 最佳答案会多次嵌入标识符,这样即使只发布了作业的一部分,也可以识别来源。
我认为这意味着我基本上需要一些可见的东西,但在文本本身中很容易被忽视。想法:
- 重复一个读者可能忽略的特定单词——根据重复哪个单词,可以知道其身份(例如,我在前面的文字中重复了单词“that”)。
- 使用可能容易被忽视的额外标点符号......(请参见末尾的双句号)。
- 插入偶尔的字母翻转,看起来像是马虎的拼写检查,但实际上对标识符进行了编码。(请参阅偶尔的拼写)
有人知道已经实施了什么吗?有没有关于最简单方法的建议?
这是解决方案的潜在起点。在最佳实现中, \stenagbox 可以包含“复杂”文本,例如 begin/end{enumerate}、带格式的多个段落等。
\documentclass[10pt]{article}
\newcommand{\stenagbox}[2]{#2}
\begin{document}
\stenagbox{16000}{When moving to a new area, it is important to
understand the climate that you will be living in. Does it rain
more or less than you are used to? Will it typically be hotter or
colder than the city that you are coming from? Just knowing where
a city is located on a map is not sufficient. In some areas,
nearby mountains may block the wind and make the climate hotter or
colder than expected. In other areas, the ocean may keep the
region cool in the summer time and warm in the winter. Using
statistics, the climate in two areas can be compared to determine
what to expect.}
\end{document}
答案1
我创建了一个\bitstream[<total tests>]{<test number>}
(默认总共 256 个测试),它将组成测试号的二进制位写入一个令牌寄存器。我在 MWE 中演示了它的工作原理(您在测试准备中不需要它,它只是为了演示)
然后,为了对测试版本进行编码,人们会将\dobit{<output A>}{<output B>}
细微的差异放入输出流(即打印的测试)。每次调用它时,它都会从令牌寄存器中吸取高位,\bits
并使用它来决定输出 A 还是 B。
在 MWE 中,我创建了一个 8 测试矩阵,需要 3 位(2^3=8),因此\dobit
会遇到 3 个选择来创建 8 个不同的测试版本。版本中是否包含逗号,“versions”拼写是否正确,以及是否重复单词“the”。
尽管我只是做一个\bigskip
来分离测试版本,但据推测,人们会使用一个\clearpage
以便各个测试会出现在单独的纸上。
\documentclass{article}
\usepackage{pgffor}
\newcounter{bitreg}
\newcounter{bitval}
\newtoks\bits
\newcommand{\addtotoks}[2]{#1\expandafter{\the#1#2}}
\newcommand\bitstream[2][256]{%
\setcounter{bitreg}{#2}%
\setcounter{bitval}{\the\numexpr#1/2\relax}%
\bits{}%
\bitstreamaux%
}
\newcommand\bitstreamaux{%
\addtocounter{bitreg}{-\thebitval}%
\ifnum\thebitreg>-1\relax\addtotoks\bits{1}\else
\addtotoks\bits{0}\addtocounter{bitreg}{\thebitval}\fi
\ifnum\thebitval=1\relax\else%
\setcounter{bitval}{\the\numexpr\thebitval/2\relax}%
\expandafter\bitstreamaux%
\fi
}
\newcommand\dobit[2]{%
\expandafter\checkbit\the\bits\relax
\ifnum\thisbit=1\relax#2\else#1\fi
}
\def\checkbit#1#2\relax{\gdef\thisbit{#1}\bits{#2}}
\begin{document}
\bitstream{255} \the\bits
\bitstream{128} \the\bits
\bitstream{53} \the\bits
\bitstream[8]{5} \the\bits
\foreach\x in{0,...,7}{\bitstream[8]{\x}
Test \x:
This is a test\dobit{}{,} of % COMMA IN OR NOT
multiple vers\dobit{io}{oi}ns. The test %VERSIONS MISSPELLED OR NOT
is for all \dobit{the the}{the} marbles.\bigskip\par% THE REPEATED OR NOT.
}
\end{document}
补充
另外值得注意的一点。虽然<total tests>
通常预期为 2 的幂,但如果它们是不是,仍将生成唯一测试。但是,不会\bitstream
对应于的二进制表示<test number>
。例如,以下总共 9 个测试的比特流,
\bitstream[9]{0}\the\bits\par
\bitstream[9]{1}\the\bits\par
\bitstream[9]{2}\the\bits\par
\bitstream[9]{3}\the\bits (4 not 3)\par
\bitstream[9]{4}\the\bits (5 not 4)\par
\bitstream[9]{5}\the\bits (8 not 5)\par
\bitstream[9]{6}\the\bits (9 not 6)\par
\bitstream[9]{7}\the\bits (10 not 7)\par
\bitstream[9]{8}\the\bits (12 not 8)\par
/2
产生 9 个唯一的结果,只是没有对应于数字 0 到 8 的比特流。该伪影源于与非 2 的幂的数字的除法运算相关的整数算术。
双倍补充
一个可能的陷阱(用户错误)是,如果您未能发出足够的\dobit
调用来匹配分配给的位数\bitstream
。那么区分案例的数字永远不会进入测试,因此某些案例可能无法区分。
解决该用户错误的方法是\bitstream
从 LSB(最低有效位)而不是 MSB(最高有效位)开始构建。这样,即使调用次数不完整,\dobit
仍会提供差异。
这是答案的一个版本,它\bitstream
从 LSB 构建到 MSB,而不是相反。
\documentclass{article}
\usepackage{pgffor}
\newcounter{bitreg}
\newcounter{bitval}
\newtoks\bits
\newcommand{\apptoks}[2]{#1\expandafter{\expandafter#2\the#1}}
\newcommand\bitstream[2][256]{%
\setcounter{bitreg}{#2}%
\setcounter{bitval}{\the\numexpr#1/2\relax}%
\bits{}%
\bitstreamaux%
}
\newcommand\bitstreamaux{%
\addtocounter{bitreg}{-\thebitval}%
\ifnum\thebitreg>-1\relax\apptoks\bits{1}\else
\apptoks\bits{0}\addtocounter{bitreg}{\thebitval}\fi
\ifnum\thebitval=1\relax\else%
\setcounter{bitval}{\the\numexpr\thebitval/2\relax}%
\expandafter\bitstreamaux%
\fi
}
\newcommand\dobit[2]{%
\expandafter\checkbit\the\bits\relax
\ifnum\thisbit=1\relax#2\else#1\fi
}
\def\checkbit#1#2\relax{\gdef\thisbit{#1}\bits{#2}}
\begin{document}
\bitstream{255} \the\bits
\bitstream{128} \the\bits
\bitstream{53} \the\bits
\bitstream[8]{5} \the\bits
\foreach\x in{0,...,7}{\bitstream[8]{\x}
Test \x:
This is a test\dobit{}{,} of % COMMA IN OR NOT
multiple vers\dobit{io}{oi}ns. The test %VERSIONS MISSPELLED OR NOT
is for all \dobit{the the}{the} marbles.\bigskip\par% THE REPEATED OR NOT.
}
\end{document}