Hello, I am crooked.
I recently saw a piece of code on stackoverflow, how to say it.
At first glance, I was stunned, but I immediately knelt down when I understood it!
I'll take you to see what this question on stackoverflow is, and then lead to this code:
https://stackoverflow.com/questions/15182496/why-does-this-code-using-random-strings-print-hello-world
The question is very simple, in one sentence:
Can anyone explain to me: why does this code print out hello world with a random string?
The code is also very simple, I took 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();
}
}
You can also paste the above code directly into your running environment and run it to see if it also outputs hello world:
I will ask you: even if the code is given to you, are you dumbfounded when you see hello world for the first time?
high praise answer
Gao Zan's answer is also very simple, just two sentences.
Let me translate it for you, this guy said:
When we call the Random constructor, we are given a "seed" parameter. For example in this example: -229985452 or -147909649.
Then Random will generate random numbers starting from the specified seed value.
And each Random object constructed with the same seed will generate numbers according to the same pattern.
I didn't see it very clearly, did I?
It doesn't matter, I will give you a piece of code, and you will suddenly realize what the above paragraph is saying:
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());
}
This code, running on my machine, looks like this:
If you take it and run it, your running result must be like this.
Why is this?
The answer is written in the 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 above code two -229985452
are the same seed, and three calls nextInt()
are the same call sequence.
So, they generate and return the same, seemingly random number.
And our normal usage in the program should be like this:
When new Random() is called, a value is not specified.
We all know that Random is a pseudo-random algorithm, and the one that specifies the seed parameter during construction is a more pseudo-random algorithm.
Because if I can infer your seed, or your seed is leaked, then theoretically I can infer your random number generation sequence.
This I have demonstrated in the previous code.
Look at the problem again
After explaining the key point of "seed" a little earlier, we will go back to the question of Yipin, and we can probably see some clues.
Mainly look at the code inside this loop.
First, nextInt(27) is limited, and the current returned number k must be a number between [0,27).
If it returns 0, the loop ends, if not zero. Then do a type conversion.
Next is a cast to the char type.
When you see the conversion of numbers to char types, you should think of ascii code by conditioned reflex:
From the ascii code table, we can see that "96" is the symbol here:
So, the range of the following code is [96+1, 96+26]:
'`' + k
That is [97,122], which is the az corresponding to the ascii code.
So, let me take you to disassemble the demo code above.
First, the first five returns of new Random(-229985452).nextInt(27) are as follows:
And the first five returns of new Random(-147909649).nextInt(27) are like this:
Therefore, looking at the ascii code 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, as to why this enigmatic piece of code outputs "hello world", is it like a clear mirror in my heart?
See through it, it's just a little trick.
Then there is a comment below this question, which led me to see another way to open it:
You can specify to print hello world, then theoretically I can specify other words as well.
For example, this old man typed a phrase: the quick browny fox jumps over a lazy dog.
Literally translated, it would be "the nimble brown fox steps over the lazy dog", which seems like a no-brainer.
But, you know, my English is relatively high, and I can see at a glance that this phrase is definitely not easy here.
So I checked:
Sure enough, it is a bit of a story, belonging to tricks in tricks.
You can see that while learning sand sculpture techniques, you also enriched your English skills by the way. You can kill multiple birds with one stone. You can't give me a like or "watching" at the end of this article?
After reading this dude's quick brown fox example, I have a new idea.
Since it can type all the letters, can I also type the specific phrase I want?
Something like i am fine thank you and you.
In the answer to this question, the code for the function of finding the seed corresponding to the specified word has already been written by a "good person" for us.
I will paste it directly, you can also use it directly:
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 :/");
}
Then I'm looking for the aforementioned phrase, which is very simple:
And when I ran it, I clearly felt that it took a lot of time to search for the word "thank".
Why?
Let me tell you a story, only one sentence, you must have heard:
As long as the time is long enough, the monkey can knock out a "Shakespeare".
Our generateSeed method here is equivalent to this monkey. The word thank is "Shakespeare".
In the generateSeed method, through the continuous arrangement and combination of 26 letters, "thank" can always be arranged, but the time is short.
The longer the word, the longer it will take.
For example, let me give a congratulations, such a long word, I have run from 00:05 for 23 hours and still haven’t run out:
But in theory, as long as there is enough time, the seed will be found.
At this point, you should fully understand why the aforementioned code uses a random string to print out hello world.
source code
You thought I was going to take you to read the source code?
No, I mainly take you to eat melon.
First, take a look at the no-argument constructor of Random:
Good guy, it turned out to be a "parameterless" shell. In fact, I created a seed myself, and then called the parameterized construction method.
It's just that the variable "System.nanoTime()" was added when it was built, which made the seed look a little random.
Wait, isn't there a "seedUniquifier" method in front of it?
The method is like this:
Dude, my head got big when I first saw it, there are two "magic numbers" in it:
181783497276652981L
8682522807148012L
Can't understand this thing?
Indecisive, stackoverflow.
A quick search will find this place:
https://stackoverflow.com/questions/18092160/whats-with-181783497276652981-and-8682522807148012-in-random-java-7
In this question, he said that he was also very confused about these two figures. After looking around on the Internet, there was very little relevant information. But I found a paper that mentioned one of the very close "magic numbers":
The numbers mentioned in the paper are as follows:
Do you see it?
The number in the Java source code is missing a "1" in front of it. What's going on? Could it be that I made a mistake when copying it?
A great answer below is this:
"It does look like a copying error."
It's a bit interesting, you want to say that this is the brother who wrote the Java source code and his hands trembled when he copied the code, and I'm excited.
Immediately go to the Java Bug page and search for the string of numbers, and there is really an unexpected harvest:
https://bugs.openjdk.java.net/browse/JDK-8201634
In the description of the bug, he drew my attention to this place in the source code:
It turns out that the annotations in this place represent a paper, so there must be a source of this number hidden in this paper.
Wait, why do I feel that the name of this paper is a bit familiar?
The link mentioned in the previous stackoverflow, click on it is a paper address:
Look at the name of this paper and the comments in Java here are the same thing:
That must be the same thing, just one lowercase and one uppercase.
So, here is the real hammer. It is indeed that the old man who first wrote the source code of Java trembled when he copied the numbers, and he copied one less "1".
And I can even imagine that when I wrote this part of the source code, the old man pasted the number "1181783497276652981" and found: Hey, why are there two 1s in front of this, it's repeated, delete it.
As for the deletion of this "1", what problems will it bring?
Anyway, there is a problem associated here, that is: the randomness of concurrent calls to new Random() is not large enough.
I didn't go to study this, if you are interested, you can go and have a look. I am only responsible for taking you to eat melons.
Therefore, based on this "melon", the official modified this code once:
I happen to have the code for the JDK 15 and JDK 8 versions here. I looked at it, and it was really a "1" difference:
And about random numbers, Random is rarely used now.
It's directly on ThreadLocalRandom, isn't it fragrant?
What, you said no?
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。