Почему этот код с использованием случайных строк печатает "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();
}
14 ответов
когда экземпляр java.util.Random
строится с определенным параметром seed (в этом случае -229985452
или -147909649
), он следует алгоритму генерации случайных чисел начало С этим значением семян.
Random
построенный с тем же семенем будет генерировать тот же шаблон чисел каждый раз.другие ответы объяснить, почему, но вот как.
данный экземпляр 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
Я просто оставлю это здесь. У кого есть много (CPU) времени, не стесняйтесь экспериментировать :) кроме того, если вы освоили некоторые fork-join-fu, чтобы заставить эту вещь сжечь все ядра процессора (просто потоки скучны, не так ли?), пожалуйста, поделитесь своим кодом. Я был бы очень признателен.
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) = log₂(27)
слово ω ∈ Σ*
и ⊥-
прекращено коллегой ω' := ω · ⊥ ∈ (Σ')*
, мы
I (ω): = I(ω') = |ω'| * log₂(27) = (|ω| + 1) * log₂(27)
поскольку генератор псевдослучайных чисел (PRNG) инициализируется 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);
}
}
}
Ps. -727295876, -128911, -1611659, -235516779
.
Я был заинтригован этим, я запустил этот генератор случайных слов в списке словаря. Диапазон: Целое Число.MIN_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
большинство генераторов случайных чисел на самом деле являются "псевдослучайными"."Они являются линейными Конгруэнтными генераторами, или LCGs (http://en.wikipedia.org/wiki/Linear_congruential_generator)
LCGs вполне предсказуемы, учитывая фиксированное семя. В принципе, используйте семя, которое дает вам первую букву, а затем напишите приложение, которое продолжает генерировать следующий int (char), пока вы не нажмете следующую букву в целевой строке и не запишите, сколько раз вам пришлось вызывать LCG. Продолжайте, пока не сгенерируете каждую букву.
Random всегда возвращает одну и ту же последовательность. Он используется для перетасовки массивов и других операций в качестве перестановок.
чтобы получить разные последовательности, необходимо инициализировать последовательность в некоторой позиции, называемой "seed".
randomSting получает случайное число в позиции i (seed = -229985452) "случайной" последовательности. Затем использует ASCII код для следующих 27 символов в последовательности после позиции затравки, пока это значение не будет равно 0. Это возвращение "привет". То же самое происходит и с "миром".
Я думаю, что код не работал ни для каких других слов. Парень, который запрограммировал это, очень хорошо знает случайную последовательность.
Это очень большой geek код!
поскольку многопоточность очень проста С 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();
}
}
}
полученные от Денис Тульскийответ, этот метод генерирует семя.
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 :/");
}
Принципал-это случайный класс, построенный с одним и тем же семенем, будет генерировать один и тот же шаблон чисел каждый раз.
из документов Java это преднамеренная функция при указании начального значения для случайного класса.
Если два экземпляра Random создаются с одним и тем же семенем и же последовательность вызовов методов для каждого, они будут генерировать и возврат идентичных последовательностей чисел. Чтобы гарантировать это свойства, определенные алгоритмы определяются для класса Random. Реализации Java должны использовать все алгоритмы, показанные здесь для класс Случайный, ради абсолютной переносимости Java-кода.
http://docs.oracle.com/javase/1.4.2/docs/api/java/util/Random.html
странно, хотя, вы могли бы подумать, что есть неявные проблемы безопасности при наличии предсказуемых "случайных" чисел.
вот небольшое улучшение для Дениса Тульского ответ. Это сокращает время наполовину
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 :/");
}