Mỗi nhận xét sửa đổi câu hỏi ban đầu: OP phỏng đoán rằng 100 chữ số $y_n$ Trong $\{0,1,2,3,4,5\}$ sở hữu của họ có được bằng cách sử dụng biểu thức C(++) tương đương với rand()%6
với rand()
sử dụng PRNG (không mã hóa) dựa trên Trình tạo đồng dư tuyến tính, với mã tương đương với
tĩnh không dấu dài x;
int rand(void) {
x = 214013*x+2531011;
return (int)((x>>16)&0x7FFF);
}
Thông báo rằng $x$ ít nhất là 32 bit, nhưng chỉ có 31 bit bậc thấp mới quan trọng do (x>>16)&0x7FFF
và C biểu diễn không dấu dài
phép nhân với việc cắt bớt các bit bậc cao không phù hợp với biến.
Trừu tượng hóa các chi tiết lập trình, phỏng đoán là $x$ phát triển mỗi $x_{n+1}:=a\cdot x_n+b\bmod m$ với $m=2^{31}$ và $(a,b)$ đối với một số hằng số cố định được cho là một trong hai $(214013,2531011)$ hoặc $(22695477,1)$. Và rand()
đầu ra bit 30â¦16 của $x$ do đó $y_n:=\lfloor x_n/2^{16}\rfloor\bmod 6$ được trao cho $n=0$ đến $99$ (với hạt giống là một số nguyên chưa biết $x_{-1}$ trong một số phạm vi phi vật chất, vì chỉ $x_{-1}\bmod m$ sẽ quan trọng; và tốt hơn hết là chúng ta nên cố gắng tìm $x_0$ dù sao).
OP hỏi liệu có thể xác nhận hoặc xác nhận phỏng đoán đó không và (nếu đúng) tìm $(a,b)$ được sử dụng và dự đoán những gì tiếp theo trong trình tự.
Đúng, điều đó là có thể, với sự tự tin tuyệt vời. Trạng thái hiệu quả của các PRNG được xem xét có 31 bit, chỉ có hai PRNG được xem xét, chúng có thể sử dụng được cho mục đích mô phỏng, do đó chúng ta có thể tìm thấy trạng thái của chúng và trạng thái nào được sử dụng nhiều hơn một chút $31+1=32$ chút thông tin; và chúng tôi nhận được $100\cdot\log_2(6)\approx258,5$ bit, sẽ cung cấp nhiều xác nhận.
Đơn giản nhất là thử cho cả hai phỏng đoán $(a,b)$, và khám phá các giá trị có thể có của $x_0$. Chúng là duy nhất $2^{31}$, biết $y_0$ cho phép giảm một cách có hệ thống theo hệ số $6$. Mỗi người theo dõi $y_i$ tiếp tục loại bỏ $5$ ứng cử viên ra khỏi $6$. Nếu không có ứng cử viên sống sót qua tất cả các $y_i$, giả thuyết bị bác bỏ. Nếu một trận đấu được tìm thấy, chúng tôi biết đó $(a,b)$ chúng tôi đã sử dụng và có thể tính toán bổ sung $y_i$. Việc triển khai thành thạo bằng ngôn ngữ được biên dịch sẽ mất như một giây trên PC để bàn hiện đại.
Nhưng có lẽ muốn phá vỡ mọi thứ trong thời gian thực với CPU \$0,5 hiện đại chạy từ pin \$0,2 hoặc bị kẹt vào một máy tính lập trình của những năm 1970, hoặc là ENIAC. nhận xét rằng $6$ là số chẵn và số chia là $2^{16}$. Nó theo sau $y_n\bmod 2$ là một chút $16$ của $x_n$. Cũng nhận xét rằng kể từ khi $m$ là một sức mạnh của hai, sự thay đổi của một chút trong $x_n$ không lan truyền đến các bit bậc thấp hơn của $x_{n+1}$. Nếu theo bit 16 của $x_n$ chỉ phụ thuộc vào 17 bit thấp của $x_0$. Chúng tôi biết bit 16 của $x_0$, do đó cần kiểm tra nhiều nhất $2^{16}$ ứng cử viên cho các bit 15â¦0 của $x_0$. Sau đó chúng ta có thể tìm thấy 14 bit khác như trên. Điều đó phân chia và chinh phục cách tiếp cận sẽ cho phép giải quyết các tham số lớn hơn nhiều, ví dụ: Biến đổi x
loại uin64_t
và trả về x>>33
, trên một CPU hiện đại.
Tôi tự hỏi chúng ta có thể làm gì nếu $a$, $b$ và/hoặc $m$ không rõ.
Ghi chú ở trên:
- Nó sử dụng quy ước thống trị trong khoa học máy tính (và mật mã với một vài ngoại lệ như DES): bit được tính từ 0 (bit bậc thấp), do đó nếu một số nguyên không âm $v$ được biểu diễn ở dạng nhị phân như $k$ chút ít $b_j$, sau đó $v=\sum b_j$ với $0\le j<k$. Trong quy ước big-endian, bit 0 được viết ở bên phải: $6\times7=42$ trong số thập phân là $110\times111=101010$ ở dạng nhị phân.
- biến máy tính
x
ít nhất là 32 bit, nhưng chỉ có vấn đề 31 bit thứ tự thấp (0 đến 30) và được sử dụng trong $x_n$, do đó $0\le x_n<m=2^{31}$. đầu ra của rand()
ít nhất là 16 bit, nhưng chỉ có 15 bit thứ tự thấp (0 đến 14) và tất cả các bit khác đều bằng 0, do đó $0\le$rand()
$\le$RAND_MAX
$=2^{15}-1$. Nếu $0\le i<15$ sau đó cắn $j$ đầu ra của rand()
phù hợp với bit $j+16$ của x
. Nó đi sau bit 0 của $y_n$ phù hợp với bit 16 của $x_n$.
Nhận xét (ngoài chủ đề) về mã hiện tại:
- Nó cố gắng sử dụng khả năng tăng tốc được thực hiện bởi
6
bằng nhau. Tôi duy trì đây là không phải cần thiết để đạt được thời gian thực hiện tính bằng giây, nếu
- chúng tôi khám phá các giá trị có thể có của $x_0$, chứ không phải là hạt giống nhiều bước trước đó.
- chúng tôi sử dụng nó $x_0$ là 31 bit, do đó chúng tôi có thể hạn chế tìm kiếm bên ngoài thành [
0
, 0x7ffffffff
] đó là $2^{31}$ ứng viên $x_0$.
- chúng tôi sử dụng mà chúng tôi biết $y_0$, do đó mà $x_0=2^{16}\cdot(6\cdot u+ y_0)+v$ vì $0\le u<\lceil2^{15}/6\rceil$ và $0\le v<2^{16}$, giới hạn trong khoảng $2^{31}/6$ ứng cử viên cho $x_0$.
- chúng tôi tối ưu hóa bài kiểm tra để kiểm tra ứng viên $x_0$ chống lại $y_1$ trong vòng lặp bên trong trên $v$.
- Bản chất của không bắt buộc ghi chú tăng tốc
6
chẵn là trước tiên tìm các bit 16â¦0 của $x_0$ phù hợp với các giá trị $y_0$ được thu thập, sau đó là các bit 30–¦17. Mã không làm điều đó! Với tốc độ tăng tốc đó, thời gian thực hiện sẽ giảm xuống còn một phần nghìn giây.
- Chúng ta cần các giá trị đầy đủ của $y_n$ được thu thập (trong cả tìm kiếm không được tối ưu hóa và phần thứ hai của tìm kiếm được tối ưu hóa), nhưng chúng dường như không có sẵn trong đầu vào, mà tôi đoán là $y_n\bmod2$. Hơn nữa, tôi không hiểu tại sao đó là mảng hai chiều
KẾT QUẢ[3][7]
.
- Khi nào $x_0$ được tìm thấy, nếu cần tìm hạt giống (hay đúng hơn là bit 30–¦0 của số đó) một số bước đã biết trước đó, điều đó có thể được thực hiện hiệu quả bằng cách quay lại LCG bằng cách sử dụng $x_{n-1}:=a^{-1}\,(x_n-b)\bmod m$ ở đâu $a^{-1}$ là nghịch đảo mô-đun của $a$ modulo $m$.
Đây là mã làm việc không có tăng tốc tùy chọn (do đó có khả năng hoạt động với mô đun giảm cuối cùng lẻ $r$ câu hỏi có ở đâu 6
). Hãy thử nó trực tuyến!
#include <stdint.h>
#include <stdio.h>
#include <inttypes.h>
#define A 214013
#define B 2531011
#define R 6
#define M ((uint32_t)(((R-1)&R)!=0 ? 0x8000 : R)<<16)
const int16_t y[] = {
0,5,3,0,4,3,1,0,4,5,4,4,4,5,5,3,0,2,0,5,4,5,0,
};
#define INVA (IN_A(IN_A(IN_A(IN_A((uint32_t)A))))&(M-1))
#define IN_A(v) (v*(2-v*A))
int main(void) {
const uint32_t y0 = y[0], y1 = y[1];
int32_t x0 = (int32_t)(((M >> 16) - y0) / R * R + y0) << 16 | 0xffff;
uint32_t tìm thấy = 0;
printf("trình tạo: ((int)((x = %" PRIu32 " * x + %" PRIu32 ") >> 16) & 0x7fff) %% %u\n",
(uint32_t)A, (uint32_t)B, (không dấu)R);
trong khi (x0 >= 0) {
uint32_t x1 = A * (uint32_t)x0 + B;
làm {
if (((x1 >> 16) & 0x7fff) % R == y1) {
uint32_t x = x1;
số nguyên;
for (n = 2; n < sizeof(y) / sizeof(*y); ++n)
if ((((x = A * x + B) >> 16) & 0x7fff) % R != y[n])
goto nxt;
x = (INVA * ((uint32_t)x0 - B)) & (M - 1);
printf("x0 co the la %" PRId32 ", that la seed=%" PRIu32
" modulo 0x%" PRIx32 ", mang lại:\n", x0, x, M);
cho (n = 0; n < sizeof(y) / sizeof(*y) + 8; ++n)
printf(" %d", ((int)((x = A * x + B) >> 16) & 0x7fff) % R);
printf("\n");
++tìm thấy;
}
nxt: x1 -= (uint32_t)A;
} while (((x0--) & 0xffff) != 0);
x0 -= (int32_t)(R - 1) << 16;
}
printf("đã tìm thấy %" PRIu32 " giải pháp%s\n", đã tìm thấy, đã tìm thấy > 1 ? "s" : "");
trả về 0;
}