为什么这段代码使用随机字符串打印"hello world"?

0x5*_*94E 1733 java string random

以下print语句将打印"hello world".有人能解释一下吗?

System.out.println(randomString(-229985452) + " " + randomString(-147909649));
Run Code Online (Sandbox Code Playgroud)

而且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();
}
Run Code Online (Sandbox Code Playgroud)

Eng*_*uad 1126

其他答案解释了原因,但这里是如何.

给出一个例子Random:

Random r = new Random(-229985452)
Run Code Online (Sandbox Code Playgroud)

r.nextInt(27)生成的前6个数字是:

8
5
12
12
15
0
Run Code Online (Sandbox Code Playgroud)

并且r.nextInt(27)生成给定的前6个数字Random r = new Random(-147909649)是:

23
15
18
12
4
0
Run Code Online (Sandbox Code Playgroud)

然后只需将这些数字添加到字符的整数表示`(即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
Run Code Online (Sandbox Code Playgroud)

  • 迂腐地,`new Random(-229985452).nextInt(27)`总是返回8. (47认同)
  • @rootTraveller首先,`new Random()`根本不返回数字. (3认同)
  • @roottraveller "Random" 是一个确定性的伪随机数生成器。如果你用一个固定的种子初始化它,它会产生一个固定的数字序列。 (2认同)
  • 有没有办法计算这些种子?必须有一些逻辑……或者仅仅是蛮力。 (2认同)
  • @SohitGore 鉴于 Java 的默认“Random”在加密上并不安全(我很确定它是 Mersenne Twister,但不要引用我的话),可能可以从“我想要这些数字”向后工作到“这是我要使用的种子”。我已经用标准 C 线性同余生成器做了类似的事情。 (2认同)

FTh*_*son 904

java.util.Random使用特定种子参数(在这种情况下-229985452-147909649)构造实例时,它遵循以该种子值开始的随机数生成算法.

每个Random使用相同种子构建的每个都将每次生成相同的数字模式.

  • 随机数序列的每个元素以27为模,每个"hello\0"和"world\0"中有6个元素.如果你假设一个真正随机的发生器,获得你正在寻找的序列的概率是27 ^ 6(387,420,489)中的1 - 所以它相当令人印象深刻,但不是很令人兴奋! (80认同)
  • @RussellBorogove:但是有了这些可能性,以及2 ^ 64种可能的种子,预计有476亿个种子值可以提供该序列.这只是找到一个问题. (16认同)
  • @Vulcan - javadoc说种子是48位.http://docs.oracle.com/javase/7/docs/api/java/util/Random.html.此外,实际种子是32位值. (8认同)
  • @ dan04 - 我不太愿意做出那个估计; 取决于PRNG的实现,种子字的大小可能不等于状态的大小,并且序列路径可能不是均匀分布的.但仍然,赔率肯定是好的,如果你找不到一对,你可以尝试不同的套管("你好""世界"`),或使用`122-k`代替`96+ k`,或...... (8认同)
  • @ThorbjørnRavnAndersen[Javadoc](http://docs.oracle.com/javase/7/docs/api/java/util/Random.html)指定"为Random类指定特定算法.Java实现必须使用全部为了Java代码的绝对可移植性,这里显示的Random类算法." (7认同)
  • @OneTwoThree随机数生成器在需要时可以预测几乎总是非常重要,因为否则很难测试,因为两次运行相同的代码会产生不同的结果.在PRNG中使用种子意味着您可以在测试时使用特定种子(或者至少报告种子以便可以再现结果),然后您可以使用将改变的种子 - 例如,新的Random()使用取自的种子当前时间. (5认同)

Den*_*kiy 276

我会把它留在这里.谁有很多(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();
}
Run Code Online (Sandbox Code Playgroud)

输出:

-9223372036808280701
Using base: 'Z'
stack
-9223372036853943469
Using base: 'b'
over
-9223372036852834412
Using base: 'e'
flow
-9223372036838149518
Using base: 'd'
rulez
Took 7087 ms
Run Code Online (Sandbox Code Playgroud)

  • @Vulcan大多数种子非常接近最大值,就像你选择1到1000之间的随机数一样,你最终选择的大多数种子将有三位数.当你想到它时,这并不奇怪:) (30认同)
  • @OneTwoThree`nextInt(27)`表示在[[0,26]`范围内. (24认同)
  • @Vulcan事实上,如果你进行数学运算,你会发现它们的最大值接近于零(我想种子在生成代码中被解释为无符号).但是因为数字的数量只与实际值成对数增长,所以当数字真的没有时,数字看起来非常接近. (18认同)
  • @Marek:我不认为伪随机的神会赞成这种行为. (13认同)
  • 很好的答案.对于奖励积分,你能找到一个种子,它会初始化一个随机数,这将生成最终randoms初始化所需的4个种子序列吗? (9认同)
  • 一个有趣且相关的规则是[Benford's Law](http://en.wikipedia.org/wiki/Benford%27s_law),指出在许多自然数据源中,前导数字"1"和"2"出现得更频繁,出于类似的原因:从9到10比10到20要小得多.在这种情况下,从'0`变为`int.max/10`所需要的东西远远少于`int .max/10`到`int.max`. (8认同)
  • @Vulcan实际上它与"Long.MIN_VALUE""接近".如果你从零开始,你会得到这些:`46495107` /`832339` /`1941396` /`16626290` (4认同)
  • 我注意到生成的种子都非常接近于"Long.MAX_VALUE".有趣的是找出每个字符串可以产生多少种不同的种子,但我无法以合理,及时的方式实现这一目标. (3认同)
  • 也许有点晚了,但我做了多核 - 使用多线程.我在这里发布了它:http://pastebin.com/xZQtGRdq由于多核问题,我改变了一些代码. (3认同)
  • 为什么它必须是`.nextInt(27)`?数字'27'有什么特别之处?我想如果你刚刚打电话给'nextInt()`,你会有更大的机会找到种子吗?另外,为什么你只检查一个序列的第一个`x`数字(其中`x` ==`goal.length()`)? (2认同)
  • @sulai:拥有更大范围的可能值 (2认同)

xDD*_*xDD 253

这里的每个人都很好地解释了代码是如何工作的,并展示了如何构建自己的示例,但这里有一个信息理论答案,说明为什么我们可以合理地期望蛮力搜索最终会找到解决方案.

26个不同的小写字母构成了我们的字母表?.为了生成不同长度的单词,我们进一步添加终止符号?以产生扩展字母表?' := ? ? {?}.

我们?是一个符号和X均匀分布的随机变量了?'.获得该符号P(X = ?)及其信息内容的概率I(?)由下式给出:

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

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

对于一个词? ? ?*及其?-终止的对应词?' := ? · ? ? (?')*,我们有

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

由于伪随机数发生器(PRNG)是用32位种子初始化的,我们可以期待大多数单词的长度达到

λ= floor [32/log 2(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;
    }
}
Run Code Online (Sandbox Code Playgroud)

请参阅输出:http://ideone.com/JRGb3l


Ilm*_*nen 66

我写了一个快速程序来找到这些种子:

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);
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我现在在后台运行它,但它已经找到了足够的经典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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

(关于ideone的演示.)

PS.-727295876, -128911, -1611659, -235516779.


Pur*_*u-- 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) + " ");
}
Run Code Online (Sandbox Code Playgroud)

打印

the quick browny fox jumps over a lazy dog 
Run Code Online (Sandbox Code Playgroud)

  • 你做了我的一天男人:DI尝试了Long.Min/Max并搜索我的同事的名字,只找到彼得:(彼得4611686018451441623彼得24053719彼得-4611686018403334185彼得-9223372036830722089彼得-4611686017906248127彼得521139777彼得4611686018948527681彼得-9223372036333636031彼得 - 4611686017645756173 peter 781631731 peter 4611686019209019635 peter -9223372036073144077 peter -4611686017420317288 peter 1007070616 peter -9223372035847705192) (6认同)

小智 25

事实上,大多数随机数生成器都是"伪随机数".它们是线性同余生成器或LCG(http://en.wikipedia.org/wiki/Linear_congruential_generator)

鉴于固定种子,LCG是可预测的.基本上,使用为您提供第一个字母的种子,然后编写一个继续生成下一个int(char)的应用程序,直到您点击目标字符串中的下一个字母并记下您必须调用LCG的次数.继续,直到你生成每一个字母.

  • 什么是非伪随机数发生器的一个例子 (3认同)

Two*_*The 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();
    }
  }
}
Run Code Online (Sandbox Code Playgroud)


Arn*_*jar 21

随机总是返回相同的序列.它用于改组数组和其他操作作为排列.

要获得不同的序列,必须在某个位置初始化序列,称为"种子".

randomSting获得"随机"序列的i位置(seed = -229985452)中的随机数.然后在种子位置之后的序列中使用ASCII代码作为接下来的27个字符,直到此值等于0.这将返回"hello".对"世界"进行相同的操作.

我认为代码不适用于任何其他单词.编程的人非常了解随机序列.

这是非常棒的极客代码!

  • @ dan04真正的程序员不仅仅使用PRNG,他们还记得整个时期,并根据需要枚举值. (24认同)
  • 我怀疑他是否"非常了解随机序列".更可能的是,他只是尝试了数十亿种可能的种子,直到找到一种有效的种子. (10认同)

tom*_*101 14

主体是使用相同种子构造的随机类,每次都会生成相同的数字模式.


sul*_*lai 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 :/");
}
Run Code Online (Sandbox Code Playgroud)


dee*_*392 10

从Java文档中,在为Random类指定种子值时,这是一个有意的功能.

如果使用相同的种子创建两个Random实例,并且对每个实例都进行相同的方法调用序列,则它们将生成并返回相同的数字序列.为了保证此属性,为Random类指定了特定的算法.为了Java代码的绝对可移植性,Java实现必须使用此处显示的所有算法用于Random类.

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

但奇怪的是,你会认为有可预测的"随机"数字存在隐含的安全问题.

  • @ deed02392当然,有一些用于指定种子值的实际用例.如果您正在模拟数据以使用某种蒙特卡罗方法来解决问题,那么能够重现结果是一件好事.设置初始种子是最简单的方法. (4认同)
  • 这就是为什么"随机数"的默认构造函数将随机数生成器的种子设置为一个非常可能与此构造函数的任何其他调用不同的值"([javadoc](http://docs.oracle.com/ JavaSE的/ 6 /文档/ API/JAVA/util的/ Random.html#随机%28%29)).在当前实现中,这是当前时间和计数器的组合. (3认同)

小智 8

这是关于"种子".相同的种子给出相同的结果.


Ily*_*man 5

这是 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 :/");
}
Run Code Online (Sandbox Code Playgroud)