下面的打印语句将打印“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

其他回答

在Java文档中,当为Random类指定种子值时,这是一个有意的特性。

如果使用同一种子创建了两个“随机”实例对每个方法调用相同的序列,它们将生成和返回相同的数字序列。为了保证这一点属性,为类Random指定特定算法。Java实现必须使用此处显示的所有算法类Random,为了Java代码的绝对可移植性。

http://docs.oracle.com/javase/1.4.2/docs/api/java/util/Random.html

奇怪的是,你会认为拥有可预测的“随机”数字存在隐性安全问题。

我写了一个快速程序来寻找这些种子:

import java.lang.*;
import java.util.*;
import java.io.*;

public class RandomWords {
    public static void main (String[] args) {
        Set<String> wordSet = new HashSet<String>();
        String fileName = (args.length > 0 ? args[0] : "/usr/share/dict/words");
        readWordMap(wordSet, fileName);
        System.err.println(wordSet.size() + " words read.");
        findRandomWords(wordSet);
    }

    private static void readWordMap (Set<String> wordSet, String fileName) {
        try {
            BufferedReader reader = new BufferedReader(new FileReader(fileName));
            String line;
            while ((line = reader.readLine()) != null) {
                line = line.trim().toLowerCase();
                if (isLowerAlpha(line)) wordSet.add(line);
            }
        }
        catch (IOException e) {
            System.err.println("Error reading from " + fileName + ": " + e);
        }
    }

    private static boolean isLowerAlpha (String word) {
        char[] c = word.toCharArray();
        for (int i = 0; i < c.length; i++) {
            if (c[i] < 'a' || c[i] > 'z') return false;
        }
        return true;
    }

    private static void findRandomWords (Set<String> wordSet) {
        char[] c = new char[256];
        Random r = new Random();
        for (long seed0 = 0; seed0 >= 0; seed0++) {
            for (int sign = -1; sign <= 1; sign += 2) {
                long seed = seed0 * sign;
                r.setSeed(seed);
                int i;
                for (i = 0; i < c.length; i++) {
                    int n = r.nextInt(27);
                    if (n == 0) break;
                    c[i] = (char)((int)'a' + n - 1);
                }
                String s = new String(c, 0, i);
                if (wordSet.contains(s)) {
                    System.out.println(s + ": " + seed);
                    wordSet.remove(s);
                }
            }
        }
    }
}

我现在已经在后台运行了它,但它已经找到了足够的词来描述经典的穿山甲:

import java.lang.*;
import java.util.*;

public class RandomWordsTest {
    public static void main (String[] args) {
        long[] a = {-73, -157512326, -112386651, 71425, -104434815,
                    -128911, -88019, -7691161, 1115727};
        for (int i = 0; i < a.length; i++) {
            Random r = new Random(a[i]);
            StringBuilder sb = new StringBuilder();
            int n;
            while ((n = r.nextInt(27)) > 0) sb.append((char)('`' + n));
            System.out.println(sb);
        }
    }
}

(视频演示。)

第727295876页、第128911页、第1611659页、第235516779页。

其他答案解释了原因,但这里是如何解释的。

给定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

我就把它留在这里。如果你掌握了一些fork-join-fu来让这个东西燃烧所有的CPU内核(线程很无聊,对吧?),请分享你的代码。我将非常感激。

public static void main(String[] args) {
    long time = System.currentTimeMillis();
    generate("stack");
    generate("over");
    generate("flow");
    generate("rulez");

    System.out.println("Took " + (System.currentTimeMillis() - time) + " ms");
}

private static void generate(String goal) {
    long[] seed = generateSeed(goal, Long.MIN_VALUE, Long.MAX_VALUE);
    System.out.println(seed[0]);
    System.out.println(randomString(seed[0], (char) seed[1]));
}

public static long[] generateSeed(String goal, long start, long finish) {
    char[] input = goal.toCharArray();
    char[] pool = new char[input.length];
    label:
    for (long seed = start; seed < finish; seed++) {
        Random random = new Random(seed);

        for (int i = 0; i < input.length; i++)
            pool[i] = (char) random.nextInt(27);

        if (random.nextInt(27) == 0) {
            int base = input[0] - pool[0];
            for (int i = 1; i < input.length; i++) {
                if (input[i] - pool[i] != base)
                    continue label;
            }
            return new long[]{seed, base};
        }

    }

    throw new NoSuchElementException("Sorry :/");
}

public static String randomString(long i, char base) {
    System.out.println("Using base: '" + base + "'");
    Random ran = new Random(i);
    StringBuilder sb = new StringBuilder();
    for (int n = 0; ; n++) {
        int k = ran.nextInt(27);
        if (k == 0)
            break;

        sb.append((char) (base + k));
    }

    return sb.toString();
}

输出:

-9223372036808280701
Using base: 'Z'
stack
-9223372036853943469
Using base: 'b'
over
-9223372036852834412
Using base: 'e'
flow
-9223372036838149518
Using base: 'd'
rulez
Took 7087 ms

我对此很感兴趣,我在字典单词列表上运行了这个随机单词生成器。范围:整数.MIN_VALUE到整数.MAX_VALUE

我得到了15131次点击。

int[] arrInt = {-2146926310, -1885533740, -274140519, 
                -2145247212, -1845077092, -2143584283,
                -2147483454, -2138225126, -2147375969};

for(int seed : arrInt){
    System.out.print(randomString(seed) + " ");
}

打印

the quick browny fox jumps over a lazy dog