下面的打印语句将打印“hello world”。有人能解释一下吗?
System.out.println(randomString(-229985452) + " " + randomString(-147909649));
randomString()如下所示:
public static String randomString(int i)
{
Random ran = new Random(i);
StringBuilder sb = new StringBuilder();
while (true)
{
int k = ran.nextInt(27);
if (k == 0)
break;
sb.append((char)('`' + k));
}
return sb.toString();
}
这里的每个人都做了很好的工作,解释了代码是如何工作的,并展示了如何构建自己的示例,但这里有一个信息理论的答案,说明了为什么我们可以合理地期望存在暴力搜索最终会找到的解决方案。
26个不同的小写字母组成了我们的字母表∑。为了允许生成不同长度的单词,我们进一步添加了终止符,以生成扩展的字母表∑':=∑⑪{⑪}。
设α是符号,X是∑′上均匀分布的随机变量。获得该符号P(X=α)及其信息内容I(α)的概率由下式给出:
P(X=α)=1/|∑'|=1/27I(α)=-log₂[P(X=α)]=-log₂(1/27)=对数₂(27)
对于一个词ω∈∑*和它的∈终止的对应词ω':=ω·∈(∑')*,我们有
I(ω):=I(ω')=|ω'|*log₂(27)=(|ω|+1)*log₂(27)
由于伪随机数生成器(PRNG)是用32位种子初始化的,因此我们可以预期长度最多为
λ=地板[32/log₂(27)] - 1 = 5
由至少一个种子产生。即使我们要搜索一个6个字符的单词,我们仍有41.06%的成功率。不太破旧。
对于7个字母,我们看到接近1.52%,但我在尝试之前没有意识到这一点:
#include <iostream>
#include <random>
int main()
{
std::mt19937 rng(631647094);
std::uniform_int_distribution<char> dist('a', 'z' + 1);
char alpha;
while ((alpha = dist(rng)) != 'z' + 1)
{
std::cout << alpha;
}
}
参见输出:http://ideone.com/JRGb3l
其他答案解释了原因,但这里是如何解释的。
给定Random的一个实例:
Random r = new Random(-229985452)
r.nextInt(27)生成的前6个数字是:
8
5
12
12
15
0
并且r.nextInt(27)生成给定Random r=new Random(-1470909649)的前6个数字是:
23
15
18
12
4
0
然后,只需将这些数字添加到字符“”的整数表示形式(即96):
8 + 96 = 104 --> h
5 + 96 = 101 --> e
12 + 96 = 108 --> l
12 + 96 = 108 --> l
15 + 96 = 111 --> o
23 + 96 = 119 --> w
15 + 96 = 111 --> o
18 + 96 = 114 --> r
12 + 96 = 108 --> l
4 + 96 = 100 --> d
由于多线程在Java中非常容易,这里有一个变体,它使用所有可用的内核搜索种子:http://ideone.com/ROhmTA
import java.util.ArrayList;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
public class SeedFinder {
static class SearchTask implements Callable<Long> {
private final char[] goal;
private final long start, step;
public SearchTask(final String goal, final long offset, final long step) {
final char[] goalAsArray = goal.toCharArray();
this.goal = new char[goalAsArray.length + 1];
System.arraycopy(goalAsArray, 0, this.goal, 0, goalAsArray.length);
this.start = Long.MIN_VALUE + offset;
this.step = step;
}
@Override
public Long call() throws Exception {
final long LIMIT = Long.MAX_VALUE - this.step;
final Random random = new Random();
int position, rnd;
long seed = this.start;
while ((Thread.interrupted() == false) && (seed < LIMIT)) {
random.setSeed(seed);
position = 0;
rnd = random.nextInt(27);
while (((rnd == 0) && (this.goal[position] == 0))
|| ((char) ('`' + rnd) == this.goal[position])) {
++position;
if (position == this.goal.length) {
return seed;
}
rnd = random.nextInt(27);
}
seed += this.step;
}
throw new Exception("No match found");
}
}
public static void main(String[] args) {
final String GOAL = "hello".toLowerCase();
final int NUM_CORES = Runtime.getRuntime().availableProcessors();
final ArrayList<SearchTask> tasks = new ArrayList<>(NUM_CORES);
for (int i = 0; i < NUM_CORES; ++i) {
tasks.add(new SearchTask(GOAL, i, NUM_CORES));
}
final ExecutorService executor = Executors.newFixedThreadPool(NUM_CORES, new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
final Thread result = new Thread(r);
result.setPriority(Thread.MIN_PRIORITY); // make sure we do not block more important tasks
result.setDaemon(false);
return result;
}
});
try {
final Long result = executor.invokeAny(tasks);
System.out.println("Seed for \"" + GOAL + "\" found: " + result);
} catch (Exception ex) {
System.err.println("Calculation failed: " + ex);
} finally {
executor.shutdownNow();
}
}
}