Tại sao mã này sử dụng chuỗi ngẫu nhiên in ấn hello hello world World?


1769

Các tuyên bố in sau đây sẽ in "xin chào thế giới". Bất cứ ai có thể giải thích điều này?

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

randomString()trông như thế này:

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
Vâng, những hạt giống đặc biệt chỉ xảy ra để làm việc hoàn hảo. Ngẫu nhiên không thực sự ngẫu nhiên, nó là giả danh.
Doorknob

341
Nó hoạt động, như những người khác đã nói, bởi vì ngẫu nhiên không. Đối với tôi, một câu hỏi thú vị hơn sẽ là người đã viết nó, vũ phu ép buộc nó, hoặc có một cách dễ dàng để dự đoán những gì ngẫu nhiên sẽ tạo ra cho các giá trị N tiếp theo cho một hạt giống nhất định. Buộc Brute rất dễ dàng và với phần cứng hiện đại không nên mất quá nhiều thời gian, vì vậy đó là một cách khả thi để thực hiện nó. Cho rằng nó là tĩnh, bạn thậm chí có thể dễ dàng phân phối tìm kiếm trên một mạng.
jmoreno

78
Tôi tự hỏi mục đích của ntrong for (int n = 0; ; n++). Họ có thể sử dụng for(;;)hoặc while(true)thay vào đó!
Eng.Fouad

13
Trong một chuỗi thực sự ngẫu nhiên, mọi chuỗi có thể cuối cùng sẽ xuất hiện. Trong chuỗi ngẫu nhiên giả chất lượng cao trên có thể mong đợi hợp lý mọi chuỗi có độ dài (log_s (N) - n) có thể (trong đó N là số bit ở trạng thái bên trong PRNG và n là một số nhỏ, hãy chọn 8 để thuận tiện ) để xuất hiện trong chu trình. Mã này nhận được một số trợ giúp từ việc sử dụng điểm bắt đầu được mã hóa cứng được chọn tự do (giá trị của ký tự backtick) nhận lại gần như toàn bộ 8 bit.
dmckee --- ex-moderator mèo con

13
Đây là từ một bài đăng tôi đã viết một vài năm trước đây. vanillajava.blogspot.co.uk/2011/10/randomly-no-so-random.html
Peter Lawrey

Câu trả lời:


917

Khi một thể hiện của java.util.Randomđược xây dựng với một tham số hạt giống cụ thể (trong trường hợp này -229985452hoặc -147909649), nó tuân theo thuật toán tạo số ngẫu nhiên bắt đầu bằng giá trị hạt giống đó.

Mỗi lần Randomxây dựng với cùng một hạt giống sẽ tạo ra cùng một kiểu số.


8
@Vulcan - javadoc nói rằng hạt giống là 48 bit. docs.oracle.com/javase/7/docs/api/java/util/Random.html . Và bên cạnh đó, các hạt giống thực tế là các giá trị 32 bit.
Stephen C

80
Mỗi phần tử của dãy số ngẫu nhiên được lấy theo modulo 27, và có 6 phần tử trong mỗi "hello\0""world\0". Nếu bạn giả sử một trình tạo ngẫu nhiên thực sự, tỷ lệ cược sẽ là 1 trong 27 ^ 6 (387.420.489) khi nhận được chuỗi bạn đang tìm kiếm - vì vậy nó khá ấn tượng nhưng không hoàn toàn gây chú ý!
Russell Borogove

17
@RussellBorogove: Nhưng với tỷ lệ cược đó và 2 ^ 64 hạt giống có thể, có 47,6 tỷ giá trị hạt giống dự kiến ​​sẽ đưa ra chuỗi đó. Đó chỉ là vấn đề tìm kiếm một.
dan04

8
@ dan04 - Tôi không sẵn sàng đưa ra ước tính đó; tùy thuộc vào việc triển khai PRNG, kích thước của từ giống có thể không bằng kích thước của trạng thái và các đường dẫn trình tự có thể không được phân bổ đều. Tuy nhiên, tỷ lệ cược chắc chắn là tốt và nếu bạn không thể tìm thấy một cặp bạn có thể thử lại với các vỏ khác nhau ( "Hello" "World") hoặc sử dụng 122-kthay vì 96+k, hoặc ...
Russell Borogove

7
@ ThorbjørnRavnAndersen Javadoc chỉ định rằng "các thuật toán cụ thể được chỉ định cho lớp Random. Việc triển khai Java phải sử dụng tất cả các thuật toán được hiển thị ở đây cho lớp Random, vì tính di động tuyệt đối của mã Java."
FThndry

1137

Các câu trả lời khác giải thích tại sao, nhưng đây là cách.

Cho một ví dụ về Random:

Random r = new Random(-229985452)

6 số đầu tiên r.nextInt(27)tạo ra là:

8
5
12
12
15
0

và 6 số đầu tiên r.nextInt(27)tạo ra được đưa ra Random r = new Random(-147909649)là:

23
15
18
12
4
0

Sau đó, chỉ cần thêm các số đó vào biểu diễn số nguyên của ký tự `(là 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
Về mặt giáo dục, new Random(-229985452).nextInt(27)luôn trả về 8.
user253751

1
@immibis tại sao? ý tôi là Random () nên trả về số ngẫu nhiên mỗi lần, không phải là số thứ tự sửa lỗi được đặt?
roottraveller

5
@rootTraveller Để bắt đầu, new Random()hoàn toàn không trả về một số nào.
dùng253751

2
Có cách nào những hạt giống này được tính toán? Phải có một số logic ... hoặc nó chỉ là vũ phu.
Sohit Gore

2
@SohitGore Cho rằng mặc định của Java Randomkhông bảo mật bằng mật mã (tôi khá chắc chắn đó là Mersenne Twister, nhưng đừng trích dẫn tôi về điều đó), có lẽ có thể hoạt động ngược từ "Tôi muốn những con số này" thành "đây là hạt giống tôi sẽ sử dụng ". Tôi đã làm một cái gì đó tương tự với máy phát đồng quy tuyến tính C tiêu chuẩn.
Vụ kiện của Quỹ Monica

280

Tôi sẽ chỉ để nó ở đây. Bất cứ ai có nhiều thời gian (CPU) rảnh rỗi, hãy thử nghiệm :) Ngoài ra, nếu bạn đã thành thạo một số fork-jo-fu để làm cho điều này đốt cháy tất cả các lõi CPU (chỉ là các luồng nhàm chán, phải không?), Hãy chia sẻ ma cua ban. Tôi sẽ đánh giá cao về nó.

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

Đầu ra:

-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)có nghĩa là trong phạm vi [0, 26].
Eng.Fouad

30
@Vulcan Hầu hết các hạt giống rất gần với giá trị tối đa, giống như nếu bạn chọn các số ngẫu nhiên trong khoảng từ 1 đến 1000, hầu hết các số bạn chọn cuối cùng sẽ có ba chữ số. Không có gì đáng ngạc nhiên, khi bạn nghĩ về nó :)
Thomas

18
@Vulcan Trong thực tế nếu bạn làm toán, bạn sẽ thấy chúng gần bằng giá trị tối đa bằng 0 (Tôi cho rằng hạt giống đang được hiểu là không dấu trong mã thế hệ). Nhưng vì số chữ số chỉ tăng theo logarit với giá trị thực, nên số này trông rất gần khi thực sự không có.
Thomas

10
Câu trả lời chính xác. Và để nhận điểm thưởng, bạn có thể tìm thấy một hạt giống sẽ khởi tạo Ngẫu nhiên sẽ tạo ra chuỗi 4 hạt giống cần thiết để khởi tạo các randoms cuối cùng không?
Marek

13
@Marek: Tôi không nghĩ các vị thần giả ngẫu nhiên sẽ tán thành hành vi đó.
Denis Tulskiy

254

Mọi người ở đây đã làm rất tốt khi giải thích cách mã hoạt động và chỉ ra cách bạn có thể xây dựng các ví dụ của riêng mình, nhưng đây là một câu trả lời lý thuyết thông tin cho thấy lý do tại sao chúng ta có thể mong đợi một giải pháp tồn tại mà tìm kiếm vũ phu cuối cùng sẽ tìm thấy.

26 chữ cái viết thường khác nhau tạo thành bảng chữ cái của chúng tôi Σ. Để cho phép tạo ra các từ có độ dài khác nhau, chúng tôi thêm một biểu tượng dấu kết thúc để tạo ra một bảng chữ cái mở rộng Σ' := Σ ∪ {⊥}.

Đặt αmột ký hiệu và X một biến ngẫu nhiên phân bố đồng đều trên Σ'. Xác suất có được biểu tượng đó P(X = α)và nội dung thông tin của nó I(α)được đưa ra bởi:

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

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

Đối với một từ ω ∈ Σ*⊥-đối tác chấm dứt của nó ω' := ω · ⊥ ∈ (Σ')*, chúng tôi có

Tôi (ω): = Tôi (ω ') = | ω' | * log (27) = (| ω | + 1) * log₂ (27)

Do Trình tạo số giả ngẫu nhiên (PRNG) được khởi tạo với hạt giống 32 bit, chúng tôi có thể mong đợi hầu hết các từ có độ dài lên đến

= sàn [32 / log₂ (27)] - 1 = 5

được tạo ra bởi ít nhất một hạt giống. Ngay cả khi chúng tôi tìm kiếm một từ có 6 ký tự, chúng tôi vẫn sẽ thành công khoảng 41,06% thời gian. Không quá xấu.

Trong 7 chữ cái, chúng tôi đang tìm kiếm gần 1,52%, nhưng tôi đã nhận ra điều đó trước khi thử:

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

Xem đầu ra: http://ideone.com/JRGb3l


lý thuyết thông tin của tôi là loại yếu nhưng tôi thích bằng chứng này. ai đó có thể giải thích dòng lambda cho tôi không, rõ ràng chúng ta đang phân chia nội dung thông tin của người này với người kia, nhưng tại sao điều này lại cho chúng ta độ dài từ? như tôi đã nói, tôi hơi thô lỗ nên xin lỗi vì đã hỏi điều hiển nhiên (NB có phải làm gì với giới hạn shannon - từ đầu ra mã)
Mike HR

1
@ MikeH-R Dòng lambda là I(⍵)phương trình được sắp xếp lại. I(⍵)là 32 (bit) và |⍵|hóa ra là 5 (ký hiệu).
iceman

67

Tôi đã viết một chương trình nhanh chóng để tìm những hạt giống này:

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

Bây giờ tôi có nó chạy trong nền, nhưng nó đã tìm thấy đủ từ cho một pangram cổ điển:

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

( Bản demo trên ideone. )

Thi thiên -727295876, -128911, -1611659, -235516779.


35

Tôi bị thu hút bởi điều này, tôi đã chạy trình tạo từ ngẫu nhiên này trong danh sách từ điển. Phạm vi: Số nguyên.MIN_VALUE đến Số nguyên.MAX_VALUE

Tôi đã nhận được 15131 lượt truy cập.

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

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

Bản in

the quick browny fox jumps over a lazy dog 

7
Bạn đã tạo ra người đàn ông của tôi: DI đã thử nó với Long.Min / Max và tìm kiếm tên của các đồng nghiệp của tôi và tìm kiếm tên của các đồng nghiệp của tôi và chỉ tìm thấy tên của các đồng nghiệp của tôi và chỉ tìm thấy peter: (peter 4611686018451441623 peter 24053719 peter -461168601840 4611686017645756173 peter 781631731 peter 4611686019209019635 peter -9223372036073144077 peter -4611686017420317288 peter 1007070616 peter -9223372035347705192)
Marcel

25

Trên thực tế, hầu hết các trình tạo số ngẫu nhiên là "giả ngẫu nhiên". Chúng là các Trình tạo cộng hưởng tuyến tính hoặc LCG ( http://en.wikipedia.org/wiki/Linear_congruential_generator )

LCG khá dễ đoán với một hạt giống cố định. Về cơ bản, hãy sử dụng một hạt giống cung cấp cho bạn chữ cái đầu tiên của bạn, sau đó viết một ứng dụng tiếp tục tạo int (char) tiếp theo cho đến khi bạn nhấn chữ cái tiếp theo trong chuỗi mục tiêu của bạn và ghi lại số lần bạn phải gọi LCG. Tiếp tục cho đến khi bạn tạo ra từng chữ cái.


3
ví dụ về trình tạo số ngẫu nhiên không giả
chiliNUT

1
@chiliNUT Máy phát điện như vậy là tiện ích bên ngoài. Một số đèn điện tử. Hoặc bit được viết xấu được đọc 0 hoặc 1. Bạn không thể thực hiện trình tạo số thuần túy của các số ngẫu nhiên, thuật toán kỹ thuật số KHÔNG phải là ngẫu nhiên, chúng hoàn toàn chính xác.
Gangnus

@chiliNUT Nhiều hệ điều hành thu thập entropy . Ví dụ: trong Linux, bạn có thể sử dụng /dev/urandomthiết bị để đọc dữ liệu ngẫu nhiên. Đây là một nguồn tài nguyên khan hiếm, tuy nhiên. Vì vậy, dữ liệu ngẫu nhiên như vậy thường được sử dụng để gieo hạt PRNG.
Adrian W

@AdrianW Wikipedia cho biết urandomvẫn là giả ngẫu nhiên en.wikipedia.org/wiki//dev/random
chiliNUT

1
Đúng, nhưng nó an toàn về mặt mật mã, có nghĩa là người ta không thể thực hiện các cuộc tấn công vũ phu (như tìm hạt giống cho chuỗi "thế giới" ngẫu nhiên "xin chào thế giới") với các chuỗi ngẫu nhiên được tạo từ /dev/random. Bài báo tôi đã trích dẫn ở trên cho biết Hạt nhân Linux tạo entropy từ thời gian bàn phím, chuyển động chuột và thời gian IDE và cung cấp dữ liệu ký tự ngẫu nhiên cho các quy trình hệ điều hành khác thông qua các tệp / dev / ngẫu nhiên và / dev / urandom đặc biệt. Điều đó cho tôi tin rằng nó thực sự ngẫu nhiên. Có thể đó không phải là hoàn toàn chính xác. Nhưng /dev/randomít nhất có chứa một số entropy.
Adrian W

23

Vì đa luồng rất dễ dàng với Java, đây là một biến thể tìm kiếm hạt giống bằng cách sử dụng tất cả các lõi có sẵn: 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 như tôi, bạn cần phải nhập số đầu ra Lvà thay đổi loại đối số thành long, tức là randomString(long i)để chơi xung quanh. :)
Trái cây

21

Ngẫu nhiên luôn trả về cùng một chuỗi. Nó được sử dụng để xáo trộn các mảng và các hoạt động khác như hoán vị.

Để có được các chuỗi khác nhau, cần thiết khởi tạo trình tự ở một số vị trí, được gọi là "hạt giống".

RandomSting lấy số ngẫu nhiên ở vị trí i (seed = -229985452) của chuỗi "ngẫu nhiên". Sau đó sử dụng mã ASCII cho 27 ký tự tiếp theo trong chuỗi sau vị trí hạt giống cho đến khi giá trị này bằng 0. Điều này trả về "xin chào". Các hoạt động tương tự được thực hiện cho "thế giới".

Tôi nghĩ rằng mã không hoạt động cho bất kỳ từ nào khác. Người mà lập trình mà biết rất rõ trình tự ngẫu nhiên.

Đó là mã geek rất tuyệt vời!


10
Tôi nghi ngờ nếu anh ta "biết rất rõ trình tự ngẫu nhiên". Nhiều khả năng, anh ta chỉ thử hàng tỷ hạt giống có thể cho đến khi tìm thấy một hạt giống có hiệu quả.
dan04

24
@ dan04 Các lập trình viên thực sự không chỉ đơn thuần sử dụng PRNG, họ ghi nhớ toàn bộ thời gian và liệt kê các giá trị khi cần thiết.
Thomas

1
"Ngẫu nhiên luôn trả về cùng một chuỗi" - đặt () sau Ngẫu nhiên hoặc hiển thị nó dưới dạng mã. Mặt khác câu là sai.
Gangnus

14

Hiệu trưởng là Lớp ngẫu nhiên được xây dựng với cùng một hạt giống sẽ tạo ra cùng một kiểu số.


12

Xuất phát từ câu trả lời của Denis Tulskiy , phương pháp này tạo ra hạt giống.

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

Từ các tài liệu Java, đây là một tính năng có chủ ý khi chỉ định giá trị hạt giống cho lớp Ngẫu nhiên.

Nếu hai trường hợp Ngẫu nhiên được tạo với cùng một hạt giống và cùng một chuỗi các lệnh gọi phương thức được thực hiện cho từng trường hợp, chúng sẽ tạo và trả về các chuỗi số giống hệt nhau. Để đảm bảo tính chất này, các thuật toán cụ thể được chỉ định cho lớp Ngẫu nhiên. Việc triển khai Java phải sử dụng tất cả các thuật toán được hiển thị ở đây cho lớp Random, vì tính di động tuyệt đối của mã Java.

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

Mặc dù kỳ lạ, bạn sẽ nghĩ rằng có những vấn đề bảo mật tiềm ẩn trong việc có các số 'ngẫu nhiên' có thể dự đoán được.


3
Đó là lý do tại sao hàm tạo mặc định của Random"đặt hạt giống của trình tạo số ngẫu nhiên thành một giá trị rất có thể khác biệt với bất kỳ lệnh gọi nào khác của hàm tạo này" ( javadoc ). Trong triển khai hiện tại, đây là sự kết hợp giữa thời gian hiện tại và bộ đếm.
martin

Thật. Có lẽ có những trường hợp sử dụng thực tế để xác định giá trị hạt giống ban đầu, sau đó. Tôi đoán đó là nguyên tắc hoạt động của các keyfobs giả mà bạn có thể nhận được (RSA?)
deed02392

4
@ deed02392 Tất nhiên có những trường hợp sử dụng thực tế để chỉ định giá trị hạt giống. Nếu bạn đang mô phỏng dữ liệu để sử dụng một số cách tiếp cận monte carlo để giải quyết vấn đề thì đó là một điều tốt để có thể tái tạo kết quả của bạn. Đặt hạt giống ban đầu là cách dễ nhất để làm điều đó.
Dason


3

Đây là một cải tiến nhỏ cho câu trả lời của Denis Tulskiy . Nó giảm thời gian xuống một nửa

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

Đó là tất cả về hạt giống đầu vào . Cùng một hạt giống cho kết quả giống nhau mọi lúc. Ngay cả khi bạn chạy lại chương trình của mình nhiều lần, nó cũng có cùng một đầu ra.

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

}

Đầu ra

-755142161
-1073255141
-369383326
1592674620
-1524828502
------------
-755142161
-1073255141
-369383326
1592674620
-1524828502
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.