Hi, I’m crooked.

I recently saw a piece of code on StackOverflow, how to say.

Is the first look at a face meng force, understand to kneel down directly!

I’ll show you what the problem is on StackOverflow, and then I’ll introduce this code:

Stackoverflow.com/questions/1…

It was a very simple question, one sentence:

Who can explain to me why this code prints hello World using random strings?

The code is also very simple, LET me take it out for you to run:

public class MainTest { public static void main(String[] args) { System.out.println(randomString(-229985452) + " " + randomString(-147909649)); } 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(); }}Copy the code

You can also paste the above code into your runtime environment and run it to see if it also prints hello world:

I asked you: Even if you were given the code, did you see Hello World at first sight?

High praise to answer

Gao Zan answer is also particularly simple, so two words.

Let me translate for you. This guy says:

When we call the constructor of Random, we are given a “seed” argument. Such as -229985452 or -147909649 in this example.

Random then generates a Random number starting with the specified seed value.

Each Random object constructed with the same seed will generate numbers in the same pattern.

I don’t see it very clearly, do I?

It doesn’t matter, I’ll give you a piece of code so you can see what the above paragraph is about:

public static void main(String[] args) {

    randomString(-229985452);
    System.out.println("------------");
    randomString(-229985452);

}

private static void randomString(int i) {
    Random ran = new Random(i);
    System.out.println(ran.nextInt());
    System.out.println(ran.nextInt());
    System.out.println(ran.nextInt());
    System.out.println(ran.nextInt());
    System.out.println(ran.nextInt());

}
Copy the code

This code, running on my machine, looks like this:

You run it, you run it like this.

Why is that?

The answer is right here on Javadoc:

If two instances of Random are created with the same seed, and the same sequence of method calls is made to each instance, they will generate and return the same sequence of numbers.

In the code above, two -229985452 are the same seed, and three nextInt() calls are the same call sequence.

So, they generate and return the same, seemingly random numbers.

Our normal usage in the program would be like this:

At new Random(), a value is not specified.

We all know that Random is a pseudo-random algorithm, and the seed parameter specified during construction is a more pseudo-pseudo-random algorithm.

Because if I can guess your seed, or if your seed leaks, then I can theoretically guess your random number generation sequence.

I’ve demonstrated this in the previous code.

Let’s look at the problem

Having explained the key to seed a little bit earlier, let’s go back to the problem of tasting it, and maybe we can see some clues.

Let’s look at the code inside this loop.

First nextInt(27) qualifies that the currently returned number k must be a number between [0,27).

If 0 is returned, the loop ends, if not zero. Do a type conversion.

What follows is a cast of type CHAR.

When you see a number converted to a char, you should automatically think of ASCII:

From the ASCII code table, we can see that “96” is this symbol right here:

So, the scope of this code is [96+ 1,96 +26] :

‘`’ + k

That is, [97,122], which corresponds to ASCII a-z.

So, I’ll take you through the demo code again.

First new Random(-229985452). NextInt (27) returns the first five returns like this:

The first five returns from new Random(-147909649).nextint (27) look like this:

So, if you look at the ASCII table, you can see the corresponding letters:

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\

Now, the reason why this enigmatic piece of code output “Hello world” is clear to me.

I see. It’s just a trick.

Then there was a comment below the question that showed me another way to open it:

You can specify hello world, so in theory I can specify other words.

4. The sentence: “The quick browny fox jumps over a lazy dog.”

A quick brown fox jumps over a lazy dog.

But, as you know, my English level is relatively high, so you can see at a glance that this phrase must be difficult here.

So I looked it up:

It’s got a story. It’s tricks in tricks.

You see learn sand carving technology also incidentally enrich their English skills, kill multiple birds with one stone, this moment should not give me a thumbs up at the end of the article, point “looking at” what?

After watching my brother’s Quick Brown Fox example, I have a new idea.

Since it can type out all the letters, can I also type out the specific phrases I want?

Things like I am fine thank you and you.

The code for finding the seed that corresponds to a given word has been written by “good samaritans” in the answer to this question.

I’ll just paste it in, or you can just grab it and use it:

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) { for (int i = 0; i < input.length; i++) { if (input[i] ! = pool[i]) continue label; } return seed; } } throw new NoSuchElementException("Sorry :/"); }Copy the code

So I’m looking for the aforementioned phrase, and it’s easy:

And when it ran, I could tell I was spending a lot of time searching for the word “thank.”

Why is that?

I’ll tell you a story. There’s only one sentence you’ll know:

Given enough time, a monkey can knock out Shakespeare.

We have the generateSeed method here, which is equivalent to this monkey. And the word thank is Shakespeare.

In generateSeed’s method, it’s just a matter of how long it takes to get a thank from the 26 letters in continuous permutations.

The longer the word, the longer it takes.

For example, I have come to congratulate. This is such a long word. I have been running for 23 hours since 00:05:

But theoretically, given enough time, the seed will be found.

At this point, you should be fully aware of why the previous code prints Hello World as a random string.

The source code

You think I’m gonna show you the source code?

No, I mainly take you to eat melons.

First, take a look at the Random no-argument constructor:

It’s a “no arguments” shell, but it’s actually creating a seed and calling the argument constructor.

It just builds with the “system.nanotime ()” variable to make the seed look random.

Wait, isn’t there a “seedUniquifier” method ahead?

Here’s how it works:

Boy, the first time I saw it, my head exploded. There were two magic numbers:

181783497276652981L

8682522807148012L

You can’t read this shit, can you?

Up in the air, StackOverflow.

A quick search will find this place:

Stackoverflow.com/questions/1…

In this question, he said that he was also confused by these two numbers. He searched online, but there was very little relevant information. But I found a paper that mentioned one of the magic numbers that came close:

The numbers mentioned in the paper are as follows:

See?

This Java source code in front of the number is missing a “1” ah, why it is wrong, should not be a copy of the wrong time?

One of the top responses was this:

“It does look like a mistake.”

A little interesting, you say this is to write Java source code elder brother copy code when the hand shook, I will energize.

Immediately go to the Java Bug page and search for that string of numbers, and there are some unexpected results:

Bugs.openjdk.java.net/browse/JDK-…

In the description of the bug, he drew my attention to this place in the source code:

It turns out that the notes in this place represent a paper, so the paper must contain the source of the number.

Wait, why does the title of this paper sound familiar to me?

Here’s a link from StackOverflow:

Look at the title of this paper and the comments here in Java say the same thing:

It has to be the same thing, just one lowercase and one uppercase.

So, here is the real hammer, is indeed the beginning to write the Java source code of the old brother copy numbers when the hand shook, less copy a “1”.

And I can even imagine when writing this part of the source code, the old brother “1181783497276652981” this number stuck over, found: ah, this front how there are two 1 ah, the whole repeat, delete it.

What’s the problem with removing the “1”?

Calling new Random() simultaneously is not Random enough.

This I did not go to the research, you can have a look, I am only responsible for taking you to eat melon.

So, based on this “melon”, the official change to the code:

There is a “1” difference between JDK 15 and JDK 8:

And regarding Random numbers, Random is rarely used nowadays.

ThreadLocalRandom. Doesn’t it smell good?

What, you say no?