ทำไมรหัสนี้โดยใช้สตริงสุ่มพิมพ์“ สวัสดีโลก”


1769

คำสั่งพิมพ์ต่อไปนี้จะพิมพ์ "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();
}

158
เมล็ดพันธุ์เหล่านั้นก็เกิดขึ้นได้อย่างสมบูรณ์แบบ การสุ่มไม่ได้สุ่มอย่างแท้จริงมันคือการสุ่มเทียม
Doorknob

341
มันใช้งานได้เหมือนที่คนอื่นพูดเพราะสุ่มไม่ได้ สำหรับฉันแล้วคำถามที่น่าสนใจกว่านั้นคือคนที่เขียนว่าเดรัจฉานบังคับมันหรือมีวิธีง่ายๆในการทำนายสิ่งที่สุ่มสร้างขึ้นสำหรับค่า N ต่อไปสำหรับเมล็ดที่กำหนด การบังคับให้เดรัจฉานเป็นเรื่องง่ายและด้วยฮาร์ดแวร์ที่ทันสมัยไม่ควรใช้เวลานานเกินไป เมื่อระบุว่าเป็นแบบคงที่คุณสามารถกระจายการค้นหาข้ามเครือข่ายได้อย่างง่ายดาย
jmoreno

78
ผมสงสัยว่าวัตถุประสงค์ของการในn for (int n = 0; ; n++)พวกเขาสามารถใช้for(;;)หรือwhile(true)แทน!
Thai.Fouad

13
ในลำดับสุ่มอย่างแท้จริงทุกสตริงที่เป็นไปได้ในที่สุดจะปรากฏขึ้น ในลำดับการสุ่มหลอกที่มีคุณภาพสูงสามารถคาดหวังได้ทุกสตริงความยาวที่เป็นไปได้ (log_s (N) - n) บิต (โดยที่ N คือจำนวนบิตในสถานะภายใน PRNGs และ n เป็นจำนวนน้อยให้เลือก 8 เพื่อความสะดวก ) เพื่อปรากฏในรอบ รหัสนี้ได้รับความช่วยเหลือจากการใช้จุดเริ่มต้นฮาร์ดโค้ดที่เลือกได้อย่างอิสระ (ค่าของ backtick ตัวละคร) ซึ่งได้รับเกือบทั้ง 8 บิตกลับ
dmckee --- ผู้ดูแลอดีตแมว

13
นี่คือจากการโพสต์ฉันเขียนสองสามปีที่ผ่านมา vanillajava.blogspot.co.uk/2011/10/randomly-no-so-random.html
Peter Lawrey

คำตอบ:


917

เมื่ออินสแตนซ์ของjava.util.Randomถูกสร้างด้วยพารามิเตอร์เมล็ดเฉพาะ (ในกรณีนี้-229985452หรือ-147909649) มันจะติดตามอัลกอริทึมการสร้างหมายเลขสุ่มที่เริ่มต้นด้วยค่าเมล็ดนั้น

ทุกสิ่งที่Randomสร้างด้วยเมล็ดเดียวกันจะสร้างรูปแบบของตัวเลขที่เหมือนกันทุกครั้ง


8
@Vulcan - javadoc บอกว่าเมล็ด 48 บิต docs.oracle.com/javase/7/docs/api/java/util/Random.html นอกจากนี้เมล็ดจริงยังมีค่า 32 บิต
Stephen C

80
องค์ประกอบของลำดับตัวเลขสุ่มแต่ละครั้งจะถูกนำมาโมดูโล 27 และมี 6 องค์ประกอบในแต่ละและ"hello\0" "world\0"หากคุณคิดว่าเครื่องกำเนิดไฟฟ้าแบบสุ่มอย่างแท้จริงอัตราต่อรองจะเป็น 1 ใน 27 ^ 6 (387,420,489) ในการรับลำดับที่คุณกำลังมองหา - ดังนั้นมันค่อนข้างน่าประทับใจ แต่ไม่น่าเหลือเชื่อ!
รัสเซล Borogove

17
@RussellBorogove: แต่ด้วยราคาที่เป็นไปได้และเมล็ดที่เป็นไปได้ 2 ^ 64 มีค่าเมล็ดที่คาดว่าจะมี 47,600 ล้านค่าที่ให้ลำดับนั้น มันเป็นเพียงเรื่องของการค้นหา
dan04

8
@ dan04 - ฉันไม่เต็มใจที่จะทำการประเมิน; ขึ้นอยู่กับการนำไปใช้ของ PRNG ขนาดของคำเมล็ดอาจไม่เท่ากับขนาดของรัฐและเส้นทางลำดับอาจไม่กระจายเท่า ๆ กัน แต่ก็ยังมีโอกาสที่ดีและถ้าคุณไม่สามารถหาคู่คุณสามารถลองอีกครั้งกับปลอกที่แตกต่างกัน ( "Hello" "World") หรือใช้122-kแทน96+kหรือ ...
Russell Borogove

7
@ ThorbjørnRavnAndersen Javadocระบุว่า "ระบุอัลกอริทึมเฉพาะสำหรับคลาส Random การใช้งาน Java ต้องใช้อัลกอริทึมทั้งหมดที่แสดงที่นี่สำหรับคลาส Random เพื่อประโยชน์ในการพกพาของ Java code อย่างสมบูรณ์"
FThompson

1137

คำตอบอื่น ๆ อธิบายว่าทำไม แต่นี่คือวิธี

รับตัวอย่างของRandom:

Random r = new Random(-229985452)

ตัวเลข 6 ตัวแรกที่r.nextInt(27)สร้างคือ:

8
5
12
12
15
0

และตัวเลข 6 ตัวแรกที่r.nextInt(27)สร้างขึ้นมา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

48
อวด, new Random(-229985452).nextInt(27)เสมอกลับ 8.
user253751

1
@immibis ทำไม? ฉันหมายถึง Random () ควรส่งคืนหมายเลขสุ่มทุกครั้งไม่ใช่ชุดคำสั่งแก้ไขหรือไม่
roottraveller

5
@rootTraveller สำหรับการเริ่มต้นnew Random()อย่าส่งกลับตัวเลขเลย
user253751

2
มีวิธีคำนวณเมล็ดเหล่านี้หรือไม่? จะต้องมีตรรกะบางอย่าง ... หรือมันเป็นเพียงกำลังดุร้าย
Sohit Gore

2
@SohitGore เนื่องจากค่าเริ่มต้นของ Java Randomนั้นไม่มีความปลอดภัยในการเข้ารหัส (ฉันค่อนข้างแน่ใจว่ามันเป็น Mersenne Twister แต่ไม่ได้อ้างถึงฉัน) อาจเป็นไปได้ที่จะทำงานจาก "ฉันต้องการตัวเลขเหล่านี้" ถึง "นี่คือ ฉันจะใช้เมล็ดพันธุ์ " ฉันได้ทำสิ่งที่คล้ายกับเครื่องกำเนิดเชิงเส้นเชิงเส้น C แบบมาตรฐาน
คดีฟ้องร้องกองทุนโมนิก้า

280

ฉันจะทิ้งไว้ที่นี่ ใครมีเวลาเหลือเฟือ (CPU) ในการทดลองอย่าลังเลที่จะทดลอง :) นอกจากนี้หากคุณมีความเชี่ยวชาญในการแยก 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

24
@OneTwoThree หมายในช่วงnextInt(27) [0, 26]
Eng.Fouad

30
@Vulcan เมล็ดส่วนใหญ่มีค่าใกล้เคียงกับค่าสูงสุดเช่นเดียวกับถ้าคุณเลือกตัวเลขสุ่มระหว่าง 1 ถึง 1,000 ส่วนใหญ่หมายเลขที่คุณเลือกจะมีตัวเลขสามหลัก มันไม่น่าแปลกใจเมื่อคุณคิดถึง :) :)
Thomas

18
@Vulcan ในความเป็นจริงถ้าคุณทำคณิตศาสตร์คุณจะเห็นว่าพวกเขากำลังใกล้เคียงกับค่าสูงสุดเป็นศูนย์ (ฉันคิดว่าเมล็ดจะถูกตีความว่าเป็นไม่ได้ลงนามในรหัสการสร้าง) แต่เนื่องจากจำนวนหลักเติบโตขึ้นอย่างลอการิทึมโดยมีค่าจริงตัวเลขจึงดูใกล้เคียงจริง ๆ เมื่อมันไม่ใช่
โทมัส

10
คำตอบที่ดี และสำหรับคะแนนโบนัสคุณสามารถหาเมล็ดที่จะเริ่มต้นแบบสุ่มซึ่งจะสร้างลำดับ 4 เมล็ดที่จำเป็นสำหรับการเริ่มต้นของ randoms สุดท้ายหรือไม่
Marek

13
@ Marek: ฉันไม่คิดว่าเทพเจ้าหลอกหลอกจะเห็นด้วยกับพฤติกรรมดังกล่าว
Denis Tulskiy

254

ทุกคนที่นี่ทำงานได้อย่างยอดเยี่ยมในการอธิบายวิธีการทำงานของรหัสและการแสดงว่าคุณสามารถสร้างตัวอย่างของคุณเองได้อย่างไร แต่นี่คือคำตอบทางทฤษฎีที่แสดงข้อมูลว่าทำไมเราสามารถคาดหวังได้ว่า

26 Σตัวอักษรกรณีที่ต่ำกว่าที่แตกต่างกันในรูปแบบตัวอักษรของเรา ในการอนุญาตให้สร้างคำของความยาวที่แตกต่างกันเรายังเพิ่มสัญลักษณ์ตัว Terminator ให้ผลผลิตขยายตัวอักษรΣ' := Σ ∪ {⊥}

อนุญาตαเป็นสัญลักษณ์และ X Σ'ตัวแปรสุ่มกระจายอย่างสม่ำเสมอมากกว่า ความน่าจะเป็นที่ได้รับสัญลักษณ์นั้นP(X = α)และเนื้อหาข้อมูลI(α)นั้นได้รับจาก:

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

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

สำหรับคำω ∈ Σ*และ⊥-คู่ที่ถูกยกเลิกω' := ω · ⊥ ∈ (Σ')*เรามี

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

เนื่องจาก Pseudorandom Number Generator (PRNG) เริ่มต้นด้วยเมล็ดพันธุ์แบบ 32 บิตเราจึงสามารถคาดหวังคำที่มีความยาวสูงสุดได้

λ = floor [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


ทฤษฎีข้อมูลของฉันอ่อนแอ แต่ฉันชอบการพิสูจน์นี้ ใครบางคนสามารถอธิบายแลมบ์ดาบรรทัดให้ฉันเห็นได้ชัดว่าเรากำลังแบ่งเนื้อหาข้อมูลของหนึ่งกับอื่น ๆ แต่ทำไมนี้ให้เราคำยาวของเราหรือไม่ อย่างที่ฉันบอกว่าฉันค่อนข้างเป็นสนิมดังนั้นขอโทษที่ถามชัดเจน (NB เป็นสิ่งที่ต้องทำกับขีด จำกัด นอนส์ - จากรหัสเอาท์พุท)
Mike HR

1
@ MikeH-R แลมบ์ดาไลน์เป็นI(⍵)สมการที่จัดใหม่ I(⍵)คือ 32 (บิต) และ|⍵|เปลี่ยนเป็น 5 (สัญลักษณ์)
iceman

67

ฉันเขียนโปรแกรมด่วนเพื่อค้นหาเมล็ดเหล่านี้:

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);
                }
            }
        }
    }
}

ฉันให้มันทำงานในพื้นหลังตอนนี้ แต่มันก็พบคำที่เพียงพอสำหรับ pangram คลาสสิก:

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);
        }
    }
}

( สาธิตเกี่ยวกับ ideone )

ps -727295876, -128911, -1611659, -235516779.


35

ฉันรู้สึกทึ่งกับสิ่งนี้ฉันจึงใช้เครื่องมือสร้างคำแบบสุ่มนี้ในรายการคำศัพท์ในพจนานุกรม ช่วง: Integer.MIN_VALUE ถึง Integer.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 

7
คุณทำให้คนในวันของฉัน: DI ลองด้วย Long.Min / Max และค้นหาชื่อของเพื่อนร่วมงานของฉันและพบเฉพาะปีเตอร์: (ปีเตอร์ 4611686018459,711,911,968,768,711,711,711,711,711,711,711,711,711,711,711,711,711,711,711,711,711,711,911,711,711,711,711,911,711,7117,798,797,736,8361,436,986,9861] 4611686017645756173 peter 781631731 peter 4611686019209019635 peter -9223372036073144077 peter -4611686017420317288 peter -9223372035847705192)
Marcel

25

ในความเป็นจริงแล้วตัวกำเนิดตัวเลขแบบสุ่มส่วนใหญ่จะเป็น "หลอกสุ่ม" พวกมันคือเครื่องกำเนิดไฟฟ้าเชิงเส้นตรงหรือ LCG ( http://en.wikipedia.org/wiki/Linear_congruential_generator )

LCG สามารถคาดการณ์ได้ค่อนข้างมากเมื่อมีเมล็ดตายตัว โดยทั่วไปให้ใช้เมล็ดพันธุ์ที่ให้อักษรตัวแรกของคุณจากนั้นเขียนแอปที่ยังคงสร้าง int (char) ตัวต่อไปจนกว่าคุณจะตีตัวอักษรถัดไปในสตริงเป้าหมายของคุณและจดจำนวนครั้งที่คุณต้องเรียกใช้ LCG ทำต่อไปจนกว่าคุณจะสร้างจดหมายทุกฉบับ


3
ตัวอย่างของตัวสร้างตัวเลขสุ่มหลอกไม่ใช่
chiliNUT

1
@chiliNUT ตัวสร้างดังกล่าวเป็นอุปกรณ์เสริมภายนอก หลอดไฟอิเล็กทรอนิกส์บางส่วน หรือบิตที่เขียนไม่ดีที่อ่าน 0 หรือ 1 คุณไม่สามารถสร้างตัวกำเนิดสัญญาณดิจิตอลที่บริสุทธิ์ของตัวเลขสุ่มได้อัลกอริธึมดิจิตอลไม่ใช่การสุ่มพวกมันแม่นยำอย่างแน่นอน
Gangnus

@chiliNUT ระบบปฏิบัติการหลายเก็บเอนโทรปี เช่นใน Linux คุณสามารถใช้/dev/urandomอุปกรณ์เพื่ออ่านข้อมูลแบบสุ่ม อย่างไรก็ตามนี่เป็นทรัพยากรที่หายาก ดังนั้นข้อมูลแบบสุ่มดังกล่าวมักจะใช้ในการหว่าน PRNG
Adrian W

@AdrianW Wikipedia บอกว่าurandomยังคงเป็นแบบสุ่มหลอกen.wikipedia.org/wiki//dev/random
chiliNUT

1
ใช่ แต่มันจะถูกเข้ารหัสรักษาความปลอดภัยซึ่งหมายความว่าหนึ่งไม่สามารถทำให้การโจมตีแรงเดรัจฉาน (ชอบที่จะหาเมล็ดพันธุ์สำหรับการ "สุ่ม" ลำดับ "Hello World") /dev/randomกับลำดับสุ่มสร้างขึ้นจาก บทความที่ฉันอ้างถึงข้างต้นกล่าวว่าเคอร์เนล Linux สร้างเอนโทรปีจากการกำหนดเวลาแป้นพิมพ์การเคลื่อนไหวของเมาส์และการกำหนดเวลา IDE และทำให้ข้อมูลอักขระแบบสุ่มพร้อมใช้งานสำหรับกระบวนการระบบปฏิบัติการอื่น ๆ ผ่านไฟล์พิเศษ / dev / random และ / dev / urandom ให้ฉันเชื่อว่ามันเป็นการสุ่มอย่างแท้จริง อาจเป็นว่าไม่ถูกต้องทั้งหมด แต่/dev/randomอย่างน้อยก็มีเอนโทรปี
Adrian W

23

เนื่องจากมัลติเธรดเป็นเรื่องง่ายมากกับ 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();
    }
  }
}

หากต้องการ java noob อย่างฉันคุณต้องต่อท้ายหมายเลขผลลัพธ์ด้วยLและเปลี่ยนประเภทอาร์กิวเมนต์เป็นlongเช่นrandomString(long i)เพื่อเล่น :)
ผลไม้

21

สุ่มกลับลำดับเดียวกันเสมอ มันใช้สำหรับการสับอาร์เรย์และการดำเนินการอื่น ๆ เป็นวิธีเรียงสับเปลี่ยน

ในการรับลำดับที่แตกต่างกันจำเป็นต้องเริ่มต้นลำดับในบางตำแหน่งที่เรียกว่า "seed"

RandomSting รับตัวเลขสุ่มในตำแหน่ง i (seed = -229985452) ของลำดับ "สุ่ม" จากนั้นใช้รหัสASCIIสำหรับอักขระ 27 ตัวถัดไปในลำดับหลังตำแหน่งเมล็ดจนกระทั่งค่านี้เท่ากับ 0 ซึ่งส่งคืน "hello" การดำเนินการเดียวกันนี้ทำสำหรับ "โลก"

ฉันคิดว่ารหัสไม่ทำงานสำหรับคำอื่น ๆ ผู้ชายที่ตั้งโปรแกรมที่รู้ลำดับสุ่มอย่างดี

มันเป็นโค้ดที่เกินบรรยายมาก!


10
ฉันสงสัยว่าเขา "รู้จักลำดับสุ่มดีมาก" มีความเป็นไปได้มากขึ้นที่เขาจะลองใช้เมล็ดที่มีค่าเป็นพัน ๆ ล้าน ๆ จนกว่าจะพบเมล็ดที่ใช้ได้
dan04

24
@ dan04 โปรแกรมเมอร์จริงไม่เพียง แต่ใช้ PRNG เท่านั้นพวกเขายังจำช่วงเวลาทั้งหมดได้ด้วยใจและแจกแจงค่าตามที่ต้องการ
โทมัส

1
"Random สุ่มคืนค่าลำดับเดียวกันเสมอ" - ใส่ () หลัง Random หรือแสดงเป็นรหัส นอกจากนั้นประโยคนั้นเป็นเท็จ
นักเลง

14

ตัวการคือคลาสสุ่มที่สร้างด้วยเมล็ดเดียวกันจะสร้างรูปแบบของตัวเลขที่เหมือนกันทุกครั้ง


12

มาจากคำตอบของDenis Tulskiyวิธีนี้สร้างเมล็ด

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 :/");
}

10

จากเอกสาร Java นี้เป็นคุณสมบัติที่มีเจตนาเมื่อระบุค่าเมล็ดสำหรับคลาสสุ่ม

หากอินสแตนซ์ของ Random ถูกสร้างขึ้นด้วยเมล็ดเดียวกันและมีการเรียกเมธอดลำดับเดียวกันสำหรับแต่ละรายการพวกเขาจะสร้างและส่งกลับลำดับตัวเลขที่เหมือนกัน เพื่อรับประกันคุณสมบัตินี้อัลกอริทึมเฉพาะจะถูกระบุไว้สำหรับคลาส Random การใช้งาน Java จะต้องใช้อัลกอริทึมทั้งหมดที่แสดงที่นี่สำหรับคลาส Random เพื่อประโยชน์ในการพกพาได้อย่างสมบูรณ์ของโค้ด Java

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

แม้ว่าแปลกคุณจะคิดว่ามีปัญหาด้านความปลอดภัยโดยนัยในการมีตัวเลข 'สุ่ม' ที่คาดการณ์ได้


3
นั่นคือเหตุผลที่ตัวสร้างเริ่มต้นของRandom"ตั้งค่าเมล็ดของตัวสร้างตัวเลขสุ่มเป็นค่าที่น่าจะแตกต่างจากการเรียกใช้ตัวสร้างอื่นนี้" ( javadoc ) ในการใช้งานในปัจจุบันนี้เป็นการรวมกันของเวลาปัจจุบันและตัวนับ
martin

จริง สันนิษฐานว่ามีกรณีการใช้งานจริงสำหรับการระบุค่าเมล็ดเริ่มต้นแล้ว ฉันเดาว่านี่เป็นหลักการปฏิบัติงานของ keyfobs ปลอมเหล่านั้นที่คุณจะได้รับ (ชุด RSA?)
deed02392

4
@ deed02392 แน่นอนว่ามีกรณีการใช้งานจริงสำหรับการระบุค่าเมล็ด หากคุณกำลังจำลองข้อมูลเพื่อใช้วิธีมอนเต้คาร์โลเพื่อแก้ปัญหานั่นเป็นเรื่องดีที่คุณจะสามารถสร้างผลลัพธ์ของคุณได้ การตั้งค่าเมล็ดเริ่มต้นเป็นวิธีที่ง่ายที่สุดในการทำเช่นนั้น
Dason


3

นี่คือการปรับปรุงเล็กน้อยสำหรับ Denis Tulskiy คำตอบ มันลดเวลาลงครึ่งหนึ่ง

public static long[] generateSeed(String goal, long start, long finish) {
    char[] input = goal.toCharArray();

    int[] dif = new int[input.length - 1];
    for (int i = 1; i < input.length; i++) {
        dif[i - 1] = input[i] - input[i - 1];
    }

    mainLoop:
    for (long seed = start; seed < finish; seed++) {
        Random random = new Random(seed);
        int lastChar = random.nextInt(27);
        int base = input[0] - lastChar;
        for (int d : dif) {
            int nextChar = random.nextInt(27);
            if (nextChar - lastChar != d) {
                continue mainLoop;
            }
            lastChar = nextChar;
        }
        if(random.nextInt(27) == 0){
            return new long[]{seed, base};
        }
    }

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

1

มันเป็นเรื่องของการป้อนข้อมูลเมล็ด เมล็ดเดียวกันให้ผลเหมือนกันตลอดเวลา แม้ว่าคุณจะเรียกใช้โปรแกรมของคุณซ้ำแล้วซ้ำอีกมันเป็นผลลัพธ์เดียวกัน

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());

}

เอาท์พุต

-755142161
-1073255141
-369383326
1592674620
-1524828502
------------
-755142161
-1073255141
-369383326
1592674620
-1524828502
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.