[java] 임의 문자열을 사용하는이 코드가 “hello world”를 인쇄하는 이유는 무엇입니까?

다음 인쇄 문은 “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();
}



답변

의 인스턴스가 java.util.Random특정 시드 매개 변수 (이 경우 -229985452또는 -147909649) 로 구성된 경우 해당 시드 값으로 시작 하는 난수 생성 알고리즘을 따릅니다 .

Random동일한 시드로 구성된 모든 항목은 매번 동일한 패턴의 숫자를 생성합니다.


답변

다른 답변은 이유를 설명하지만 여기에 방법이 있습니다.

주어진 인스턴스 Random:

Random r = new Random(-229985452)

r.nextInt(27)생성 되는 처음 6 개의 숫자 는 다음과 같습니다.

8
5
12
12
15
0

r.nextInt(27)주어진 6 개의 숫자 는 다음 Random r = new Random(-147909649)과 같습니다.

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


답변

그냥 여기 두겠습니다 여유가 많은 (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


답변

여기의 모든 사람들은 코드의 작동 방식을 설명하고 자신의 예제를 구성 할 수있는 방법을 보여주는 훌륭한 작업을 수행했지만, 무차별 대입 검색이 결국에는 찾게 될 솔루션이 합리적으로 예상되는 이유를 보여주는 정보 이론적 답변이 있습니다.

26 개의 소문자가 알파벳을 형성합니다 Σ. 길이가 다른 단어를 생성 할 수 있도록 확장 알파벳을 만들기 위해 종료 기호 를 추가합니다 Σ' := Σ ∪ {⊥}.

α대해 균일하게 분포 된 랜덤 변수 인 기호와 X를 사용합니다 Σ'. 해당 기호 P(X = α)및 해당 정보 내용 을 얻을 수있는 확률 I(α)은 다음과 같습니다.

P (X = α) = 1 / | Σ ‘| = 1/27

I (α) = -log₂ [P (X = α)] = -log₂ (1/27) = 로그 ₂ (27)

단어 ω ∈ Σ*와 그에 ⊥-대응 하는 단어의 ω' := ω · ⊥ ∈ (Σ')*경우

I (ω) : = I (ω ‘) = | ω’| * log₂ (27) = (| ω | + 1) * log₂ (27)

PRNG (Pseudorandom Number Generator)는 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


답변

이 씨앗을 찾기위한 빠른 프로그램을 작성했습니다.

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.


답변

나는 이것에 흥미를 느꼈다. 나는이 임의의 단어 생성기를 사전 단어 목록에서 실행했다. 범위 : 정수 .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 


답변

대부분의 난수 생성기는 실제로 “의사 난수”입니다. 선형 일치 생성기 또는 LCG입니다 ( http://en.wikipedia.org/wiki/Linear_congruential_generator ).

LCG는 고정 된 시드가 주어지면 상당히 예측 가능합니다. 기본적으로 첫 번째 문자를 제공하는 시드를 사용하고 대상 문자열에서 다음 문자를 칠 때까지 다음 int (char)를 계속 생성하는 앱을 작성하고 LCG를 호출 해야하는 횟수를 적어 두십시오. 각각의 모든 문자를 생성 할 때까지 계속하십시오.