Случайный вопрос.
Я пытаюсь создать программу, которая будет генерировать псевдослучайное распределение. Я пытаюсь найти правильный псевдослучайный алгоритм для моих нужд. Это мои проблемы:
1) Мне нужен один вход, чтобы генерировать один и тот же вывод каждый раз, когда он используется.
2) Он должен быть достаточно случайным, чтобы человек, который просматривает выходной сигнал с входа 1, не видел никакой связи между ним и выходным сигналом с входа 2 (и т. Д.), Но не нужно, чтобы он был криптографически безопасным или действительно случайным.
3) Выходное значение должно быть числом от 0 до (29 ^ 3200) -1, причем каждое возможное целое число в этом диапазоне является возможным и равным (или близким к нему) вероятным выходным значением.
4) Я хотел бы иметь возможность гарантировать, что каждая возможная перестановка последовательностей из 410 выходов также является потенциальным выходом последовательных входов. Другими словами, все возможные группировки 410 целых чисел от 0 до (29 ^ 3200) -1 должны быть потенциальными выходами последовательных входов.
5) Я бы хотел, чтобы функция была обратимой, чтобы я мог взять целое число или серию целых чисел и сказать, какой вход или серия входов даст такой результат.
Метод, который я разработал до сих пор, состоит в том, чтобы выполнить ввод через простую последовательность halson:
boost::multiprecision::mpz_int denominator = 1;
boost::multiprecision::mpz_int numerator = 0;
while (input>0) {
denominator *=3;
numerator = numerator * 3 + (input%3);
input = input/3;
}
и умножьте результат на 29 ^ 3200. Он соответствует требованиям 1-3, но не 4. И он обратим только для отдельных целых чисел, а не для рядов (так как не все последовательности могут быть получены им). Я работаю в C ++, используя повышение точности.
Будем весьма благодарны за любые советы, которые мне могут дать относительно способа создания случайного распределения, отвечающего этим требованиям, или просто за класс алгоритмов, которые стоит изучить с этой целью. Заранее благодарю за рассмотрение моего вопроса.
—-ОБНОВИТЬ—-
Поскольку несколько комментаторов были сосредоточены на размере рассматриваемых чисел, я просто хотел прояснить, что я осознаю практические проблемы, возникающие при работе с такими наборами, но при задании этого вопроса меня интересует только теоретический или концептуальный подход к проблема — например, представьте себе работу с намного меньшим набором целых чисел, например от 0 до 99, и перестановками наборов из 10 выходных последовательностей. Как бы вы разработали алгоритм для удовлетворения этих пяти условий: 1) входные данные являются детерминированными, 2) кажутся случайными (по крайней мере, для человеческого глаза), 3) каждое целое число в диапазоне является возможным выходным значением, 4) не только все значения, но также все перестановки последовательностей значений являются возможными выходами, 5) функция является обратимой.
—второе обновление —
Большое спасибо @Severin Pappadeux. Мне удалось перевернуть lcg. Я подумал, что добавлю немного о том, что я сделал, чтобы, надеюсь, облегчить это любому, кто увидит это в будущем. Прежде всего, это отличные источники по инвертированию модульных функций:
https://www.khanacademy.org/computing/computer-science/cryptography/modarithmetic/a/modular-inverses
https://www.khanacademy.org/computer-programming/discrete-reciprocal-mod-m/6253215254052864
Если вы возьмете уравнение next = ax + c% m, используя следующий код со своими значениями для a и m, выведите евклидовы уравнения, которые вам нужно найти, а также значение ainverse:
int qarray[12];
qarray[0]=0;
qarray[1]=1;
int i =2;
int reset = m;
while (m % a >0) {
int remainder=m%a;
int quotient=m/a;
std::cout << m << " = " << quotient << "*" << a << " + " << remainder << "\n";
qarray[i] =qarray[i-2]-(qarray[i-1]*quotient);
m=a;
a=remainder;
i++;
}
if (qarray[i-1]<0) {qarray[i-1]+=reset;}
std::cout << qarray[i-1] << "\n";
Другое дело, что мне потребовалось некоторое время, чтобы понять, что, если вы получите отрицательный результат, вы должны добавить к нему m. Вы должны добавить аналогичный термин в ваше новое уравнение:
prev = (ainverse(next-c))%m;
if (prev<0) {prev+=m;}
Я надеюсь, что это поможет любому, кто решится пойти по этому пути в будущем.
Хорошо, я не уверен, есть ли общий ответ, поэтому я бы сконцентрировался на генераторе случайных чисел, имеющем, скажем, 64-битное внутреннее состояние / начальное число, производящем 64-битный выход и имеющем период 2 ^ 64-1. В частности, я бы посмотрел на линейный конгруэнтный генератор (он же LCG) в виде
next = (a * prev + c) mod m
где a
а также m
простые числа друг к другу
Так:
1) Проверить
2) Проверить
3) Проверьте (ну, для 64-битного пространства конечно)
4) Проверка (снова, кроме 0, я полагаю, но каждая перестановка 64 битов является выводом LCG, начиная с некоторого начального числа)
5) Проверьте. LCG, как известно, является обратимым, то есть можно получить
prev = (next - c) * a_inv mod m
где a_inv может быть вычислен из a
, m
используя алгоритм Евклида
Что ж, если для вас это нормально, вы можете попытаться внедрить LCG в свое пространство 15546 бит
ОБНОВИТЬ
И быстрый поиск показывает обратимое обсуждение / код LCG здесь
В вашем обновлении слово «кажется случайным (человеческому глазу)» — это фраза, которую вы используете. Определение «кажется случайным» не является хорошо согласованной темой. Существуют различные степени тестов на «случайность».
Однако, если вы просто хотите, чтобы это выглядело случайным для человеческого глаза, вы можете просто использовать кольцевое умножение.
Это сделает свое дело. Теперь самое сложное — доказать, что то, что ты сделал, стоило твоего времени. Учтите, что период, состоящий всего из 29 ^ 3200 длинных последовательностей, находится за пределами физической реальности. Вы никогда не будете использовать все это. Когда-либо. Учтите, что сверхпроводник, изготовленный из джозефиновых переходов (10 ^ 12 кг, обрабатывающий 10 ^ 11 бит / с), при массе всей вселенной 3 * 10 ^ 52 кг) может обрабатывать примерно 10 ^ 75 бит / с. Число, которое может сосчитать до 29 ^ 3200, имеет длину приблизительно 15545 битов, поэтому суперкомпьютер может обрабатывать приблизительно 6,5×10 ^ 71 чисел / с. Это означает, что для того, чтобы просто сосчитать эту высоту, потребуется примерно 10 4600 секунд или где-то около 10 459 лет. Ожидается, что где-то через 10 ^ 12 лет звезды будут постоянно гаснуть, так что это может занять некоторое время.
Есть M**N
последовательности N
числа между 0
а также M-1
,
Вы можете представить, что все они записаны один за другим в (псевдослучайной) последовательности и случайно поместили указатель чтения в результирующий цикл N*(M**N)
числа между 0
а также M-1
…
def output(input):
total_length = N*(M**N)
index = input % total_length
permutation_index = shuffle(index / N, M**N)
element = input % N
return (permutation_index / (N**element)) % M
Конечно, для каждой перестановки из N элементов между 0 и M-1 существует последовательность из N последовательных входов, которые ее генерируют (просто отмените перемешивание индекса перестановки). Я также сказал бы (просто используя рассуждения симметрии), что при любом начальном входе выход следующих N элементов одинаково вероятен (каждое число и каждая последовательность из N чисел одинаково представлены в общем периоде).